jQuery Live traversal parent() selector - javascript

$(".hovertip").parent().live('hover', function() {
...
The above code doesn't seem to register.
This doesn't seem to work either:
$(".hovertip").parent().live({
mouseenter:
function() {
...
Any examples of .live, .delegate, .bind, or .on working with a jQuery selector and a .parent() selector with .hover() or mouseenter: and mouseleave:?
Update: I've created a separate question to address the dynamic DOM issue this Question has raised: jQuery .on() with a .parent() and dynamic selector

Try:
$(".hovertip").parent().on('hover', function() {
alert('yay');
});
Note: .on was introduced in jQuery 1.8.
Working demo http://jsfiddle.net/pRB8n/ Hover over test test - in the demo
If you really want to use .delegate try this please: http://jsfiddle.net/TURBX/2/ - http://api.jquery.com/delegate/
Delegate
Attach a handler to one or more events for all elements that match the
selector, now or in the future, based on a specific set of root
elements.
Hope rest fits the needs :)
P.S. - .live is deprecated: for further if you keen - my old post here: :) What's wrong with the jQuery live method?
under category you will see: http://api.jquery.com/live/ "deprecated"

I would add a comment to Tats_innit's post, but I can't.
As per the documentation on live,
Chaining methods is not supported. For example, $("a").find(".offsite, .external").live( ... ); is not valid and does not work as expected.
That's why .parent() does not work.

Binding to parent
Event delegation (handled by the deprecated live and .delegate, and now by .on/.one) only moves downwards. You can't have an upward event delegation like you seem to want to do here.
That is to say if the parent of ".hovertip" does not exist then clearly ".hovertip" does not exist so you are actually binding to nothing.
If your goal is to bind the event to the parent of ".hovertip" when it appears, then you're SOL since delegation only moves downwards (to descendants).
Your options to handle that would be:
* Bind to the parent of .hovertip when it is appended to the DOM.
* Know a selector for the parent of .hovertip ahead of time and bind to it immediately, perhaps through delegation.
Delegating to child
If your goal is to have the event fire when .hovertip is hovered, but .hovertip may not be in the DOM and its parent is not known, you must use a method like this:
$("known parent selector of .hovertip").on('hover', '.hovertip', function () {
"known parent selector of .hovertip" has to be an element that you know ahead of time. If you can't know, you have to use document, but I'd suggest to try to get as close as possible. You can only use this on elements that exist in the DOM at the time of binding.

I think what you are looking for, actually, is something along these lines:
$(document).on('mouseover', '.hovertip', function() {
// handle your mouseover changes, here
});
$(document).on('mouseout', '.hovertip', function() {
// handle your mouseout changes, here
});
.live, .bind, are all deprecated, AFAIK, which means they'll go away in the future, and you might not want to rely on their continued support.
It would also be far better to replace $(document) with a selector that's closer to your .hovertip elements, but above them in the DOM nesting, so they can respond to your event, but without forcing jQuery to watch for every event on every element in the whole document. I simply put document in there as an example, as I don't know what the rest of your structure looks like.
http://jsfiddle.net/mori57/qa7py/
As I think about it, I think it's worth pointing out that throwing things to .parent() may not always work out the way you expect, especially if you're modifying the DOM. I think it's far safer to set a higher-level event handler.
If you must use something like the .parent(), I always found more accurate results with .closest(), and giving it a selector also helps the parsing engine narrow its search. You don't want one parent triggering the hover state for /all/ the .hovertips at one time, which could happen in some cases.

Related

is $(document).on(event, '#id .class') faster than $(document).on(event, '.class')

So I know that if you query an element by ID, it's way faster than by only class.
Is this true in the case binding events to dynamically created elements.
Example:
$(document).on('click', '#id .class', someFunction);
vs
$(document).on('click', '.class', someFunction);
Assume I have really a lot of elements on my page.
When I click on the element binded by the function above, will the 1st method call someFunction faster than the 2nd method?
If you understand how delegated event handling works in jQuery, then your first version with '#id .class' is just making more work for the event handling system. As others have said, you would have to run some tests to see if the difference is even measurable, much less consequential.
To help you understand, here's how the delegated event handling for this works:
$(document).on('click', '#id .class', someFunction);
An event handler for the click event is registered on the document object. Then, anytime a click event bubbles up to the document object, the jQuery system is called and it has to check to see if the event target matches the '#id .class' selector. This is a bit of work. First it has to see if the object itself (or a parent) matches .class, then it has to search up the parent chain from where it finds a match to see if it finds #id.
Your second version doesn't have to search up the parent chain for the #id. So, if that isn't required for accurately targeting only the items you want, then you should just go with your second option:
$(document).on('click', '.class', someFunction);
simply because it makes less work for the code to do.

If .off automatically called when .remove is called?

jQuery:
var a = $("<a href='#'>Click me</a>")
var d = $("div:first");
a.appendTo(d);
a.on("click",function(){
if(prompt('enter password') === 'password'){
$(d).remove();
}
});
Fiddle: http://jsfiddle.net/bujRr/
Question: Although the <a> doesn't exist anymore, the click was still bound to it.
Was the event binding removed? when the <div> was .remove()ed?
If it wasn't, could this cause performance problems after a theoretical few hundred runs?
Should I just call .off() before .remove()?
Note: No, I am not really storing any passwords in JS. I removed ajax calls as well as other code for the sake of having a SSCCE.
Well according to the documentation events are removed as well: http://api.jquery.com/remove/
Similar to .empty(), the .remove() method takes elements out of the
DOM. Use .remove() when you want to remove the element itself, as well
as everything inside it. In addition to the elements themselves, all
bound events and jQuery data associated with the elements are removed.
To remove the elements without removing data and events, use .detach()
instead.
Was the event binding removed? when the <div> was .remove()ed?
Yes, events bound to a removed item (or its children) are automatically removed for you.
If it wasn't, could this cause performance problems after a theoretical few hundred runs?
It would, but for the reason above it's not an issue
Should I just call .off() before .remove()?
No.

Handle stuff after dom changes

I've got a page with some Javascript / jQuery stuff, for example:
(function()
{
$('.tip').tooltip();
$('.test').click(function()
{
alert('Clicked!')
});
}();
On the page I insert some HTML with jQuery so the DOM changes. For example, I insert a extra element with the class "tip" or "test". The just new inserted elements doesn't work because jQuery is working with the non-manipulated DOM and the just inserted elements aren't there. So I've searched around and came to this solution for the "click":
$('body').on('click','.click',function()
{
alert('Clicked!')
});
I don't understand why, but this way it's working with the manipulated DOM and the jQuery stuff works on the new inserted elements. So my first question is, why does this work and just the click() function not? And the second question, why do I have to point to the "body"?
Finally, my third question is, how get this done with the tooltip?
I know that there is so many information about this subject (previous the delegate() and live() function I've read) but I can't found a explanation about it. And I can't get my third question solved with the information I found.
I'm looking forward to your responses!
Extra question:
4) Is it recommended to point always to the "body" for this kind of situations? It's always there but for possible performance issues?
So my first question is, why does this work and just the click()
function not?
Because the event handler is now delegated to a parent element, so it remains even after replacing/manipulating child elements.
Ancient article on event delegation for your perusal - but the concepts remain the same:
http://www.sitepoint.com/javascript-event-delegation-is-easier-than-you-think/
And the second question, why do I have to point to the "body"
You don't, any suitable parent element will do. For example, any direct parent (a div wrapper, for instance) which does not get replaced.
Finally, my third question is, how get this done with the tooltip?
You need to re-initialize your tooltip plugin on the newly inserted elements. For example:
$.get("foo.html", function (html) {
$("#someDiv").html(html);
$("#someDiv").find(".tip").tooltip();
});
The click() event doesn't work when you manipulate the DOM because JQuery is not watching for DOM changes. When you bind the click() event it is selecting the elements that are on the page at that time. New ones are not in the list unless you explicitly bind the event.
Because you have pointed the click() event on the body. JQuery then checks to see if the target of the click matches any of the event handlers (like what you have created) match the element clicked. This way any new elements will get the event 'associated' with them.
Because the tooltip isn't an event that you can place on the body, you will need to re-initialize it when the element is created.
EDIT:
For your fourth question, is it depends. The advantage of binding to the body is that you don't accidentally bind an event to an element more than once. The disadvantage is that you are adding event handlers that need to be checked on each event and this can lead to performance issues.
As for your concerns about DRY, put the initialization of the tooltips into a function and call that when you add them. Trying to avoid having the same function call is a little overkill in this regard, IMO.
Events are bound to the specific object you are binding it to.
So something like $('.tip').tooltip() will perform the tooltip() functionality on $('.tip') which is actually just a collection of objects that satisfies the css selector .tip. The thing you should take note of is, that collection is not dynamic, it is basically a "database" query of the current page, and returns a resultset of HTML DOM objects wrapped by jQuery.
Therefore calling tooptip() on that collection will only perform the tooltip functionality on the objects within that collection, anything that was not in that collection when tooltip is called will not have the tooltip functionality. So adding an element that satisfies the .tip selector, after the tooltip() call, will not give it the tooltip functionality.
Now, $('body').on('click','.click', func) is actually binding the click event to the body tag (which should always exist :P), but what happens is it captures whether the click event has passed through an element your target css selector (.click in this case), so since the check is done dynamically, new elements will be captured.
This is a relatively short summary of what's going on... I hope it helped
UPDATE:
Best way for your tooltip thing is to bind tooltip after you have added elements, e.g.
$('#container').load('www.example.com/stuff', function() {
$('.tip', $(this)).tooltip();
});

jQuery adding event handlers to eval'ed elements

I've a table generated dynamically by jQuery, using
this.html("<table><tr><td><div>Click Me</div></td></tr></table>");
within the table, I've a few divs (my sample shows only one to keep things simple), which I want to add click event handler to. I'd like to keep html clean and use as much of jQuery power as I can, but since I'm doing an 'eval' type of things I can't quite figure out how to do that.
I know, that I can use $("div[some attribute selector]").on("click", {}, clickHandler);, but is it a good idea in my case?
You need delegated events. To do that, simply use jQuerys on() method like this:
$(document.body).on('click', 'div', function( event ) {
// do something
});
Ref.: .delegate(), .on()
What is that? Almost all events do what we call 'bubble'. That means, if you click on a nested element, your browser looks if there is any click-event handler ascociated on that node. If so, it executes them and then the parent of that element is also asked if there is any click-event handler. This continues until either some handler prevents the event from further bubbling or we have reached the document.documentElement (html).
So, you should change the above document.body into the closest node relative to your dynamically added elements.
You can use either use live or delegate to do that

Is binding events in jQuery very expensive, or very inexpensive?

I just wrote a $().bind('event') function and then got concerned that this kind of a call might be very expensive if jQuery has to run through each element in the DOM to bind this event.
Or maybe, it's just about as efficient as an event could be. The jQuery docs I've read aren't making this clear. Any opinions?
There are two things that can make your event binding code slow: the selector and the # of bindings. The most critical of the two is the # of bindings, but the selector could impact your initial performance.
As far as selectors go, just make sure you don't use pure class name selectors like .myclass. If you know that the class of myclass will always be in a <div> element, make your selector be div.myclass as it will help jQuery find the matching elements faster. Also, don't take advantange of jQuery letting you give it huge selector strings. Everything it can do with string selectors it can also do through functions, and this is intentional, as it is (marginally, admittedly) faster to do it this way as jQuery doesn't have to sit around to parse your string to figure out what you want. So instead of doing $('#myform input:eq(2)'); you might do $('input','#myform').eq(2);. By specifying a context, we are also not making jQuery look anywhere it doesn't have to, which is much faster. More on this here.
As far as the amount of bindings: if you have a relatively medium-sized amount of elements then you should be fine - anything up to 200, 300 potential element matches will perform fine in modern browsers. If you have more than this you might want to instead look into Event Delegation.
What is Event Delegation? Essentially, when you run code like this:
$('div.test').click(function() {
doSomething($(this));
});
jQuery is doing something like this behind the scenes (binding an event for each matched element):
$('div.test').each(function() {
this.addEventListener('click', function() {
doSomething(this);
}, false);
});
This can get inefficient if you have a large amount of elements. With event delegation, you can cut down the amount of bindings done down to one. But how? The event object has a target property that lets you know what element the event acted on. So you could then do something like this:
$(document).click(function(e) {
var $target = $(e.target);
if($target.is('div.test')) { // the element clicked on is a DIV
// with a class of test
doSomething($target);
}
});
Thankfully you don't actually have to code the above with jQuery. The live function, which is advertised as an easy way to bind events to elements that do not yet exist, is actually able to do this by using event delegation and checking at the time an action occurs if the target matches the selector you specify to it. This has the side effect, of course, of being very handy when speed is important.
The moral of the story? If you are concerned about the amount of bindings your script has just replace .bind with .live and make sure you have smart selectors.
Do note, however, that not all events are supported by .live. If you need something not supported by it, you can check out the livequery plugin, which is live on steroids.
Basically, you're not going to do any better.
All it is doing is calling attachEventListener() on each of your selected elements.
On parse time alone, this method is probably quicker than setting inlined event handlers on each element.
Generally, I would consider this to be a very inexpensive operation.

Categories