开发者

jQuery - Issues with combining selectors in a single event handler

开发者 https://www.devze.com 2023-01-27 09:49 出处:网络
This is regarding Patrick DW\'s comment on my answer to this question. Multiple selectors: identify the triggering one?

This is regarding Patrick DW's comment on my answer to this question.

Multiple selectors: identify the triggering one?

My Answer:

Using $(this) will get you the element that was clicked.. and using is() can help you determine what was clicked.

$('a.button, span.xyz, a.another').click(function(e) {
   if ($(this).is("a.button")) {
     alert("a.button was clicked");
   } else if ($(this).is("span.xyz")) {
     alert("span.xyz was clicked");
   } else if($(this).is("a.another")) {
     alert("a.another was clicked);
   }
});

Patrick's Comments

Trouble with this is that it is effectively combining the inefficiency that .delegate() suffers (testing against a selector) with the inefficiency of assigning multiple redundant handlers. So you g开发者_StackOverflow中文版et the worst of both worlds.

@John - Yes, it would make more sense to break them up into separate handlers, and place any common functionality they share into a named function that can be called from within the handlers. This eliminates the need to run the .is() tests at every event occurrence, which can be costly. It can sometimes be beneficial to test for the element that was clicked. This is what .delegate() does. You suffer the expense of the test, but gain from the reduced number of handlers being assigned


Could someone explain (in detail) the inefficiencies that Patrick is talking about? Why is .is() costly in this scenario? Can you give an example as well using .delegate()


Using jQuery's .delegate() method suffers a performance hit because every time the event occurs inside the element with the delegate handler, a similar test needs to take place to determine which element received the event.

This is often worthwhile because by using .delegate() you are (presumably) avoiding the need to assign individual handlers to every element.

With the code above, you have the overhead of assigning multiple handlers, and you are still running the code that needs to test to see which received the event.

Hard to say if .delegate() is the appropriate choice for the question you referenced, but it would be much better to assign individual handlers, and avoid the if( $(this).is('something') ) tests.

If the purpose of the original OP's code was to have individual functionality for each element type, but still have them share some subset of common functionality, it would be better to roll that common functionality into its own named function and call it. This is a much more efficient approach.


EDIT:

Take this example. Let's say the span.target and p.anothertaget elements are to get handlers. As you can see, there are a number of elements in the container.

Using selectors to assign a .click(), you end up with several separate event handlers.

$('span.target').click(function() { /* do something with span */ });
$('p.anothertarget').click(function() { /* do something with p */ });

html

<div id='container'>
    <span class='target'>click me</span>             <!-- i get a handler -->
    <div>i don't do anything</div>
    <p class='anothertarget'>i get a click too</p>   <!-- i get a handler -->
    <span class='target'>click me</span>             <!-- i get a handler -->
    <div>i don't do anything</div>
    <p class='anothertarget'>i get a click too</p>   <!-- i get a handler -->
    <span class='target'>click me</span>             <!-- i get a handler -->
    <div>i don't do anything</div>
    <p class='anothertarget'>i get a click too</p>   <!-- i get a handler -->
    <span class='target'>click me</span>             <!-- i get a handler -->
</div>

This means that jQuery needs to manage individual handers for all of these elements. This is the downside. The upside is that you don't need to run a selector to see what received the event. It will always be represented by this (assuming you used separate functions for the p and the span.


Using .delegate(), you assign a handler only to the container. This is done for each type of element that should trigger an event.

As you can see, there are only two handlers assigned. One for the <p> element, and the other for the <span>. This obviously reduces the quantity of handlers needed. But the expense is that jQuery needs to run the selectors that you passed to .delegate() for each event that occurs inside container to see which type of element actually received the event.

So there's some give and take.

$('#container').delegate('span.target','click',function() { /* do something with span */ });
               .delegate('p.anothertarget','click',function() { /* do something with p */ });

html

<div id='container'>   <!-- i get a handler for the span.target -->
                       <!--             and for p.anothertarget -->
    <span class='target'>click me</span>             
    <div>i don't do anything</div>
    <p class='anothertarget'>i get a click too</p> 
    <span class='target'>click me</span>   
    <div>i don't do anything</div>
    <p class='anothertarget'>i get a click too</p> 
    <span class='target'>click me</span> 
    <div>i don't do anything</div>
    <p class='anothertarget'>i get a click too</p> 
    <span class='target'>click me</span>
</div>

The trouble with the code in your previous answer was that it was effectively combining the inefficiency of the first example (many handlers being assigned) with the inefficiency of the second example (running a selector). That's what I meant by the worst of both worlds.


Why is it less efficient? In this case mainly because you're casting this to a jQuery object an extra 3 times per click per element, where as:

$("#container").delegate('a.button', 'click', function() {
  alert("a.button was clicked");
}).delegate('span.xyz', 'click', function() {
  alert("span.xyz was clicked");
}).delegate('a.another', 'click', function() {
  alert("a.another was clicked");
});

Would bind once to the container (3 handlers) and run each of the 3 selectors once per click, rather than also having n handlers (number of elements) across all bound elements. So the binding time is lower and the number of overall handlers (assuming there's 3+ elements) is far lower in a big page, as this will always attach exactly 3.

Also you're not incurring the cost of the initial $('a.button, span.xyz, a.another') selector to find the elements you need at the onset.


Well it is inefficient in the way that using a multiple selector would indicate that the function is generic enough to be used for all selected elements.

It would in my point of view be better to select each selector individualy and attach a click event to it. It then would also look more natural to me.


Per my comment above... (this can be simpler etc.)

<!-- using unique IDs or attributes to solve the issue -->
<p><span id="elementA">elementA</span></p>
<div id="elementB">elementB</div>
<script type="text/javascript">
$(document).ready(function(){
     $('div, p span').click(function(){
        var id = $(this).attr('id');
        alert('You clicked ' + $('#' + id).text());
     });
});
</script>

OR let the tag type handle things for you if that's appropriate for your case

<p><span>elementA</span></p>
<div>elementB</div>


<script type="text/javascript">
 $(document).ready(function() {
    $('div, p span').click(function() {
        var tagName = $(this)[0].tagName;
        alert(tagName); 
    });
});
</script>
0

精彩评论

暂无评论...
验证码 换一张
取 消