Jquery recursive function combine nested li into one - javascript

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.

Related

jQuery selector and cloning in a smart fashion

I am having trouble finding the way to solve this issue. I have this ul-menu output by Wordpress:
<ul class="menu">
<li>
Page 1
</li>
<li>
Page 2
</li>
</ul>
But I want the end result to be like this - cloning and appending the anchor and put a clone below:
<ul class="menu">
<li>
Page 1
Page 1
</li>
<li>
Page 2
Page 2
</li>
</ul>
I have used jQuery - but I am not having any luck at all for 2 hours of trial and error. This is as close as I can get. But it is wrong.
/*jQuery*/
$('.menu li a:first-child').eq(0).clone().insertAfter('.menu li a:first-child');
Fiddle here: http://jsfiddle.net/67jXz/1/
You're not supposed to .eq(0); that will limit it to the first a element that's matched, so that will be cloned and inserted after every subsequent a, resulting in copies of "Page 1".
Instead, you need to perform the cloning and inserting for each individual element by iterating with .each(), like so:
$('.menu li a:first-child').each(function() {
$(this).clone().insertAfter(this);
});
Note that the .insertAfter(this) part refers to inserting the cloned element after the original element that was matched by the .menu li a:first-child selector; the same this in $(this) that references the matched element.
Updated fiddle
Try this code:
$(function(){
$('.menu li a:first-child').each(function(k,v){
$(v).clone().insertAfter(v);
});
});
jsfiddle

Filter method jQuery

Please help me out with the following code. I don't understand it. I have to use a snippet like this in my project.
$('strong', this) <- this part is not clear to me at all.
Please be kind enough to explain the whole code line by line if possible.
<ul>
<li><strong>list</strong> item 1 -
one strong tag
</li>
<li><strong>list</strong> item <strong>2</strong> -
two <span>strong tags</span>
</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
<li>list item 6</li>
</ul>
JavaScript:
$('li').filter(function(index) {
return $('strong', this).length == 1;
}).css('background-color', 'red');
$('strong', this) is jQuery selector with $(target, context) format.
According to your code:
this refers to li and $('strong', li) is searching a <strong> that within that li tag.
This statement can also be written as:
$(this).find('strong') and from jQuery library code you'll see that:
$(target, context) format internally implement the
$(context).find(target) process.
For more see here.
The code is basically getting a list of li elements using the jQuery $('li') (this will get all <li> ... </li> tags on the page)
It then reduces this set with the .filter function, filter takes a function as an argument, the function is called on each element in the list, if it returns true the element is returned in the list from filter if it return false the item is ignored.
In this context the function calls $('strong', this).length == 1 where this is the li tag that currently being decided checked by the filter, as mentioned in other answers it's simply checking returning the list of <strong>..</strong> tags in the current li. If there is not strong in the current li, length is 0 so the function returns false, this means the filter wont return that element in the list it produces, it then moves on to the next li.
this means the the first part of the code simply produces a list of li's with a strong tag in them, this is then chained with the css function which colours all those tags in red.
Hope that helps.

Parallel sorting two different lists using jquery sortable

I am having very specific case where I needed to split the data into two different lists in html. Like this:
<ul id="first_list">
<li ref="1">The quick brown</li>
<li ref="2">My father works</li>
</ul>
And the second list is like:
<ul id="second_list">
<li ref="1">jumps over the lazy dog</li>
<li ref="2">at the Ministry of Defense</li>
</ul>
So as you can see I from the "ref" attribute I know which <li> element from the second list is a continuation of which <li> element from the fist list.
Now I need to enable the jQuery UI sortable() to those lists but when I reorder the first I need the second reordered too. I tried using handle but it doesn't works because it looks like the handle element needs to be inside the element which is moved but these two are at a different places in the page.
I do believe that you should have shared some of your code (what you've tried), and I'm assuming you are familiar with Sortable plugin that you are using. You should run the below code on success event of Sortable so as soon as you sort any LI, the other list will be sorted too. Anyways,
Try this:
//This line stored the LIs in a temp variable and remove it
var $cachedList = $('<div />').html($('#second_list').html());
$('#second_list > li').remove();
//This line loads up the first UL's LIs and replaces the content for each LI
//using $cachedList.
$('#second_list').html($('#first_list').html()).find('li').each(function () {
$(this).html($cachedList.find('li[ref="'+$(this).attr('ref')+'"]').html());
});
Demo: http://jsfiddle.net/AR8px/

jQuery Retrieve ID

I tried tackling my first project, an accordion menu. I have set to each item that needs to show/hide a class of .menu and an id.
While trying to retrieve the id's of each element i've used this statement:
var $currentId = $('ul.menu').attr('id');
Only problem is that it seems this only retrieves the id of the first element. Can anyone tell how can I retrieve all the It's to store them in a variable. I am planing to use if statements in order to check for each particular id when it's clicked.Thank You!
EDIT:It seems I was misunderstood what I have to do is this I'll start from the beginning:
Here is my HTML :
<ul id="container">
<li class="select">Downloads</li>
<li >
<ul class = "menu" id="first" >
<li>
iTunes
</li>
<li>
iTunes
</li>
</ul>
</li>
<li class="select">
Products List
</li>
<li>
<ul class = "menu" id="second" >
<li>
iTunes
</li>
<li>
iTunes
</li>
</ul>
</li>
What I have to do is when I click on the li with class of select I have to make the ul with the class of menu appear. How I wanted to do this to retrieve all the id's of the ul.menu and store them in a variable and when I click on any of the li.select the underlying ul should show.
Use each loop to get the ids of each element. Inside the loop use just just use this.id to get the id of the element where `this represents the dom element.
$('ul.menu').each(function(){
alert(this.id);
});
If you want the ids of all elements into an array you can use jQuery map method.
var Ids = $.map($('ul.menu'), function(){
return this.id;
});
map() translates all items in an array or object to new array of items. Ids will contain the ids of all the elements.
Then you can use $.inArray to search for a specific id within it. It will return its index or -1 if not found in the array.
if($.inArray("someId", Ids) != -1){
//Id found in the array
}
You may need to iterate over all the items and check the current status.
You can do this using the each method.
You can even dynamically add a listener for the click event for each element that matches your selector (in this case 'ul.menu'):
$('ul.menu').each(function(){
$(this).click(function()
{
alert('click');
});
});
EDIT: You can do this to hide/show the secondary items when the li.select items are clicked:
$('li.select').click(function(){
$(this).next().toggle();
});
This will give you an array that contains the ids:
var listOfIds = [];
$('ul.menu').each(function(){ listOfIds.push(this.id); });
The better thing to do would be to use
$('ul.menu li').click(function(event){
$(this).attr('id');
});
That will setup an event listener for each menu item and then allow you to do some thing when that menu item is clicked. The line with $(this).attr('id') can be replaced with any code you wish. Maybe even $(this).find('ul').show();

find number of nodes between two elements with jquery?

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;

Categories