nearest ancestor node in jQuery - javascript

In my javascript experience, I found that is a very common task "searching the nearest ancestor of an element with some condition (tag name, class,...)".
Can the parents() method of jquery do the job? The order of returned elements of parents() is predictable? Is top-to-bottom or bottom-to-top?
For the moment I use this utility function:
function ancestor(elem, selector) {
var $elem = $( elem ).parent();
while( $elem.size() > 0 ) {
if( $elem.is( selector ) )
return $elem;
else
$elem = $elem.parent();
}
return null;
}
Can someone tell me if there is a clever way to do the job?

Edit: Since jQuery 1.3, this has been built in as the closest() function. eg: $('#foo').closest('.bar');
yep - parents() traverses up the tree.
<div id="a">
<div id="b">
<p id="c">
<a id="d"></a>
</p>
</div>
</div>
$('#d').parents("div:first"); will select div b.

Adding to #nickf's answer:
jQuery 1.3 simplifyed this task with closest.
Given a DOM:
<div id="a">
<div id="b">
<p id="c">
<a id="d"></a>
</p>
</div>
</div>
You can do:
$('#d').closest("div"); // returns [ div#b ]
[Closest returns a] set of
elements containing the closest parent
element that matches the specified
selector, the starting element
included.

You should use closest, because parents won't give you the result you expect if you're working with multiple elements. For instance, let's say you have this:
<div id="0">
<div id="1">test with <b>nested</b> divs.</div>
<div id="2">another div.</div>
<div id="3">yet <b>another</b> div.</div>
</div>
and you want to add a class to the divs that have a <b> element as their immediate child (ie, 1 and 3). If you use $('b').parents('div'), you get divs 0, 1 and 3. If you use $('b').parents('div:first'), you only get div 1. To get 1 and 3, but not 0, you have to use $('b').closest(elem).

closest() starts at current element, if the parent you are looking for has the same tag as current (eg. both are divs), use parent().closest()

Related

check for child element, hide parent if child exists

I have tried this:
<script>
$(document).ready(function() {
$('#title_article:not(:has(>hr:first-child))').hide();
});
</script>
But never shows..
<div id="title_article">
<span style="padding-left:121px;">Article | </span>
<span class="entry-date"><?php echo get_the_date(); ?></span>
</div>
Seeking to check for child element of <hr> if <hr> exists hide parent or #title_article
<hr> would not be within <div id="title_article"></div> but below:
<!-- page content -->
<div id="title_article></div>
<hr>
<!-- page content -->
You are looking for .next()
Get the immediately following sibling of each element in the set of matched elements. If a selector is provided, it retrieves the next sibling only if it matches that selector.
You have to do something like:
if ($('#title_article').next('hr').length)
$('#title_article').hide();
In your example, <hr> is not a child element of #title_article, but a sibling.
$(document).ready(function() {
if($("#title_article").next("hr").length==0)
$("#title_article").hide()
});
http://fiddle.jshell.net/prollygeek/57gcx6or/3/
Edit:
Use .siblings()
You could use the children function jQuery has to offer.
Try it in this context:
if($('#title_article').children('hr').length > 0) {
$('#title_article').hide();
}
Or this:
if($('#title_article').children('hr').length != 0) {
$('#title_article').hide();
}
You could also use the parent function
Try it in this context:
$('#title_article hr').parent().hide();
This will hide every #title_article that has an hr in it.

targeting a div in an ocean of nested dynamically added divs

I'm using the liferay framework and I need to add a JavaScript detected inline height to a very very specific div in my page. The problem is I need to target it going through an unknown number of dynamically added divs with dynamically added classes and IDs. To complicate this even further, the divs are randomly siblings or nested in each other.
Here's what it looks like:
<div class="known-class">
<div class="unknown dynamicallygenerated"></div>
<div class="unknown dynamicallygenerated">
<div class="unknown dynamicallygenerated">
<div class="unknown dynamicallygenerated"></div>
<div class="unknown dynamicallygenerated">
<div class="DIV-I-WANT-TO-TARGET">this is the div i need to Target with my css/javascript</div>
</div>
</div>
</div>
obviously I can't target it simply with
function resize() {
var heights = window.innerHeight;
jQuery('.DIV-I-WANT-TO-TARGET').css('height', heights + "px");
}
resize();
Because that class is present elsewhere, I would rather target it with something like.
jQuery('.known-class .DIV-I-WANT-TO-TARGET')
Which obviously doesn't work because there's a ton of other divs in the middle and my div is not a child of ".known-class"
I was asking myself if there was any jQuery that could help. Something like:
Catch any div with .DIV-I-WANT-TO-TARGET class that is "generically" inside another div that has .known-class
Is this possible? thanks a lot for your help!
Something like this would work:
// this will target the known-class and find all children with DIV-I-WANT-TO-TARGET
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET');
// this will target the known-class and find the first DIV-I-WANT-TO-TARGET
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET').first();
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET:first');
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET:eq(0)');
$('div.known-class').find('div.DIV-I-WANT-TO-TARGET').eq(0);
You can try in your css file
.known-class div div div div{}
The last div being the DIV-I-WANT-TO-TARGET
Assuming that you are adding the divs starting from the outer to the inner
Assign an equal name plus a number starting from 1
<div class="known-class">
<div class="unknown dynamicallygenerated" id="dynamicdiv1"></div>
<div class="unknown dynamicallygenerated" id="dynamicdiv2">
<div class="unknown dynamicallygenerated" id="dynamicdiv3">
<div class="unknown dynamicallygenerated" id="dynamicdiv4"></div>
<div class="unknown dynamicallygenerated" id="dynamicdiv5">
<div class="DIV-I-WANT-TO-TARGET" id="dynamicdiv6"></div>
</div>
</div>
</div>
The use jQuery [.each][1] to loop through all the divs on the document
$( document.body ).click(function() {
$( "div" ).each(function( i ) {
if ( this.style.color !== "blue" ) {
this.style.color = "blue";
} else {
this.style.color = "";
}
});
});
When you reach the last item in numeric order. (you can use any split function) add the attributes to that div
you need to select last div inside the known-class:
$('.known-class').find('div:last').css('background', 'Red')
OR if you want to select all the .known-class :
$('.known-class').each(function() {$(this).find('div:last').css('background', 'Red')});
Actually your selector works just fine:
$('.known-class .DIV-I-WANT-TO-TARGET')
With a space, selectors will find any descendant.
The search is only limited to direct descendants (immediate children) if you use the > operator.
So $('.known-class > .DIV-I-WANT-TO-TARGET') would not find what you wanted.

