5

Can somebody tell how to "unbind" an anonymous function? In jQuery it's capable to do that, but how can I implement this Functionality in my own script.

This is the scenario:

The following code attach a onclick event to the Div which have someDivId as ID, now when you click the DIV, it's showing 'clicked!'.

var a  = document.getElementById('someDivId');
bindEvent(a,'click',function(){alert('clicked!');});

That's all great, the problem is how to "un-attach" the Function to the DIV if the function is anonymous or how to "un-attach" all attached events to the 'a' Element?

unBind(a,'click'); //Not necessarily the given params, it's just an example.

This is the code for bindEvent Method:

function bindEvent (el,evtType,fn){
    if ( el.attachEvent ) {
        el['e'+evtType+fn] = fn;
        el[evtType+fn] = function(){
            fn.call(el,window.event);
        }
        el.attachEvent( 'on'+evtType, el[evtType+fn] );
    } else {
        el.addEventListener( evtType, fn, false );
    }
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
WSD
  • 3,243
  • 26
  • 38
  • 1
    Where did you get `bindEvent()`? What does it do? – jfriend00 Apr 20 '12 at 22:37
  • Are you asking for a non-jQuery-based answer (e.g. plain javascript)? – jfriend00 Apr 20 '12 at 22:38
  • @jfriend00: he wants to implement it on his own - eg. hes not using jQuery, but asking how jQuery does it. – prodigitalson Apr 20 '12 at 22:38
  • @jfriend00, am trying to implement the method unBind from Jquery or any other that do the same in my own script. – WSD Apr 20 '12 at 22:40
  • @prodigitalson: exactly, that's what i need, could you help me please ?? – WSD Apr 20 '12 at 22:41
  • @josh: i think `Element.detachEvent` in IE and `Element.removeEventListener` in others natively support this, so all you should need to do is just test which API youre dealing with and invoke it... [See JREAM's answer below](http://stackoverflow.com/a/10254452/215966) – prodigitalson Apr 20 '12 at 22:43
  • @JoshGuzman - you've posted a version of `bindEvent()` that only takes two arguments, but you're clearly passing it three arguments. Did you miss the `fn` argument? – jfriend00 Apr 20 '12 at 23:08
  • Is this `el['e'+evtType+fn]` an attempt to create a unique property in order to keep a reference to the function? –  Apr 20 '12 at 23:10
  • 1
    possible duplicate of [Removing an anonymous event listener](http://stackoverflow.com/questions/3106605/removing-an-anonymous-event-listener) – jfriend00 Apr 20 '12 at 23:16
  • @jfriend00, upss, sorry for that... fixed! – WSD Apr 20 '12 at 23:59
  • @CheranShunmugavel, it's done, thanks for the warning. – WSD Apr 21 '12 at 13:51

5 Answers5

4

Finally, and after hours of Test&Errors i have found a solution, maybe it's not the best or most efficient but... IT WORKS! (Tested on IE9, Firefox 12, Chrome 18)

First all I'v create two cross-browser and auxiliary addEvent() and removeEvent() methods. (Idea taken from Jquery's source code!)

HELPERS.removeEvent = document.removeEventListener ?
function( type, handle,el ) {
    if ( el.removeEventListener ) {
    //W3C Standard    
    el.removeEventListener( type, handle, true );
    }
} : 
function( type, handle,el ) {
    if ( el.detachEvent ) {
        //The IE way
        el.detachEvent( 'on'+type, el[type+handle] );
        el[type+handle] = null;
    }
};

HELPERS.addEvent = document.addEventListener ?
function( type, handle,el ) {
    if ( el.addEventListener ) {
        //W3C Standard
        el.addEventListener( type, handle, true );
    }
} : 
function( type, handle,el ) {
    if ( el.attachEvent ) {
        //The IE way
        el['e'+type+handle] = handle;
        el[type+handle] = function(){
            handle.call(el,window.event);
        };
        el.attachEvent( 'on'+type, el[type+handle] );

    }
}

Also we need some kind of 'container' to store the attached events to elements, like this:

HELPERS.EVTS = {};

And finally the two callable and exposed to the users Methods: The next one to add an Event(event) and associate this Event to a Method (handler) for a specific Element (el).

    function bindEvent(event, handler,el) {

            if(!(el in HELPERS.EVT)) {
                // HELPERS.EVT stores references to nodes
                HELPERS.EVT[el] = {};
            }

            if(!(event in HELPERS.EVT[el])) {
                // each entry contains another entry for each event type
                HELPERS.EVT[el][event] = [];
            }
            // capture reference
            HELPERS.EVT[el][event].push([handler, true]);
            //Finally call the aux. Method
            HELPERS.addEvent(event,handler,el);

         return;

    }

Lastly the method that un-attach every pre-attached events (event) for an specific Element (el)

    function removeAllEvent(event,el) {

            if(el in HELPERS.EVT) {
                var handlers = HELPERS.EVT[el];
                if(event in handlers) {
                    var eventHandlers = handlers[event];
                    for(var i = eventHandlers.length; i--;) {
                        var handler = eventHandlers[i];
                        HELPERS.removeEvent(event,handler[0],el);

                    }
                }
            }   

    return;

    }

By the way, to call this methods you must do the following: Capture a DOM Node

    var a = document.getElementById('some_id');

Call the method 'bindEvent()' with the corresponding parameters.

    bindEvent('click',function(){alert('say hi');},a);

And to de-attach it:

    removeAllEvent('click',a);

That's all, hope will be useful for somebody one day.

WSD
  • 3,243
  • 26
  • 38
  • 1
    I'm not sure that the `HELPERS.EVT[el]` line, where `el` is a reference to a DOM element, is doing what you think it's doing. JavaScript property names [must be strings](https://developer.mozilla.org/en/JavaScript/Reference/Operators/Member_Operators#Property_names), and I'm not certain there is a standard `toString` method for DOM elements. In Firefox 11 and IE9, I get `"[object HTMLDivElement]"` when calling `toString` on a `div` element. – Cheran Shunmugavel Apr 21 '12 at 19:52
  • @CheranShunmugavel: Is exactly right. Using this system, two different `div` elements will appear as the same property in the `HELPERS.EVT` table. –  Apr 22 '12 at 00:29
2

Personally (and I know this isn't the "best" way, as it does require me to think about what I'm doing), I like to just use the on* event properties of the element I'm working with.

This has the convenient upside of being able to quickly and easily detach events.

var a = document.getElementById('someDivId');
a.onclick = function() {alert("Clicked!");};
// later...
a.onclick = null;

However, you do have to be careful with this because if you try to add a second event handler it will overwrite the first. Keep that in mind and you should be all fine.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • a.onclick = null doesn't work, that's because the function bindEvent() functions in a different way, please, check the original question: I'm going to edit it an post the code of the bindEvent() – WSD Apr 20 '12 at 22:45
  • thanks to an idea from jlaceda i found a solution to solve the problem. – WSD Apr 21 '12 at 03:29
1

I'm not sure if you can unbind an anonymous function attached via javascript. If possible you can simple remove the element from the DOM and recreate it. This will get rid of any event handlers previously attached.

RestingRobot
  • 2,938
  • 1
  • 23
  • 36
  • but JQuery can do It! so is not impossible! – WSD Apr 20 '12 at 23:13
  • 1
    @JoshGuzman: jQuery binds exactly *one* handler per element, and it's never the handler you passed it. Your handlers are stored in `jQuery.cache` It binds a generic handler that, when invoked, looks at the event object, sees what type of event it is, looks up a serial number on the element, looks to see if that serial number exists in `jQuery.cache`, and if so, checks to see if any handlers were given for the current event type, and invokes it/them if so. –  Apr 20 '12 at 23:28
  • ...the generic handler that jQuery bound is also referenced in `jQuery.cache`, so when it comes time to unbind it, it does a similar lookup, and passes it to the native methods that unbind handlers. Of course, when you do `.unbind('click')`, it's just clearing the `click` handlers in `jQuery.cache`. –  Apr 20 '12 at 23:30
  • @amnotiam, excellent observation! any approach to implement that mechanism ?? – WSD Apr 20 '12 at 23:48
  • @JoshGuzman: The best approach I could recommend would be avoidance. It requires that you create a system that will manage that `cache` data, and ties you to it. Because the only connection from the element to the data in the `cache` is the serial number on the element, any time an element is removed from the DOM, you must look for a `cache` entry, and if one exists, clean up the data. If you venture outside the management system to remove an element from the DOM, you have a memory leak. –  Apr 21 '12 at 00:01
  • ...for example, in jQuery, if you do `some_element.html("")`, it needs to traverse every element nested inside `some_element`, look for an entry in the `cache`, and clean up when one is found. This is unfortunate. –  Apr 21 '12 at 00:02
1

JavaScript provides no list of event listeners attached to a node.

You can remove all event listeners of a node but using the Node.cloneNode method, see here: https://developer.mozilla.org/En/DOM/Node.cloneNode

This clones the node (obviously) but it does not clone the event listeners attached to it.

You could also just bind empty functions as event listeners:

function noop() {}
bindEvent(myElement, "click", noop);
Griffin
  • 13,184
  • 4
  • 29
  • 43
  • But the function that i bind to an element is anonymous, which s means that i have no name reference, so i can't override them or know them. – WSD Apr 20 '12 at 23:55
  • Ah yeah, I forgot you can have multiple event listeners for the same event. You can't get a reference to your anonymous function. If you can change your code so that it is not anonymous, that would be the best solution. Otherwise you'll just have to kill all event listeners and add the ones you want, back on. – Griffin Apr 21 '12 at 00:02
-1

This is from jquery's source:

jQuery.removeEvent = document.removeEventListener ?
    function( elem, type, handle ) {
        if ( elem.removeEventListener ) {
            elem.removeEventListener( type, handle, false );
        }
    } :
    function( elem, type, handle ) {
        if ( elem.detachEvent ) {
            elem.detachEvent( "on" + type, handle );
        }
    };
jlaceda
  • 866
  • 6
  • 11
  • 2
    This is just a small part of jQuery's event system. How will it answer the question? –  Apr 20 '12 at 23:05
  • @amnotiam I figured this is the "most bare metal" part of the event removal procedure in jquery and will be useful reference in creating a custom unBind function. But you are right it that it is not a complete answer. I will edit in a standalone function that will be based on this snippet. – jlaceda Apr 20 '12 at 23:34
  • @jlaceda, i'll be waiting that snipet like gold-for-free, thanks a lot! – WSD Apr 21 '12 at 00:16
  • @jlaceda thanks to you for the Idea, i found a solution, thk! – WSD Apr 21 '12 at 03:27
  • @JoshGuzman np. Thanks for going all the way with solving this. I wasn't really getting the helpers part when I was going through the jquery source. – jlaceda Apr 21 '12 at 06:01
  • @jlaceda, i made the "HELPERS" by my self, but the Idea to use the correct addEvent function depending of the Navigator (to be cross-browser) was taken from Jquery. – WSD Apr 21 '12 at 14:08