简体   繁体   中英

JQuery function still running after attribute change

I have a menu on my page. On click of each menu item it calls the one of following functions:

$('.sideNav1').click(function(){
    $('.sideNav1').attr('class','selected1');
    $('.selected2').attr('class','sideNav2');
    $('.selected3').attr('class','sideNav3');
    alert("1 selected");
    return false;
});

$('.sideNav2').click(function(){
    $('.sideNav2').attr('class','selected2');
    $('.selected1').attr('class','sideNav1');
    $('.selected3').attr('class','sideNav3');
    alert("2 selected");
    return false;
});

$('.sideNav3').click(function(){
    $('.sideNav3').attr('class','selected3');
    $('.selected2').attr('class','sideNav2');
    $('.selected1').attr('class','sideNav1');
    alert("3 selected");
    return false;
});

HTML:

<div class="sideNav1">
Nav 1
</div>
<div class="sideNav2">
Nav 2
</div>
<div class="sideNav3">
Nav 3
</div>

Following the change of attribute (which, according to dev tools in chrome, does happen) I would expect the selected div to do nothing on click. Instead, it behaves as if there was no attribute change and the click() event listener is still called for .sideNav... running the function again.

Is there something I might be missing here?

jQuery static event handlers (the type you are using), bind the event handlers ONCE when you first run the code. Any changes to class names after that are immaterial and do not affect the event handlers in any way.

For events which propagate, you can use delegated event handling which does evaluate the selector match on each event. In delegated event handling, you bind the event to a static parent object (one whose DOM element exists at the time you install the event handler and who is not recreated at any time) and the you use the delegated form of .on() .

In your case, you would have to pick a selector of a static parent and use something like this:

$(some parent selector).on("click", ".sideNav1", function() {...});

Though it's desirable (for performance reasons in large pages) to use a closer parent than the <body> element, you can even do this:

$(document.body).on("click", ".sideNav1", function() {...});

Then, the event handler will be called only when the target object has the .sideNav1 class name and not when it doesn't.

The way delegated event handling works is thusly:

  1. Some events like the "click" event propagate up the parent chain. After they are called on the target object, they are then called on any parent objects up the chain all the way up to the document object or until someone called .stopPropagation() on it.

  2. So, if you install an event handler for a particular event on a parent object, you can see all the events that occur on the children (assuming nobody calls .stopPropagation() on them).

  3. Using the delegated form of .on() jQuery will check each event that gets called ont the parent to see the original target of the event matches the second selector passed and it checks that at runtime at the time of the event. Because this check is realtime at the time of event distribution, it will be affected by any dynamic changes you make to the DOM element (as in your case).

You can read more about the use of delegated event handling in these references:

jQuery .live() vs .on() method for adding a click event after loading dynamic html

jQuery selector doesn't update after dynamically adding new elements

jQuery .on does not work but .live does

Does jQuery.on() work for elements that are added after the event handler is created?

JQuery Event Handlers - What's the "Best" method

The more common case in these answers is for DOM elements that are added or recreated after you've installed the event handler, but the issue is pretty much that same as DOM elements where you modify the classes on them and expect the event handlers to change their behavior.


FYI, I'd also generally suggest you use .addClass() and .removeClass() instead of .attr() as that allows other parts of your program to use other classes on those objects (for styling or other reasons).

If you add a sideNav class to all your divs, you could do something like this:

$('.sideNav').click(function(){
    $('.sideNav').removeClass("selected");
    $(this).addClass("selected");

});

Jsfiddle example: http://jsfiddle.net/Tec3D/

OK, i change from live to on...

   $(document).on('click','.sideNav1',function(){
    $(this).unbind('click').attr('class','selected1');
    $('.selected2').attr('class','sideNav2').bind('click');
    $('.selected3').attr('class','sideNav3').bind('click');
    alert("1 selected");
    return false;
});

$(document).on('click','.sideNav2',function(){
     $(this).unbind('click').attr('class','selected2');
    $('.selected1').attr('class','sideNav1').bind('click');
    $('.selected3').attr('class','sideNav3').bind('click');
    alert("2 selected");
    return false;
});

$(document).on('click','.sideNav3',function(){     
    $(this).unbind('click').attr('class','selected3');
    $('.selected2').attr('class','sideNav2').bind('click');
    $('.selected1').attr('class','sideNav1').bind('click');
    alert("3 selected");
    return false;
});

http://jsfiddle.net/BHL84/14/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM