4

There is a question with a good answer about binding multiple objects to a click event here, but the answer deals with only buttons inside a container. I am wondering what happens if there is a more complex structure, with elements inside the buttons and non-button elements inside the container. Will it still be more efficient to use a single eventhandler?

Consider this (not that complex) HTML:

<div id="container">
    <div class="some nice red block"></div>
        <div id="button_1" class="button">
           <span>some text</span>
           <img src="button_1_img.jpg">
        </div>
    <p>some more text</p>
    <img src="a_nice_pic.png">
        <div id="button_2" class="button">
           <span>some text</span>
           <img src="button_2_img.jpg">
        </div>
        <div id="button_3" class="button">
           <span>some text</span>
           <img src="button_3_img.jpg">
           <div class="some icon"></div>
        </div>
    <p>some more text</p>
    <img src="a_nice_pic.png">

    //... more text, images and let's say up to 20 buttons

</div>

I could:

  1. Add eventlisteners to all buttons seperatly
  2. Add only one eventhandler to the container

In case 1 I need:

document.getElementById('button_1').addEventListener('click',dosomething,false);
document.getElementById('button_2').addEventListener('click',dosomething,false);
//etc... 18x

function dosomething(e){
   var button=e.currentTarget.id;
   //do something based on button id;
   }

I then have 20 clickhandlers attached to 20 different buttons that only fire if the button is clicked.

In case 2 I need:

document.getElementById('container').addEventListener('click',dosomething,false);

function dosomething(e){
    var container=e.currentTarget;
    var clicked=e.target;

    while(clicked != container){
        if(clicked.className == 'button'){
            var button=clicked.id;
            //do something based on button id;
           return; 
           }
        else{
           clicked=clicked.parentNode;
           }
     }
}

Now I have only one eventlistener. But it fires not only on the buttons, but on everything you click inside the container. And it has to travel up the DOM everytime to detect if there might be a button around the element clicked on.

So which one of the two is the most efficient in memory usage and performance? The one with more eventhandlers but less to do, or the one with only one eventhandler but more complex code?

I've made a fiddle to demonstrate the difference.

[edit] Perhaps I've oversimplified the example too much. There are moments some 200+ evenlisteners will be attached, in a maximum of 4 containers.

Community
  • 1
  • 1
Michel
  • 4,076
  • 4
  • 34
  • 52

2 Answers2

1

There is a question with a good answer about binding multiple objects to a click event here, but the answer deals with only buttons inside a container.

No. Only its markup example does not contain other elements, but it does work for your case as well.

$(common parent selector).on('click', selector of target objects, function() {});
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^

Which would be $("#container").on("click", ".button", …) for your markup.

So which one of the two is the most efficient in memory usage and performance? The one with more eventhandlers but less to do, or the one with only one eventhandler but more complex code?

Neither. As you just noticed, it's a tradeoff: a single event handler will take less memory; and multiple event handlers have less to do when being clicked.

I don't think there is any problem with your 20 buttons. The memory need will only affect performance if you'd use hundreds of handlers. I'd recommend to use the simpler solution - especially if you don't use a library that does the complex things for you - and only try event delegation once you suffer from performance degradation. Don't prematurely optimise.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks for your answer. Maybe I shouldn't have oversimplified the example, as the real HTML structure is far more complex than that, and has a lot more buttons. The solution you're quoting is jQuery, which I'm not using. But I feel somehow (and correct me if I'm wrong) jQuery has to do the same: either search the DOM and bind events to all found buttons, or bind one event to the container and travel the DOM from event.target upwards. It's IMHO not prematurely optimising, it's choosing the best of two and stick to it, rather then having to change a lot of code lateron. – Michel Mar 30 '15 at 10:50
  • Yes, jQuery has to do the same. But no, you shouldn't need to stick to one model; use a proper helper function and it'll be trivial to switch without having to change lots of code (in jQuery, it would be `$(container, targets, handler)` vs `$(container + targets, handler)` - a minimal difference). – Bergi Mar 30 '15 at 10:53
0

As said in my comment, I'd do the following way.

var buttons = document.querySelectorAll('.button');
[].forEach.call(buttons, function(button) {
  button.addEventListener("click", dosomething);
});

function dosomething(e) {
    var button=e.currentTarget.id;
    alert (button);
}

This would only attach event listeners to buttons and not the container like you've shown in your question. Bit out of time atm, but will come back and add more details :) In the meantime, everyone: feel free to edit and add your valuable comments!

lshettyl
  • 8,166
  • 4
  • 25
  • 31
  • Thanks for your answer. Maybe I should have been more clear. My question isn't on _how_ to add the eventhandlers, but on _how many_ should I add: one for the container or for each button one? – Michel Mar 30 '15 at 10:56
  • Sure. [Bergi](http://stackoverflow.com/users/1048572/bergi) has your questions covered in terms of performance/optimization. – lshettyl Mar 30 '15 at 11:02