find number of nodes between two elements with jquery? - javascript

I'm having a little trouble figuring out a fast way to accomplish a (seemingly) simple task. Say I have the following html:
<ul>
<li>One</li>
<li>Two</li>
<li id='parent'>
<ul>
<li>Three</li>
<li>
<ul>
<li>Four</li>
<li id='child'>Five</li>
</ul>
</li>
<li>Six</li>
</ul>
</li>
</ul>
And have the following two elements:
var child = $("#child");
var parent = $("#parent");
In this example, it's clear that:
child.parent().parent().parent().parent();
will be the same node as 'parent'. But the list I'm traversing is variable size, so I need to find a way to find out how many '.parent()'s I'll need to go through to get to that parent node. I always know where child and parent are, I just don't know how many 'layers' there are in between.
Is there any built in jQuery method to do something like this, or is my best bet a recursive function that gets the parent, checks if the parent is my desired node, and if not calls itself on its parent?
Edit: I may not have explained myself clearly enough. My problem isn't getting TO the parent, my problem is finding out how many nodes are between the child and parent. In my example above, there are 3 nodes in between child and parent. That's the data I need to find.

If you just want to get to the parent, do this:
child.parents("#parent");
That's easier than doing:
child.parent().parent().parent();
Or is there some other reason you need to know the number?
A simple loop could do it:
var node = child[0];
var depth = 0;
while (node.id != 'parent') {
node = node.parentNode;
depth++;
}

Try the closest() function
http://docs.jquery.com/Traversing/closest#expr
This will give you the total number of ul parents:
var numofuls = $(this).parents('ul').length;

Related

Find if ancestor has a certain CSS class

I would like to know if there is an easier way to check if an element has an ancestor with a particular class.
Consider the following HTML code:
<ul id="uniqueID" class="parentClass">
<li class="subclassA">
<div class="subclassB">
<nobr>
MyText
</nobr>
</div>
</li>
<li class="subclassA"> ... </li>
<li class="subclassA"> ... </li>
<li class="subclassA"> ... </li>
</ul>
<div>other elements in this page which I want to select</div>
Right now, I can select the element MyText by using a jQuery selector checking the href for a particular format. What I can then do is do .parent() a known number of times (4) and then check the class attribute of that particular element that I've now moved to. While this is working just fine, I am curious if there is a better way to do it, perhaps one that lets me be a bit more dynamic?
PS. There are a lot of elements that I'm selecting that'll fit this $('[href *= index.php]') format, so I want to keep those but remove the ones that fall under the categorization where they are a descendant of a member of class listclass. Currently I'm just selecting all of the elements with the selector above, then using an if statement to check through and see if it fits this condition. Again, if there is a more efficient way to do this (perhaps select these certain elements in the first place?) I would love to hear about it.
Current code:
$('[href *= "index.php"]').each(function(){
if ($(this).parent().parent().parent().parent().attr('class') != 'parentClass'){
//do things
}
});
To generalise you can use
.closest(".parentClass")
You can use closest and is:
$('[href*="index.php"]').each(function(){
if ($(this).closest('ul').is('.parentClass')) {
//do things''
}
});
if($(this).parents("ul.parentClass").length == 0){
//do something
}

Finding selector position within parent/set with jQuery?

I need to find out at what position in terms of parent > child:eq(n) the current selector sits, so that I can use .insertAfter() and .insertBefore() to properly move the element around within it's nest.
I've looked through several parts of the jQuery doc, but I've yet to find a function that gives the current selector's position within it's parent relevant to nest structure (as oppose to dimensional position when can be found through .postion()).
Any points to the right function for this, or a good way to move around elements in the following structure using jQuery would be greatly, greatly, greatly appreciated ;)!
<ul data-submenu="1" class="sub-menu">
<li>Text Module</li>
<li>Feature Module</li>
<li>Feature Module</li>
</ul>
(I need to move each <li> element around within the <ul> parent).
It seems you're looking for the index() method, which, if provided with no parameters, returns the elements position relative to it's siblings.
var index = $(this).index();
It sounds like you want to use $.each():
$('.sub-menu').children('li').each(function(index) {
console.log('This is the ' + index + ' li element.');
});

How to remove a parent node with all its children

I am learning the basics of javascript. now it DOM and i am stuck here, How can i remove a parent node with all its chldren. eg say i have the html code like this.
<ul id ="parent">
<li>Hi</li>
<li>How</li>
<li>Are</li>
<li>You</li>
</ul>
i wand to delete the <ul> element with all its child <li> nodes. I tried it to do like this document.getElementById('parent').removeNode(true); but its not working. Can anyone help me here. ?
You need to get a handle to the ul element then ask its parent element to delete it by passing the ul handle the parent's removeChild method.
This is how you would do it without the jQuery framework:
var x = document.getElementById('parent');
x.parentNode.removeChild(x);
Of course if you used jQuery it would be simple like this:
$("#parent").remove();
try this:
var childNode = document.getElementById("parent");
childNode.parentNode.removeChild(childNode);

Jquery recursive function combine nested li into one

