How to save HTML ul-li structure into javascript object - javascript

i have this following html structure usilg ul and li.
<ul class="treeview" id="productTree">
<li class="collapsable lastCollapsable">
<div class="hitarea collapsable-hitarea lastCollapsable-hitarea"></div>
<span id="top1" class="">top1</span>
<ul>
<li class="collapsable lastCollapsable">
<span class="">mod1</span>
<ul>
<li class="last">
<span>bottom1</span>
</li>
</ul>
</li>
</ul>
</li>
<li class="collapsable lastCollapsable">
<span id="top2" class="">top2</span>
<ul>
<li class="collapsable lastCollapsable">
<span class="">mid2</span>
<ul>
<li class="last">
<span>bottom2</span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
the website allows user to add more data under this structure and am using jquery treeview to show the tree structure dynamically.
Now i need to save this whole ul-li structure into a js object for future use in the website. how do i achieve this? the last node("bottom1 and bottom2 here") has a class "last" if that helps.
as we can add data dynamically we can be sure how much levels of ul li is there at the end when user clicks "save"

You can use recursive function to save a tree object;
function save(obj_ul, tree){
var obj_lis = obj_ul.find("li")
if (obj_lis.length == 0) return;
obj_lis.each(function(){
var $this = $(this);
if($this.parent("ul").get(0) == obj_ul.get(0))
{
tree.push({
name : $this.find('> span').text(),
child : save($this.find("ul").first(), [])
});
}
});
return tree;
}
console.log(save($('#productTree'), []));

If you want to reprouce the same thing verbatim, as a string of HTML elsewhere on the site, you could just do this? Then .append() or .prepend() treeview where you like.
​var treeview = $('#productTree').parent().html()

Assuming you want JSON:
function save(){
var tmp = [];
$('#productTree li.collapsable').each(function(){
var $this = $(this),
$spans = $this.find('span'),
o = [];
$spans.each(function(){
o.push($(this).text())
})
tmp.push(o);
});
return tmp;
}
You could also use map() to accomplish the same thing, too.
EDIT: Updated, assuming your text will live inside a span. This will create an array of arrays, each containing the text from the spans inside each of your list-items.

Related

How to query a data attribute value in javascript (no jquery)?