Select the parent div only if it has id attribute

I am trying to find the first parent div that has id attribute.
The selectors is 'div .img' and it could be inside any nested div
<div id='div1'>
<div id='div2'> first nested parent div
<div>second div
<img class='img' src='test.jpg'/>
</div>
</div>
<div> //this div has no id
<div>another div
<img class='img' src='cookie.jpg'/>
</div>
<div>
</div>
The selector
$('div.img')
should output
div2
div1
div2 shows first becasue div2 has id attribute and it is the parent of the test.img
div1 shows next becasue cookie is the second image and the first parent div that has div1 id
I have tried
if($('div .img').find('.img').closest('div').attr('id')){
console.log($('div.img').closest('div').attr('id'))
}
but it doesn't work. Are there anyways to do this? Thanks so much!
I'd suggest:
var divsWithIDs = $('img.img').parents('div[id]').get();
console.log(divsWithIDs);
JS Fiddle demo.
This approach looks for the img.img element, then looks through the parents of those images to find those div elements that have an id attribute. The get() method converts the selection to an array.
References:
get().
parents().
var IamadivparentandhaveanId=$('div').find('img').closest('div[id]');
OR using the class:
$('div').find('img.img').closest('div[id]');
or
$('.img').closest('div[id]');
EDIT per comment to clarify use to ONLY find them once:
$('img.img').each(function(){
$(this).closest('div[id]');
});// or use .filter if that is perferred
You can use:
var $divsWithIds = $('img.img').closest('div[id]');
This is the same as the answer by #David Thomas, except using closest instead of parents. It selects all "img" elements with the ".img" class, and then gets the closest parent for each one.
In the following situation, using parents() will include both "div1" and "div2", whereas using closest() will return only 'div2':
<div id='div1'>
<div id='div2'>
<div>
<img id="img1" class='img' src='test.jpg'/>
</div>
</div>
</div>
<div>
<img id="img2" class='img' src='cookie.jpg'/>
<div>
You can use jQuery each to iterate over the parents.
$('div img').each(function (a, b) {
var x = $(b).parent();
while (x && !x.attr('id')) {
x = x.parent();
}
console.log(x.attr('id'));
});

Select all elements with class name start by specific string by jquery

This is my HTML DOM:
<div id="d1" class="Addable"><span class="yak-bak-nak vok-do-maria"> <p>Goola1</p>
<span>volaa</span></span></div>
<div id="d2" class="Addable"><span class="yak-bak-val vok-do-maria"> <p>Goola2</p></span></div>
<div id="d3" class="Addable"><span class="vok-do-maria yak-bak-nak"> <p>Goola3</p></div>
<div id="d4" class="Addable"><span class="vok-do-maria yak-bak-nak"> <p>Goola4</p></div>
<input type="button" class="Doop" value="Do" />
So I want to remove all divs that class names start with "yak-bak" that means all of the above.
so I try this script:
$('.Doop').click(function () {
$('span[class|="yak-bak"]').parent('div').remove();
});
but just d1 and d2 remove and d3, d4 remain because the yak-bak class come as second also I try this:
$('.Doop').click(function () {
$('span[class^="yak-bak"]').parent('div').remove();
});
So what is your suggestion? where is the problem?
To target elements with multiple classes, where one of then starts by "yak-bak" at the beginning of the attribute class, or at the middle of it, you can use:
JQUERY
$("div[class^='yak-bak'],div[class*=' yak-bak']").parent('div').remove();
This will select elements where the class starts by "yak-bak" and elements that contain a class stated by "yak-bak".
$('div').filter(function() {
return $(this).has('span[class^="yak-bak"]');
}).remove();
DEMO
Or
$('div').filter(function() {
return $(this).has('span[class^="yak-bak"], span[class*=" yak-bak"]');
}).remove();
But First one will enough.

Select base elements?

How can I select elements (using jQuery) that do not contain any elements?
For example, in the following tree:
<div class="a">
<div class="b">
<div class="c"></div>
</div>
<div class="d"></div>
<div class="e">Lorem</div>
</div>
Only the <div>s with class c, d, and e will be selected.
$(':not(:has(*))')...
LIVE DEMO
If you want to do it with the filter function, be aware that > element will be deprecated in next jQuery versions!
you can use this:
$('*').filter(function(){
return $('*', this).length == 0
})
:empty selector won't work here because there is text node in the <div class="e">
use :empty selector:
$(':empty')...
docs:
Description: Select all elements that have no children (including text nodes).
Try this:
$('*').filter(function() {
return $(this).children().length == 0;
});
You might also be able to use (faster) native DOM access inside the filter function:
return this.children.length == 0;

Categories

Resources