jQuery; Check if object has child $(this) - javascript

I wonder how I in the best way can see if a container div contains a child element. I have a click event that fires on a child of either div id=unread or div id=read. I want to check where this child is.
Something like this is what I'm thinking:
if ($("#unread").find($(this)))
alert("unread");
else
alert("read");
Edit:
$(this) is a descendants two levels from #unread or #read.
Regards

Make use of : .children()
if( $("#unread").children().length > 0)
alert("unread");
else
alert("read");
EDIT
if($(event.target).closest('#unread').length > 0)
alert('unread');
else
alert('read');

I think just adding .length should work:
if ($("#unread").find($(this)).length > 0)
alert("unread");
else
alert("read");

Use .closest() or .parents() to search up the tree from the clicked element:
if ($(this).closest("#unread").length == 1)
// etc
Otherwise, are you interested in a non-jQuery answer? Given you've said the div with the click event is exactly two levels below the "read" or "unread" divs you could just do this:
if (this.parentNode.parentNode.id === "unread")
alert("unread");
else
alert("read");
// or just alert(this.parentNode.parentNode.id);

Go backwards from $(this) to look for #unread as an ancestor using closest:
if($(this).closest('#unread').length > 0)
alert('unread');
else
alert('read');
Depending on your HTML structure, this will be faster than searching all the children of #unread to find this. The speed difference probably won't matter that much though but you should be aware of the option of going backwards and the possible benefits of doing so.
Checking for the ancestor might be a better match for your intent as well: you have this in hand and what you really want to know is "is it inside #unread?". Using closest to go back up the DOM tree exactly matches the question you're asking.
If for some reason you're dead set on starting at #unread and looking at its descendants, then you can use find:
if($('#unread').find(this))
alert('unread');
else
alert('read');
But this approach will only work if you're using at least jQuery 1.6.