If I have a grid made up of ul's (the rows) and li's (the cells), I wanted to get a specific cell based on the data attribute values of the ul and the li:
document.querySelectorAll(div.grid ul[data-${this.y}] li[data-${this.x}]'_
When I searched on MDN, I only found how to retrieve the html element based on the data attribute, but not it's value.
Any pointers would be greatly appreciated - also no jQuery please.
You could use the String interpolation and get it worked.
Here is what you could do.
document.addEventListener('DOMContentLoaded', function() {
let ulData = 2,
liData = 4;
document.querySelector(`div.grid ul[data="${ulData}"] li[data="${liData}"]`).classList.add("red");
});
.red {
color: red;
}
<div class="grid">
<ul data="2">
<li data="1">
One
</li>
<li data="2">
Two
</li>
<li data="3">
Three
</li>
<li data="4">
Four
</li>
<li data="5">
Five
</li>
</ul>
</div>

Using jQuery .nextUntil() on split lists

Is it possible to get the .nextUntil() to work on split lists, or get the same functionality?
So I am trying to implement the ever so popular shift select for my items, and since they are ordered in a list in my application I want to be able to select across <ul> borders.
I have the following set of DOM elements:
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
And using something like this:
$('li.clicked').nextUntil('li.selected');
I'd like a list containing the following elements
[ <li class="item">third</li>,
<li class="item">fourth</li>,
<li class="item">fifth</li> ]
However all I get is the elements leading up to the split </ul>. Is there any way of doing this? I have also tried to first selecting all items with $('.item')and then using .nextUntil() on them without any luck.
Is this what you are looking for?
$('li').slice($('li').index($('.clicked'))+1,$('li').index($('.selected')));
For reference
Jquery.Index
Jquery.Slice
Edit
So if you do
$('li')
you will get an array of all elements 'li' getting:
[<li class=​"item">​first​</li>​,
<li class=​"item clicked">​second​</li>​,
<li class=​"item">​third​</li>​,
<li class=​"item">​fourth​</li>​,
<li class=​"item">​fifth​</li>​,
<li class=​"item selected">​sixth​</li>​,
<li class=​"item">​seventh​</li>​]
Since it is an array you can slice him to get an sub array you just need two positions, where to start and here to finish.
//start
$('li').index($('.clicked'))+1 // +1 because you dont want to select him self
//end
$('li').index($('.selected'))
For better preformance you should before create an array with all li so it will not search all dom 3 times for the array of 'li'
var array = $('li');
var subarray = array.slice(array.index($('.clicked'))+1,array.index($('.selected')));
Assuming these lists cannot be merged into one, it is impossible using the nextUntil method. This is because of how jQuery performs traversing. According to the documentation,
Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed.
fifth is not a sibling of the clicked element, but rather it is a child of the sibling of the element's parents.
I came up with two possible solutions.
Solution 1: Combine NEXT and PREV traversals
Assuming that .clicked is always in the first list and .selected is always in the second list, combining prevAll() with nextAll() should do the trick. This assumes that the order is the same.
var siblings = $("li.clicked").nextAll()
Get all siblings of the current element AFTER the element itself.
var distantSiblings = $("li.selected").prevAll();
Get all distant siblings after the first element, but before the second one.
siblings.push(distantSiblings);
Combine them into two and then iterate over each element.
var siblings = $("li.clicked").nextAll()
var distantSiblings = $("li.selected").prevAll();
siblings.push(distantSiblings);
siblings.each(function() {
$(this).addClass("blue");
});
.blue { color: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
http://jsfiddle.net/r15z10o4/
Note:
You will notice that the above code works, however it might not be the optimal solution. This is only confirmed to work for your example above. There may also be a less verbose solution.
Solution 2 (Find index of all list items)
Another idea is to find the index of all items, and collect the elements that are sandwiched between those two indices. You will then want to use the 'slice' selector to get the range in between.
var items = $(".item");
var clicked = $(".clicked");
var selected = $(".selected");
var clickIndex = items.index(clicked);
var selectIndex = items.index(selected);
$("li").slice(clickIndex + 1, selectIndex).addClass("blue");
var clicked = $(".clicked");
var selected = $(".selected");
var clickIndex = $("li").index(clicked);
var selectIndex = $("li").index(selected);
$("li").slice(clickIndex+1, selectIndex).addClass("blue");
.blue { color: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
You can do it manually by selecting all these items at once, and using loops.
Consider the parent element, let's say "container":
<div id="container">
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
</div>
Now, you can select all these items:
var $items = $("#container > ul > li.item"); // or simply $("#container .item");
And iterate through them:
var $items = $(".item"), $result = $(), found = false;
for (var i = 0; i < $items.length; i++)
{
$currentItem = $items.eq(i);
if ($currentItem.is('.clicked')) {
found = true;
continue;
}
if ($currentItem.is('.selected'))
break;
if (found)
$result = $result.add($currentItem);
}
console.log($result);
Here is the working JSFiddle demo.
In any case it feels like you will need to define groups of li.
I think the easiest is to create a function getting a list of lis that you can request any way you want then to filter the el you are interested in.
function elRange(elList, start, end){
// we do not use indexOf directly as elList is likely to be a node list
// and not an array.
var startI = [].indexOf.call(elList, start);
var endI = [].indexOf.call(elList, end);
return [].slice.call(elList, startI, endI + 1);
}
// request the group of *ordered* elements that can be selected
var liList = document.querySelectorAll('ul.current > li, ul.later > li');
var selectionEnd = document.querySelector('.selected');
[].forEach.call(liList, function(li){
li.addEventListener('click', function(){
var selected = elRange(liList, li, selectionEnd);
selected.forEach(function(el){
el.classList.add('red');
})
});
});
.selected {
color: blue;
}
.red {
color: red;
}
<ul class="current">
<li class="item">first</li>
<li class="item">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>

Displaying selection text in span

So let's say I have this code:
<span id="select_list">
<ul>
<li><a id="1">1</a></li>
<li><a id="2">2</a></li>
<li><a id="3">3</a></li>
</ul>
</span>
<span id="selection"></span>
And let's also assume that there are a lot of list elements, ex. '4,5,6,7... etc'.
Can I get a html file, that is basically just text, that corresponds to the list element's ID (ex. 1.html, 2.html,... etc), to show in 'selection'?
If so how?
Thanks for your time. Hope I explained it well.
Something like this (jQuery) should work:
var list = $("#select_list");
var sel = $("#selection");
$("a", list).on("click", function(){
var id = $(this).attr("id");
sel.load(id+".html");
});
<div id="select_list">
<ul>
<li id="1">1</li>
<li id="2">2</li>
<li id="3">3</li>
</ul>
</div>
<div id="selection"></div>
i would use a div not span spans are for if you want to change the size of something particular like this:
<li id="1" href="#"><a href="#"><span style="color: red;
font-size: 30px">1</span></a></li>
and from what i am understanding you want a selector to select them in css?
if so this is how:
#select_list ul li:nth_child(1) {
}
or
#select_list ul li#2 {
}
hope this helps you
I would suggest using data-attributes instead of IDs.
HTML
<ul class='selection-list'>
<li data-name='Dog'>Dog</li>
<li data-name='cat.html'>Cat</li>
<li data-name='45'>Fourty Five</li>
<li data-name='Triangle'>Three sides</li>
</ul>
<div class="output js-output"></div>
jQuery
var $output = $('.js-output');
$('.selection-list li').on('click', function() {
var selectionValue = $(this).data('name');
$output.text(selectionValue);
});
CSS
.selection-list li {
cursor: pointer;
}
jsFiddle
iframe
I'm starting to think that you are asking for an iframe with dynamic source. The question is unclear. You may want to try and rewrite it. - Here is what I think you may be after...
HTML
<ul class='selection-list'>
<li data-url='http://reputable.agency'>Reputable Agency</li>
<li data-url='http://perpetual.education'>Perpetual Education</li>
<li data-url='http://example.com/index.html'>Example.com</li>
</ul>
<iframe src='http://example.com' class="output js-output"></iframe>
JavaScript / jQuery
var $output = $('.js-output');
$('.selection-list li').on('click', function() {
// get the 'data-url' from the element...
var selectionValue = $(this).data('url');
// put that data-url into the src attribute of the iFrame
$output.attr('src', selectionValue);
});
Also..
Note that if you are using the same domain for all of these, you can build those urls differently to keep things simple.
<li data-url='index.html'>Example.com</li>
$output.attr('src', 'http://yoursite.com/' + selectionValue);
jsFiddle
AJAX
Now I'm wondering if you mean AJAX. Here is an example - but it's not tested because I don't have access to a bunch of relative URLs - but here is the basics - and should lead you to the right documentation.
HTML
<ul class='selection-list'>
<li data-url='index.html'>Reputable Agency</li>
<li data-url='index.html'>Perpetual Education</li>
<li data-url='index.html'>Example.com</li>
</ul>
<div class="output js-output"></div>
JavaScript / jQuery
var $output = $('.js-output');
var getOtherPage = function(target) {
$.ajax({
url: target,
success:function(response){
$output.html(response);
},error:function(){
alert("error");
}
});
};
$('.selection-list li').on('click', function() {
var selectionValue = $(this).data('url');
getOtherPage(selectionValue);
});

jQuery: selecting LI's parent text

I have a classic root structure represented in HTML by ULs (and Lis ofc.). What I need to do is that when I click on any of my LI items (or in fact element as it contains only s) I want to get its text and text of its LIs parents.
Heres my sample of Unordered List (already wrapped):
<ul>
<li class='firstLevel'><a href='#'>1</a>
<ul>
<li><a href='#'>11</a>
</li>
<li><a href='#'>12</a>
</li>
</ul>
</li>
<li class='firstLevel'><a href='#'>2</a>
<ul>
<li><a href='#'>21</a>
<ul>
<li><a href='#'>211</a>
</li>
<li><a href='#'>212</a>
</li>
</ul>
</li>
<li><a href='#'>22</a>
<ul>
<li><a href='#'>221</a>
<ul>
<li><a href='#'>2211</a>
</li>
<li><a href='#'>2212</a>
</li>
</ul>
</li>
<li><a href='#'>222</a>
<ul>
<li><a href='#'>2221</a>
</li>
<li><a href='#'>2222</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li class='firstLevel'><a href='#'>3</a>
</li><br>
<li class='firstLevel'><a href='#'>4</a>
<ul>
<li><a href='#'>41</a>
</li>
<li><a href='#'>42</a>
</li>
</ul>
</li>
</ul>
For example when I click 3, I get only 3, 'cause it has no parent. But if I click on 2222 element, I should get a 2222, 222, 22 and 2 output.
My thoughts were: First, I should get a level of ULs clicked, I've done this with this code:
$("li").click(function (e) {
var cnt = $(e.target).parents('ul').length;
});
In a cnt variable is stored level of plunge - so when I click on 2222 element, cnt = 4. (4 because root alredy returns a 1, not a zero.)
Next step is to get the texts of this parent elements. I tried to approach this elements with jQuerys .eq() function but it doesn't work correctly. Heres my test:
$("li").click(function (e) {
var cnt = $(e.target).parents('ul').length;
vat outputString = '';
cnt--;
for (var i = 0; i < cnt; i++) {
outputString += $(e.target).parents('li').eq(cnt).text();
outputString += '###'; //texts separator
}
});
But this approach returns all of LI item text (including his descendants) so it returns whole tree-text structure.
To summarize my question - What functions/how should I continue to get the desired output?
Heres a jsfiddle example: http://jsfiddle.net/F548m/1/
.text will always return the combined text of all descendants of an element. What you want seems to be the text of the a child of li element, not the text of the li element itself.
So with this in mind, this should do what you want:
$("li").click(function (e) {
var text = $(this).parents('li').map(function() {
return $(this).children('a').text());
}).get();
text.unshift($(this).children('a').text();
var outputString = text.join('###');
});
DEMO
This gets all the li ancestors of the clicked element and maps them to their a children's text value. Then it adds the text value of the clicked elements a children to the begnning of the array. The order of the values is from the clicked element up to root.
If you want it the other way round, i.e. the clicked element's value last, the code actually becomes a bit simpler:
$("li").click(function (e) {
var text = $(this).parents('li').addBack().map(function() {
return $(this).children('a').text());
}).get();
var outputString = text.join('###');
});
I think .parents() can help you
Check demo
You need to get the text of the a
var str=$(this).find("a:first").text();
$.each($(this).parents("li"), function (i, v) {
str+=", "+$(this).find("a:first").text();
});
alert(str);
e.stopPropagation();
http://jsfiddle.net/F548m/3/
you should use jQuery.closest() function.

jQuery UI Sortable does not serialize/toArray child elements

I have the following list and when I call toArray or serialize it only offers me the parent <li>. I am trying to get hold of the whole hierarchy so I can store this information into a self referencing heirachy table in the database. The result here shows item_1,q_6,a_7,g_8. Where is item_3,item_4,item_5.
Thanks
<div id="example5">
<ul>
<li id="item_1">Item 1
<ul id="item_2">
<li id="item_3">Item 1 1<ul></ul></li>
<li id="item_4">Item 1 2<ul></ul></li>
<li id="item_5">Item 1 3<ul></ul></li>
</ul>
</li>
<li id="q_6">Item 2<ul></ul></li>
<li id="a_7">Item 3<ul></ul></li>
<li id="g_8">Item 4<ul></ul></li>
</ul>
</div>
<button id="fred">Click</button>
$("#fred").click(function () {
//var result = $('#example5 ul').sortable('toArray');
var result = $('#example5 ul').sortable('serialize'); //Neither work
alert(result);
});
As far as I can tell there's no default way of serializing nested sortable lists in jquery UI.
The best way to do it is to go through every child of "#example5 ul" and build your own structure (I would recommend JSON in this case) to be send to the server.

Categories