Don’t let jQuery win power over you and your workflow. Listen to my words: Event Handlers for everyone! Because jQuery doesn’t has any patent of “live bindings” or any other kind of event handling in General! And it’s really easy to play with events too, just forget (like) everything about the $-sign and DHTML attributes. I’ll show you how to do it right!

HowTo: Attach an Event?

The easiest way to attach an event is by just using the global method addEventListener(), which supports up to four three arguments. The first one is the name of the event, which you want to listen for. The second is your function, which should be called on the respective event. And last but not least you have a boolean value (supported by IE) or an Object with different settings (supported by cool browsers only).

The only problem you MAY will front is the Internet Explorer, because Microsoft hates existing standards. And this results in the attachEvent() function, which you need to use on <= 8. To make it easier, we just write us a small helper function like:

Show Code Snippet
function addEvent(element, event, callback, options){
    if(element.addEventListener){
        if(window.document.documentMode){
            options = ("capture" in options)? options.capture: !!options;
        }
        element.addEventListener(event, callback, options || false);
    } else if(element.attachEvent){
        if(event == "DOMContentLoaded"){
            event = "readystatechange";
        }
        element.attachEvent("on" + event, callback);
    }
    return callback;
}

Okay, seriously, this function looks may a bit complicated, but it just checks which Event method is available and if the “capture” option can be used and if it is present. It also adds the additional “on” string in front of the event name on the Internet Explorer version and changes “DOMContentLoaded” to “onreadystatechange”. So you just need to pass your arguments as on addEventListener.

Detach an Event!

Sometimes you also want to remove an event handler from an element, which is a bit more complex as you may think. Because you need the callback function you want to remove, to remove it. You can also replace the element itself with an cloned version of itself, to remove ALL event listeners at all.

Show Code Snippet
function removeEvent(element, event, callback){
    if(!event){
        if(!element.parentElement){
            return false;
        }
        element.parentElement.replaceChild(element.cloneNode(true), element);
        return true;
    }

    if(element.removeEventListener){
        element.removeEventListener(event, callback);
    } else if(element.detachEvent){
        if(event == "DOMContentLoaded"){
            event = "readystatechange";
        }
        element.detachEvent("on" + event, callback)
    }
    return true;
}

 

HowTo: Live Event Bindings?

Now it gets hard: We want to create a function like jQuery.live(), which binds event handlers to elements which currently doesn’t exist within the DOM. You have 2 possibilities to handle such a function. The lame one: Attach the event handler to all respective elements you wanna add to your structure (LAME). The cool one: Attach an event handler to the parent Element (or the global document) and wait until the event occurs in general, and check if it concerns the respective elements.

The following Code Snippet is sponsored (copy and pasted) by plainjs.com, a really good website for pure, vanilla JavaScript content!

Show Code Snippet
function liveEvent(selector, event, callback, context){
    return addEvent(context || document, event, function(ev){
        var found, el = ev.target || ev.srcElement;
        while(el && el.matches && el !== context && !(found = el.matches(selector))){
            el = el.parentElement
        }
        if(found){
            return callback.call(el, ev);
        }
    });
}

The function above wraps your callback function within a static-written method and calculates the target – where the event occurs – with the passed selector. To achieve this our function uses the matches method of the Element object, which is available since 34, 34, 21, 7, 9 and 12. If the selector matches the target element, it will break the loop and return it. If not, it tries this test again but with the parentElement. This continues until a match could be found OR no further parentElement is available.

A smarter function

You may think That’s really inefficient!, and yeah that’s right. Because in the really worst case it will loop itself through ALL parent elements until no one exists (so up to the documents root). plainJS leaves the idea to use the “new” elements object method .closest(), which would speed up the application but isn’t available in not-so-modern browsers: 41, 35, 28, 9 and 15.

Show Code Snippet
function liveEvent(selector, event, callback, context){
    return addEvent(context || document, event, function(ev){
        if((ev.target || ev.srcElement).closest(selector)){
            return callback.call(el, ev);
        }
    });
}

Of course, you can use the respective Polyfills to replace the missing functions. Modern browsers would be rewarded with more speed, older browsers with a new functionality. But how much speed, or which difference exists between .matches and .closest as well as on the respective Polyfill constructs?

I test it and you can try it out as well on jsPerf.com, a online JavaScript Performance Benchmarking app. The results depends on the used browser, but Elements .closest should be everywhere the best solution, followed by .matches and the .closest Polyfill, which uses the .matches function to work. So it’s no exciting result.

 

Howto: Trigger an Event?

We are able to add and remove events, and create live bindings. Now we should also learn to trigger these events on our own. This isn’t really easy, because there are 2 common ways + a special Internet Explorer possibility to just trigger Events. It’s a bit hard to create an universal helper function, which serves each of them. So the following function will just show the “general basics” using the modern way as first, the “normal” way as second and the IE-Fallback as third solution.

Show Code Snippet
function triggerEvent(element, type, options){
    if(Event && Event.name){
        var event = new Event(type, options);
        element.dispatchEvent(event);
    } else if("createEvent" in document){
        var event = document.createEvent("HTMLEvents");
        event.initEvent(type, !!options.bubbles, !!options.cancelable);
        element.dispatchEvent(event);
    } else if("createEventObject" in document){
        var event = document.createEventObject();
        event.eventType = type;
        element.fireEvent("on" + type, event);
    }
    return false;
}

The modern way: Each single main Event Interface offers now his completely own constructor. This constructor allows to set event-specific attributes and data, which gets passed to the callback function. If you don’t need to pass any data, you can ALWAYS use the main Event constructor as used on our helper function!

The fashion way: You can create “UIEvents”, “MouseEvents”, “MutationEvents” and “HTMLEvents” Interface Events using documents createEvent() method. The event type as well as some basic settings (bubbles and cancelable state) must be passed using the initEvent method on the returning event object!

The uncool way: To make the Internet Explorer (below 9) also happy, we need to call the really own createEventObject document method. You need to pass the event type as variable to (event).eventType. Don’t forget to fire this using fireEvent on the respective element!

 

Simple Enough?

I need to say: Yeah, event handling starting with live bindings up to trigger Events also on really old browsers are a bit tricky. But still simple enough to avoid fat libraries like jQuery. And I hope the helper methods above help you to dive into this matter. Of course, you can use the helper functions above without credits or anything else, extend them, share them, do whatever you want with it. Just have fun coding!

But please don’t get me wrong, this whole DOM and function libraries arn’t bad (ok rather the must of them) and specially jQuery was a really good library, back in 2010. Nowadays, where anyone who uses the Internet Explorer < 11 does it just out of self-hatred and where JavaScript envolves, you don't need jQuery at all. And this Just Vanilla blog post series here on this website should prove that to you!