Browser Memory Usage Comparison: inline onClick vs. using JQuery .bind()
Asked Answered
R

3

6

I have ~400 elements on a page that have click events tied to them (4 different types of buttons with 100 instances of each, each type's click events performing the same function but with different parameters).

I need to minimize any impacts on performance that this may have. What kind of performance hit am I taking (memory etc) by binding click events to each of these individually (using JQuery's bind())? Would it be more efficient to have an inline onclick calling the function on each button instead?

Edit for clarification :):
I actually have a table (generated using JQGrid) and each row has data columns followed by 4 icon 'button' columns- delete & three other business functions that make AJAX calls back to the server:

|id|description|__more data_|_X__|_+__|____|____|
-------------------------------------------------
| 1|___data____|____data____|icon|icon|icon|icon|  
| 2|___data____|____data____|icon|icon|icon|icon|   
| 3|___data____|____data____|icon|icon|icon|icon|   
| 4|___data____|____data____|icon|icon|icon|icon|    

I am using JQGrid's custom formatter (http://www.trirand.com/jqgridwsiki/doku.php?id=wiki:custom_formatter) to build the icon 'buttons' in each row (I cannot retrieve button HTML from server).

It is here in my custom formatter function that I can easily just build the icon HTML and code in an inline onclick calling the appropriate functions with the appropriate parameters (data from other columns in that row). I use the data in the row columns as parameters for my functions.

    function removeFormatter(cellvalue, options, rowObject) {       
        return "<img src='img/favoritesAdd.gif' onclick='remove(\"" + options.rowId + "\")' title='Remove' style='cursor:pointer' />";
    }

So, I can think of two options:
1) inline onclick as I explained above
--or--
2) delegate() (as mentioned in below answers (thank you so much!))

  1. Build the icon image (each icon type has its own class name) using the custom formatter.
  2. Set the icon's data() to its parameters in the afterInsertRow JQGrid event.
  3. Apply the delegate() handler to buttons of specific classes (as @KenRedler said below)
>    $('#container').delegate('.your_buttons','click',function(e){  
>      e.preventDefault();  
>      var your_param = $(this).data('something'); // store your params in data, perhaps  
>      do_something_with( your_param );  
>    }); //(code snippet via @KenRedler)

I'm not sure how browser-intensive option #2 is I guess...but I do like keeping the Javascript away from my DOM elements :)

Reins answered 14/3, 2011 at 19:32 Comment(1)
Event delegation is the only reasonable solution here.Innis
T
9

Because you need not only a general solution with some container objects, but the solution for jqGrid I can suggest you one more way.

The problem is that jqGrid make already some onClick bindings. So you will not spend more resources if you just use existing in jqGrid event handler. Two event handler can be useful for you: onCellSelect and beforeSelectRow. To have mostly close behavior to what you currently have I suggest you to use beforeSelectRow event. It's advantage is that if the user will click on one from your custom buttons the row selection can stay unchanged. With the onCellSelect the row will be first selected and then the onCellSelect event handler called.

You can define the columns with buttons like following

{ name: 'add', width: 18, sortable: false, search: false,
  formatter:function(){
      return "<span class='ui-icon ui-icon-plus'></span>"
  }}

In the code above I do use custom formatter of jqGrid, but without any event binding. The code of

beforeSelectRow: function (rowid, e) {
    var iCol = $.jgrid.getCellIndex(e.target);
    if (iCol >= firstButtonColumnIndex) {
        alert("rowid="+rowid+"\nButton name: "+buttonNames[iCol]);
    }

    // prevent row selection if one click on the button
    return (iCol >= firstButtonColumnIndex)? false: true;
}

where firstButtonColumnIndex = 8 and buttonNames = {8:'Add',9:'Edit',10:'Remove',11:'Details'}. In your code you can replace the alert to the corresponding function call.

If you want select the row always on the button click you can simplify the code till the following

onCellSelect: function (rowid,iCol/*,cellcontent,e*/) {
    if (iCol >= firstButtonColumnIndex) {
        alert("rowid="+rowid+"\nButton name: "+buttonNames[iCol]);
    }
}

In the way you use one existing click event handler bound to the whole table (see the source code) and just say jqGrid which handle you want to use.

I recommend you additionally always use gridview:true which speed up the building of jqGrid, but which can not be used if you use afterInsertRow function which you considered to use as an option.

You can see the demo here.

UPDATED: One more option which you have is to use formatter:'actions' see the demo prepared for the answer. If you look at the code of the 'actions' formatter is work mostly like your current code if you look at it from the event binding side.

UPDATED 2: The updated version of the code you can see here.

Tove answered 14/3, 2011 at 23:37 Comment(3)
@icats: I also think so and wrote the suggestion after the writing the answer on your question.Tove
Thank you SO much!!! Your answer is incredibly concise and helpful. I appreciate the demos, so much!!! As you mentioned in your answer to the question referenced above, it looks like the 'actions' custom format type is not quite fully complete/well documented: "The formatter is just experimental and will be described in next releases". I look forward to the next JQGrid release! So, I am going to use the 'beforeSelectRow' event handler method that you outlined above. That is perfect and quite simple! I think I was thinking too much inside the box :).Reins
Also, your suggestion to the JQGrid crew is perfect. That would be very handy!Reins
K
2

You should use the .delegate() method to bind a single click handler for all elements ,through jQuery, to a parent element of all buttons.

For the different parameters you could use data- attributes to each element, and retrieve them with the .data() method.

Know answered 14/3, 2011 at 19:35 Comment(4)
+1 But the most intensive part of this would be generating and inserting all of these buttons into the DOM, and shouldn't he bind the click handler to the buttons before they are inserted into the DOM?Worley
@motionman, i assume the elements are created server side and not on-the-fly with javascript. In regard to binding the handler, no. The whole point of delegate is not having to bind to each element, but a parent of those that will get the event as well due to bubbling. So a single bind will handle all clicks (even if they are added after the binding since the binding happens on another element).Know
@motionman - I clarified my question above, thank you so much for your input! I do create the elements on-the-fly w/ Javascript.Reins
@Reins you should look at @oleg answer.Know
H
2

Have you considered using delegate()? You'd have one handler on a container element rather than hundreds. Something like this:

$('#container').delegate('.your_buttons','click',function(e){
  e.preventDefault();
  var your_param = $(this).data('something'); // store your params in data, perhaps
  do_something_with( your_param );
});

Assuming a general layout like this:

<div id="container">
  <!--- stuff here --->
  <a class="your_buttons" href="#" data-something="foo">Alpha</a>
  <a class="your_buttons" href="#" data-something="bar">Beta</a>
  <a class="your_buttons" href="#" data-something="baz">Gamma</a>
  <a class="something-else" href="#" data-something="baz">Omega</a>
  <!--- hundreds more --->
</div>
Hastie answered 14/3, 2011 at 19:37 Comment(1)
Thank you so much @KenRedler! delegate() and data()- I feel rather silly for not knowing about these. I added clarification to my question above- delegete() looks like it will work for me if I set things up correctly, but I'm not quite sure on the most efficient way to implement what I need to do.Reins

© 2022 - 2024 — McMap. All rights reserved.