Change element ID, but jQuery still fires event calling old ID. Why does this work?
Asked Answered
B

4

12

I created a fiddle to try to debug a problem I'm having where once I rearrange html elements with jQuery, hover events on those elements don't work anymore.

However, I came across this interesting situation here: http://jsfiddle.net/4yv1trj4/

I have a main div that changes color once I hover over it.

$("#block").hover(function() {
     $(this).css("backgroundColor", "red");
}, function() {
    $(this).css("backgroundColor", "#888");        
});

If you click the button, the main div's ID changes to block2:

$("#block").attr("id","block2");

but $("#block").hover() still fires when I hover over #block2. Also, all hover calls on #block2 do not work. Is there a fundamental principle of how jQuery works that would explain this?

Bloodred answered 24/6, 2015 at 16:27 Comment(0)
R
18

When you do this:

$("#block").hover(function() {
    $(this).css("backgroundColor", "red");
}, function() {
    $(this).css("backgroundColor", "#888");        
});

you're telling jQuery to look for the element with the block ID and bind the hover event to it. Once this is done, the event will remain bound to that element, no matter what happens to its ID afterwards.

That is, unless you have some code that unbinds it, of course.

Riehl answered 24/6, 2015 at 16:31 Comment(4)
Thanks, that helps me to understand it a little bit. jsfiddle.net/4yv1trj4/5 I updated the jsfiddle to unbind and re?-bind but code looks ugly as hell and I can't help feeling that there could be much better ways of doing this. Do you think technophobia's answer would be a better option to slim down my code?Bloodred
I like technophobia's workaround, although I wouldn't recommend it for your use case due to the issues stated by canon (#31032504). I think your solution was not that bad.Riehl
Clearly there is a trade of that you will have to contend with - I personally have used .on() container bindings in countless projects with minimal impact on performance.Talky
'trade of' was meant to be 'trade-off'Talky
T
9

As an extension to lucasnadalutti's answer: It's worth noting that you can add the binding to the container and yield the result you expect:

Example:

$("body").on("mouseenter", "#block", function () {
    $(this).css("backgroundColor", "red");
}).on('mouseleave', "#block", function () {
    $(this).css("backgroundColor", "#888");
});

Notice the binding on the body, not the actual element. A trick to keep in your back pocket.

Demo: jsFiddle


Update:

From the comment section, it's clear a warning is needed - you should bind to the nearest element on the page. body was used here as a simple example.

Although I think this solution would suit you elegantly, you should read Should all jquery events be bound to $(document)? before you start abusing that power.

Talky answered 24/6, 2015 at 16:45 Comment(5)
interesting, I had never knew this was an option. Is any part of doing this considered to be bad practice if I replace all of my click() and hover() events with this, considering i'd have to add a lot of binds and unbinds in my code if I were to go that route? Thanks!Bloodred
@Matt yes, there are issues. It essentially means that your events have to bubble all the way up through the DOM to body before they can be handled. If anything stops propagation along the way, you're in trouble. Generally, this pattern is reserved for dynamic content or to avoid attaching a large number of handlers, i.e.: one for each list-item in a list. Regardless, if you use event delegation, you'll always want to select the nearest reliable ancestor rather than simply using body.Auroraauroral
When testing this on a small page, these results will probably be fine; however, as the page grows, each event attached to your body tag (or whichever you attach it to using canon's recommendation) will take more and more resources. So ... for future expansion, you may want to use lucasnadalutti's recommendation to use the tags directly and unbind them as needed (unless of course, you know the relevant tag's content will remain relatively small)Rieth
@Matt I would bind to the nearest container - the performance is perfectly acceptable for most use cases. However, I'd still highly recommend you read the link I've posted at the bottom of the answer. Good luck.Talky
Thanks for adding that link. I think it helped me understand a little bit better. If you wouldn't mind indulging me, why does this example of delegation not work? jsfiddle.net/4yv1trj4/8 Passing a variable as a selector in this case would end up being the easiest way to take care of my current problem (rearranging a lot of HTML content around the page based on user input) but it seems like when that variable changes, the function won't execute for that selector. Thanks!Bloodred
G
2

If you remove the jQuery hover listener and add a CSS hover, it works the way you want:

#block:hover {
    background-color:red;
    width:300px;
    height:300px;
}
Goolsby answered 24/6, 2015 at 16:32 Comment(1)
This. Using jQuery for such a task is unnecessarySterol
B
2

$("#block") is looking for a specific DOM object. .hover() will bind a hover event to that DOM object. $("#block").attr("id","block2"); will change an attribute (id) of that DOM object, but the hover event is still bound to it.

Bluet answered 24/6, 2015 at 17:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.