I wonder how I in the best way can see if a container div contains a child element.
Use $.contains(), especially if performance is a concern. From the docs:
jQuery.contains( container, contained )
Returns: Boolean
Description: Check to see if a DOM element is a descendant of another DOM element.
It's more efficient than .find(), .children(), or .closest(), as others have recommended. Be aware, however, that it only accepts DOM elements as parameters, not jQuery objects (which is part of why its performance is better).
In your case, you could do this in your click event handler:
if ($.contains($("#unread")[0], this)) {
alert("unread");
} else {
alert("read");
}
EDIT: Upon thinking about this again, it's probably more intuitive to search up from the clicked element, rather than down from the parent element, and performance wouldn't normally be a concern in a click event handler. That is, I would opt to use the .closest() method (as suggested by #nnnnnn and #mu is too short), so my click event handler would contain:
if ($(this).closest("#unread").length > 0) {
alert("unread");
} else {
alert("read");
}

Related

How do I remove non-visible elements from an entire document?

I want to use Javascript and JQuery to make a copy of the web page's entire html and remove all elements that are not visible to the user. This is what I've tried so far:
$('html').not(':visible').remove()
However, it does not seem to work. Does anyone know how I can accomplish this?
Right now you are only targeting the <html> element with your selector. You need to iterate through all the elements and check their visibility like this:
$('*').each(function(){
if($(this).not(':visible')){
$(this).remove();
}
});
Even more concise would be to just target hidden elements and remove them as such:
$('*:hidden').remove();
The jQuery API reference states that elements are considered visible if they take up space in the document. So elements that have a height and width greater than 0 are considered visible. This means we should instead look at the computed CSS if you are having issues with the above code removing things that are visible.
$('*').each(function(){
if($(this).css('visibility') == 'hidden' || $(this).css('display') == 'none'){
$(this).remove()
}
});
you are removing html elements which are not visible. you should remove its children.
try this
$('html').children().not(':visible').remove()
EDIT:
as Barmar said this only removes immediate children of html. use other answers to remove all elements.
This should do it:
$("body :hidden").remove();

jQuery Live traversal parent() selector

$(".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.

jQuery closest with attribute filter

Does jQuery 1.5.1 support attribute selectors in the closest method?
Given the structure below, el represents the checkbox with the value 513, and I'm trying to reach up and check the ancestor checkbox with the value of 0 with
$(el).closest("input[value='0']")[0].checked = true;
but the selector is returning nothing.
Am I missing something silly?
EDIT
Sigh, so the checkbox with value 0 isn't an ancestor to el, just a sibling of the ul ancestor. I got it to work with
$(el).closest("ul").siblings("input[value='0']")[0].checked = true;
But us there a cleaner way to grab it?
The .closest() method does support attribute selectors the same as any other jQuery method (although I don't know about the specific version of jQuery you mentioned), but the problem here is that .closest() doesn't just look in the general vicinity, it goes up through the ancestors. You are trying to select an input element that is not an ancestor of the element you are starting with. (Given that inputs can't contain other elements they will never be the ancestors of anything...)
You should be able to do it by selecting the target checkbox's parent li element first:
$(el).closest('li:has(input[value="0"])')
.find('input[value="0"]').prop("checked",true);
Demo: http://jsfiddle.net/Vpzyj/
Of course if you were able to add a class to the top level menu items you wouldn't need to mess around with ":has", you could just say:
$(el).closest('li.topMenu').find('input[value="0"]').prop("checked",true);
Check for: http://api.jquery.com/closest/
.closest( selector ) // version added: 1.3
I believe that you shouldn't need the [0] as .closest only returns the first element found unlike parents() which returns multiple.
$(el).closest("input[value='0']").prop("checked") = true;
is the way I would prefer to do it.
This is assuming you are trying to check the "value 0" checkbox when you check the check the 513 box.
Edit: Sorry, misread this. In reality you need to try and get the <\li> tag and then get the underlying checkbox. Where you are actually looking for the checkbox which is not a direct parent.
Edit 2: You realize your edit doesn't work right? Wait... it would... you used siblings not children my mistake.
Edit 3: How about using:
$(el).parents().get(':eq(2)').children(':first-child');
or are we not assuming the layout is fixed?
Closest checks up the DOM tree and not on the same level.
Regarding your question: yes, it does (http://api.jquery.com/closest/)
Your code does not work because closest() goes up through the DOM tree and in your case it would have to go up and and down because your checkbox 0 is not an ancestor of checkbox 513.

Abstract question about Dom Object as Value Object in Javascript

I've got an image gallery where each item in the gallery has a unique ID I'm using to tie it to my backend so that I can run DB actions etc. I'm using the data attribute to hold the 'imageid' on the parent div of each image for example: data-imageid="168"
The image gallery items have alot of nested divs so on rollover/click I find myself doing alot of the below in order to run actions:
$(this).parent().parent().attr('data-imageid');
It doesn't feel like a clean solution or the best performance wise. How are other people handling this? What's the best solution? Whatever the solution, this must be a problem solved everyday.
Edit: I realize my subject line is vague but it further explains where my heads at with this problem.
You can specify a criterion to go "up to", so if you want to go up to the nearest div or li or whatever, either use jQuery's .closest() or a simple upTo() function:
function upTo(el, tagName) {
tagName = tagName.toLowerCase();
var el;
do {
el = el.parentNode;
if (el && el.tagName && el.tagName.toLowerCase() == tagName) {
return el;
}
} while (el)
}
This sounds suspiciously like you have a case of divitis. In any case you can use .parents([selector]) with a filter to make your code a bit less verbose. e.g.
$(this).parents(".classOfDivContainingData").attr('data-imageid');
or you could loop through the parent divs on initial load, and copy the data attribute to the "this" in your example. Is there a reason why you can't set the attribute here in the first place?
You can try this
$(this).parents("[data-imageid]:first");
I would recommend you to use additional attributes to the elements:
<div data-imageid="367">
<div>
<div data-parent-imageid="367">
</div>
</div>
</div>
So you can make action knowing only the element which fired the event. Using this solution you don't care about structure of the elements, move them in the DOM tree freely and performance will be improved too.

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