I am trying to convert a nested li into one single li using recursive method using jquery
html as follows
<ul>
<li>item-1
<ul>
<li>item-1.1</li>
</ul>
</li>
<li>item-2
<ul>
<li>item-2.1</li>
<li>item-2.2
<ul>
<li>item-2.2.1</li>
<li>item-2.2.2</li>
</ul>
</li>
</ul>
</li>
<li>item-3
<ul>
<li>item-3.1</li>
</ul>
</li>
<li>item-4</li>
<li>item-5</li>
</ul>
Final single li as below
<ul>
<li>item-1</li>
<li>item-2</li>
<li>item-3</li>
<li>item-4</li>
<li>item-5</li>
<li>item-1.1</li>
<li>item-2.1</li>
<li>item-2.2</li>
<li>item-3.1</li>
<li>item-2.2.1</li>
<li>item-2.2.2</li>
</ul>
basically loop through each level then append to the end of the list.
Any ideas how I can achieve this? so it can handle any level of the list item.
Here is a recursive approach that will give the output you're looking for:
function recurseFetchListItems($ul)
{
var $li = $ul.remove().children("li").remove();
if ($li.length) {
$li = $li.add(recurseFetchListItems($li.children("ul")));
}
return $li;
}
It uses add() to accumulate the different levels of list items, while removing each level from the document. It also uses children() instead of find() in order to process a single depth level per call.
From there, you only have to start from the first <ul> element, add the cumulated set of list items back to the document, and wrap them in a new <ul> element:
$(document).ready(function() {
recurseFetchListItems($("ul:first")).appendTo("body").wrapAll("<ul>");
});
You can see the results in this fiddle.
Original (misguided) answer follows:
You don't really need a recursive function to do that, because whole DOM element trees can be matched with a single selector. For instance, $("li") matches all the list items, whatever their depth is.
So, to achieve what you want, we only need to match all the <li> elements, remove their parent <ul> elements from the document, then wrap the list items into a new <ul> element using wrapAll() and add that element back:
$(document).ready(function() {
$("li").parent().remove().end().appendTo("body").wrapAll("<ul>");
});
You can see the results in this fiddle.

how to select 2nd <LI> element using javascript?

Using JavaScript, how can I dynamically change one of the list-items below from this:
<ul class="tabbernav">
<li class="tabberactive"><a title="All">All</a></li>
<li class=""><a title="One">One</a></li>
<li class=""><a title="Two">Two</a></li>
<li class=""><a title="Three">Three</a></li>
</ul>
to
<ul class="tabbernav">
<li class="tabberactive"><a title="All">All</a></li>
<li class=""><a title="One">One</a></li>
<li class=""><a title="Two">-----------NEW LIST ITEM CHANGED---------</a></li>
<li class=""><a title="Three">Three</a></li>
</ul>
I guess you could use getElementsByTagName inside of the ul to get all your list items inside an array. Then you can just edit the third element in your array, with index number 2.
var lItems = document.getElementsByTagName("ul").getElementsByTagName("li");
lItems[2].innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----</a>";
That will ofcourse get all ul elements on the page, and might lead to some strange results if you have more than two uls in your document. But you get the idea, right? Just ask some more if you don't understand what I'm trying to say.
Okay, the above code doesn't really work properly. I've modified my code a bit, but that also included a change in your HTML, as i presume you'll only have one ul "tabbernav", thus I changed it from class="tabbernav" to id="tabbernav". This is the code to do what you want.
var ul = document.getElementById("tabbernav");
var liArray = ul.getElementsByTagName("li");
for (var i = 0; i < liArray.length; i++) {
if(liArray[i].childNodes[0].title == "Two") {
liArray[i].innerHTML = "Your desired output";
}
}
Hope that helps you some more :)
I also suggest using jQuery, which makes selections like this trivial. In your case, you can use the :eq psuedo-selector to get the second line element:
$('.tabbernav li:eq(1)')
This selects the DOM element which is the second li (indexes start at 0) in an element with the class tabbernav. It returns a jQuery object which you can chain other methods to. Changing the inner HTML is done with .html('Your html here').
This is how you select the third Li element of your Ul list in pure JavaScript.
document.querySelectorAll("li")[2].innerHTML = "vasile";
Replace "Vasile" with your desired text.
var list = document.getElementsByTagName("li");
list[2].innerHTML = "<a title='Two'>------NEW LIST ITEM CHANGED----</a>";
this will work perfect
I know that this question is old but since it's still open, see how I modified the first answer. I feel someone else might need it.
>var lItems = document.getElementsByTagName("ul")[0];
>>var nth = lItems.getElementsByTagName("li")[2];
>>>nth.innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----> </a>";
So that basically solves it up by specifying the position of the lItems in particular to grab and in this case [0]. The code will not work properly if that position is missing because getElementsByTagName(NAME) returns a collection of html elements bearing that NAME specified. So that if you don't specify which among them all, the code fails.
If you like code reuse, see a function you can use for that. You just need to specify the parent element and its position and the childNode position and you get the same thing.
>var nthChild = function(parent, pos, childPos){
>>parent = document.getElementsByTagName(parent)[pos];
>>>return parent.children[childPos];
>>>>};
//used thus:
>nthChild("ul", 0, 2).innerHTML = "<a title='Two'>----NEW LIST ITEM CHANGED-----> </a>
";
How do you identify which <li> is the one you want to modify?
If you're doing it by index you could do something like this I think:
var list = document.getElementById("listid");
list.childNodes[2].innerHtml = "<a title='Two'>-----------NEW LIST ITEM CHANGED---------</a>";
Look into using a Javascript library such as JQuery. That will make your life a lot easier. Then you can do something like this:
$('li a[title=Two]').text('Changed Text Goes Here');
You'll need to check my syntax (not sure about the text() function), but it's easy enough to look up in JQuery's api docs.

Categories