Reputation: 1439
I've seen several questions about converting an array/object into a nested list, but I've found only one relevant question to my issue. I've tried a few ways of accessing children of an element, but it only breaks my code further.
I have a nested, unordered list.
<div id="sortableSitemap">
<ul class="sortable ui-sortable">
<li id="world_news_now"><div>World</div>
<ul>
<li id="the_news"><div>The News</div></li>
</ul>
</li>
<li id="sports_news_cool"><div>Sports</div></li>
</ul>
</div>
At the moment, it contains 3 items and appears like so:
-World
--The News
-Sports
There can be any number of nodes with varying depths. I'm trying to store the list into an array with some additional information.
Each node gets a numeric, sequential ID (first node is 1, second is 2) based on the order it appears, regardless of depth (i.e. World = 1, The News = 2, Sports = 3). I also want to store the ID of a node's parent (root is 0). So, the parent IDs would be: World = 0, The News = 1, Sports = 0.
The code below seems to work except when the list is like the one above. In that case, it assigns The News = 3 and its parent = 2 (Sports). For some reason, iterating through the items (children) ends up getting to the <ul>
last, even if it's directly after an open <li>
.
All but one jQuery solution I found ignores depth, and even then, I need the actual parent node's ID (which I currently keep in a stack based on whether I've gone down a level).
Why is this happening, and how can I modify my code to go through the list recursively?
var count = 0;
var pages = [];
var parentStack = [];
function createNewLevel(items) {
var length = items.length;
for (var i = 0; i < length; i++) {
if (items[i].tagName == 'UL') {
parentStack.push(count);
createNewLevel($(items[i]).children().get());
parentStack.pop();
} else {
++count;
pages.push({
pId: parentStack[parentStack.length - 1],
urlStr: $(items[i]).attr('id'), myId: count
});
}
}
}
createNewLevel($('#sortableSitemap ul').get());
console.log(pages);
Update: Here's a jsFiddle to show how the code does not work ("The News" should have "World" as its parent node).
Upvotes: 1
Views: 3286
Reputation: 36
I've modified your original code. This should work for all combinations of nested lists. Instead of using children().get()
, I used the native JS child methods. It will traverse everything in the list, but ignore elements unless they are <li>
or <ul>
. Good luck.
var count = 0;
var pages = [];
var parentStack = [];
var result = {};
parentStack.push(0);
function createNewLevel(obj) {
var obj = obj || document.getElementById('sortableSitemap');
if (obj.tagName == 'LI') {
++count;
pages.push({
pId: parentStack[parentStack.length - 1],
urlStr: obj.id, myId: count
});
}
if (obj.hasChildNodes()) {
var child = obj.firstChild;
while (child) {
if (child.nodeType === 1) {
if (child.tagName == 'UL') {
parentStack.push(count);
}
createNewLevel(child);
if (child.tagName == 'UL') {
parentStack.pop();
}
}
child = child.nextSibling;
}
}
}
createNewLevel();
Upvotes: 1