Plugin: Ensure Single Event Handler

As an update to "Ensuring single instance of event handlers", I wrote a plug-in to handle this as well.

The point is, you could use off and on but what if there are several handlers? You will risk overwriting other events on the item.

For example:

var element = $("#open-btn");

// Adding event to open a UI element
element.click(open);

// Later, adding event to disable button until window closed
element.off("click”).click(disable);

// Now, the window won’t open

This could cause issues when several events are attached to the same object. So instead, we use this:

var element = $("#open-btn");
element.attach("click", open);
element.attach("click", disable);

The "attach" method is my custom plugin. It will search for the event handler and only add it if not already attached. It works like this:

/**
 * Attaches an event handler only if not already attached.
 * @param {any} type The event type, such as 'click' or 'keyup'.
 * @param {any} handler The function handler.
 */
$.fn.attach = function (type, handler) {
    // Only attach if not already on element
    if (!$(this).isAttached(type, handler)) {
        $(this).on(type, handler);
    }
};

The point is to interrogate each event of all properties, which is done by iteration as:

this.each(function () {
    var events = $._data($(this)[0], "events");
    for (var prop in events) {
        if (Object.prototype.hasOwnProperty.call(events, prop)) {
            var event = events[prop][0];

We now have event which has info on what type of event it is and what handler is on it. We can interrogate one or both. The handler will be textually the same but can change during browser attachments, so I compare them as string literals, as:

if (event.type === type && typeof (handler) === "undefined") {
    // The event type, such as 'keyup' is attached but we are not
    // comparing the function. The event is attached.
    found = true;
}

if (event.type === type && typeof (handler) !== "undefined") {

    // Compare the function
    var comparer = event.handler.toString().replace(/\s/g, "") === handler.toString().replace(/\s/g, "");

    // We compare on function level as well
    // and they are the same. The event is attached.
    if (comparer) {
        found = true;
    }
}

The whole plugin can be downloaded from a Gist on Github.

Hope this helps!