Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most efficient way to dynamically bind event handlers

Problem: I need to bind any number of event handlers to any number of elements (DOM nodes, window, document) at dynamically runtime and I need to be able to update event binding for dynamically created (or destroyed) nodes during the lifetime of my page. There are three options that I can see for tackling this problem:

I) Event delegation on window
II) Direct event binding on each node
III) Event delegation on common ancestors (which would be unknown until runtime and would potentially need to be recalculated when the DOM is altered)

What is the most efficient way of doing this?

A little context

I am working on a set of pages that need analytics tracking for user events (clicks, scrolling, etc.) and I want to be able to easily configure these event handlers across a bunch of pages without needing to write a script to handle the event binding for each instance. Moreover, because I may have the need to track new events in the future, or to track events on elements that are dynamically added to/removed from the page, I need to be able to account for changes in the DOM that occur during the lifetime of the page.

As an example of what I'm currently considering, I would like to create a function that accepts a config object that allows the programmer to specify default handlers for each event, and allow them to override them for specific elements:

Analytics.init({
    // default handlers for each event type
    defaultHandlers: {
        "click": function(e) { ... },
        "focus": function(e) { ... }
    },

    // elements to listen to 
    targetElements: {

        // it should work with non-DOM nodes like 'window' and 'document'
        window: {
            // events for which the default handlers should be called
            useDefaultHandlers: ['click'],

            // custom handler
            "scroll": function(e) { ... }
        },

        // it should work with CSS selectors
        "#someId": {
            useDefaultHandlers: ['click', 'focus'],
            "blur": function(e) { ... }
        }
    }
});

Sources

  • SO: Should all jQuery events be bound to document?
  • SO: How to find the nearest common ancestors of two or more nodes
  • jQuery docs: $.fn.on()
like image 562
wvandaal Avatar asked Aug 28 '14 15:08

wvandaal


People also ask

Which of the following is the best way to bind functions?

Bind with the Arrow Function This approach is probably the best way of doing bindings. It's simple, easy to read, and most importantly, it works.

Which method is used for attaching event handlers to current and future elements?

jQuery on() Method Note: Event handlers attached using the on() method will work for both current and FUTURE elements (like a new element created by a script).

Which method is used to attach an event handler?

The most flexible way to set an event handler on an element is to use the EventTarget. addEventListener method.

How do you add an event handler to a dynamic button?

This is a lot simpler than you think, in our function that will create our new element, we need to attach the event handler, and function we want to assign to it, this can be done like so: // Create the new element var li = document. createElement('li'); li. className = 'dynamic-link'; // Class name li.


1 Answers

I usually delegate events on the document.documentElement object because:

  1. It represents the <html> element on the page, which holds everything which holds all the HTML tags the user can interact with.
  2. It is available for use the moment JavaScript starts executing, negating the need for a window load or DOM ready event handler
  3. You can still capture "scroll" events

As for the efficiency of event delegation, the more nodes the event must bubble up the longer it takes, however we're talking ~1 to 2 ms of time difference -- maybe. It's imperceptible to the user. It's usually the processing of a DOM event that introduces a performance penalty, not the bubbling of the event from one node to another.

I've found the following things negatively affect JavaScript performance in general:

  1. The more nodes you have in the document tree, the more time consuming it is for the browser to manipulate it.
  2. The greater the number of event handlers on the page the more JavaScript slows down, though you would need 100s of handlers to really see a difference.

Mainly, #1 has the biggest impact. I think trying to eek out a performance boost in event handling is a premature optimization in most cases. The only case I see for optimizing event handling code is when you have an event that fires multiple times per second (e.g. "scroll" and "mousemove" events). The added benefit of event delegation is that you don't have to clean up event handlers on DOM nodes that will become detached from the document tree, allowing the browser to garbage collect that memory.

(From the comments below) wvandell said:

The performance costs of event delegation have little to do with the actual 'bubbling' of events ... there is a performance hit incurred when delegating many selectors to a single parent.

This is true, however let's think about the perceived performance. Delegating many click events won't be noticeable to the user. If you delegate an event like scroll or mousemove, which can fire upwards of 50 times per second (leaving 20 ms to process the event) then the user can perceive a performance issue. This comes back to my argument against premature optimizations of event handler code.

Many click events can be delegated with no problem on a common ancestor, such as document.documentElement. Would I delegate a "mousemove" event there? Maybe. It depends on what else is going on and if that delegated "mousemove" event feels responsive enough.

like image 197
Greg Burghardt Avatar answered Sep 22 '22 17:09

Greg Burghardt