I would like to generate an HTML tree (preferably UL-LI) from the JSON example below. Does anyone have a simple, recursive JS function (not a framework) that can handle this specific structure? Thanks for your help!
{ "folder" : [ {
"title" : "1",
"folder" : [ {
"title" : "1.1",
"folder" : [ {
"title" : "1.1.1",
} , {
"title" : "1.1.2",
} ]
} ]
} , {
"title" : "2",
} ] }
function to_ul (obj) {
// --------v create an <ul> element
var f, li, ul = document.createElement ("ul");
// --v loop through its children
for (f = 0; f < obj.folder.length; f++) {
li = document.createElement ("li");
li.appendChild (document.createTextNode (obj.folder[f].title));
// if the child has a 'folder' prop on its own, call me again
if (obj.folder[f].folder) {
li.appendChild (to_ul (obj.folder[f].folder));
}
ul.appendChild (li);
}
return ul;
}
Caveat: No error checking! If a 'title' or 'folder' is missing, the whole thing could blow up.
I had a problem getting my browser to accept the data structure submitted by the OP, but here is a fully working example I've drawn up for my own, similar purposes. Beside the function I provide the data structure as well, with name/branches instead of title/folder.
function to_ul(branches) {
var ul = document.createElement("ul");
for (var i = 0, n = branches.length; i < n; i++) {
var branch = branches[i];
var li = document.createElement("li");
var text = document.createTextNode(branch.name);
li.appendChild(text);
if (branch.branches) {
li.appendChild(to_ul(branch.branches));
}
ul.appendChild(li);
}
return ul;
}
function renderTree() {
var treeEl = document.getElementById("tree");
var treeObj = {
"root": [{
"name": "George & Sarah Trede",
"branches": [{
"name": "George & Frances Trede",
"branches": [{
"name": "Mary (Trede) Jempty"
}, {
"name": "Carol (Trede) Moeller"
}]
}, {
"name": "Mary (Trede) Sheehan"
}, {
"name": "Ward Trede"
}]
}]
};
treeEl.appendChild(to_ul(treeObj.root));
}
renderTree()
<div id="tree"></div>
I've used PURE with some success in the past for this kind of thing.
function to_li(obj, name) {
var li = document.createElement("li");
if (typeof(name) != "undefined") {
var strong = document.createElement("strong");
strong.appendChild(document.createTextNode(name + ": "));
li.appendChild(strong);
}
if (typeof(obj) != "object"){
li.appendChild(document.createTextNode(obj));
} else {
var ul = document.createElement ("ul");
for (var prop in obj){
ul.appendChild(to_li(obj[prop],prop));
}
li.appendChild(ul);
}
return li;
}
Check out the jquery plugin JSON2HTML, it's a little simpler to use than PURE and I've used it on a couple of site's I've created.
http://json2html.com
#Boldewyn: I believe you can also use a For...In loop instead of a regular For loop to shorten the code a bit. Of course, I don't have much experience using this kind of loop, so please check my code snippet.
for (var i in obj.folder) {
li = document.createElement ("li");
li.appendChild (document.createTextNode (i.title));
// if the child has a 'folder' prop on its own, call me again
if (i.folder) {
li.appendChild (to_ul (i.folder));
}
}
I have made a pip package for the same do check it out JSON2tree. Install it using pip install json2tree then use cli tool to create HTML tree.
json2tree -j example.json -o output.html
Related
Got some JSON that I need to go through to fetch some IDs.
The JSON looks like this:
var carousel = [
{
"acf": {
"content": [
{
"acf_fc_layout": "custom",
"content": "Some text"
},
{
"acf_fc_layout": "exhibition",
"exhibition": 2594
},
{
"acf_fc_layout": "exhibition",
"exhibition": 1234
}
]
},
}
]
For every content where acf_fc_layout == exhibition I must fetch the value (ID) of exhibition so it can be used to fetch further data. As you can see there's multiple exhibition IDs aswell.
My confusion here is that there are both object and array, and that they're nested. I've done some similar stuff with jQuery, but that's out of the question this time. Don't think I need IE8 support, but still find this tricky..
If your JSON simply looks as you say, this is a simple solution:
var i;
for (i = 0; i < carousel[0].acf.content.length; i++) {
if (carousel[0].acf.content[i].acf_fc_layout === "exhibition") {
// do something with carousel[0].acf.content[i].exhibition
}
}
Alternatively if you have much more content in your JSON, this might be relevant:
var i, j;
for (i = 0; i < carousel.length; i++) {
if (typeof carousel[i].acf != 'undefined' && typeof carousel[i].acf.content != 'undefined') {
for (j = 0; j < carousel[i].acf.content.length; j++) {
if (carousel[i].acf.content[j].acf_fc_layout === "exhibition") {
// do something with carousel[i].acf.content[j].exhibition
}
}
}
}
carousel[0].acf.content.forEach(function (item) {
if (item["acf_fc_layout"] === "exhibition") {
// do some stuff
// id for exhibition placed in item["exhibition"]
}
});
With your current structure, you need to use a foreach and check the value.
var carousel = [
{
"acf": {
"content": [
{
"acf_fc_layout": "custom",
"content": "Some text"
},
{
"acf_fc_layout": "exhibition",
"exhibition": 2594
},
{
"acf_fc_layout": "exhibition",
"exhibition": 1234
}
]
},
}
];
$.each(carousel[0].acf.content,function (i,v){
if(v.acf_fc_layout == "exhibition")
$(".result").append(v.exhibition+"<br>");
});
JSFIddle
http://jsfiddle.net/oucp3v5x/
$(carousel).each(function(i, el){
$(el.acf.content).each(function(i, el){
if(el.acf_fc_layout === 'exhibition') {
$('<div>', {
text: el.exhibition
}).appendTo($('#results'));
}
});
});
If acf have many content, you need to run additional loop and acf have to be array of objects.
I have a JSON File with Tasks. They are structured as follows:
[
{
"id":"1",
"name":"task1",
"tags":["tag1","tag2","tag3"]
},
{
"id":"2",
"name":"task2",
"tags":["tag4","tag2","tag5"]
},
{
"id":"3",
"name":"task3",
"tags":["tag3"]
}
]
Here's a Plunkr for what I want
http://plnkr.co/edit/kMhiHDyybHYxGfV7W39z?p=preview
Basically, this is what I want : http://jsfiddle.net/TahmidTanzim/N9Vqk/
I've looked through various SO questions and they all suggest I am doing it right.
However I don't understand where I am going wrong. I want only those tasks that contain the selectedTag to be displayed in the red list.
Thanks!
Just change filters to compare inner tags using arrays.
myapp.filter('tagFilter',function()
{
return function(Data,selectedTags)
{
if(selectedTags.length===0) return Data;
var tempData=[];
for(var i in Data) {
for(var z in Data[i].tags) {
for(var k in selectedTags) {
var value = selectedTags[k];
if(value == Data[i].tags[z]) {
tempData.push(Data[i]);
break;
}
}
}
}
return tempData;
}
});
I need help for fixing up the bug on my JavaScript program. This program perfectly works fine on Google chrome and firefox, but the code dont works with internet explorer 8, giving a strange error mentioned below. Please someone give me a solution for my program. The fiddle link is given below
Error is:
SCRIPT438: Object doesn't support property or method 'keys'
File: treelist.js, Line: 12, Column: 13
Fiddle
var dataSource = {
"Watch": {
"Titan": {},
"parent": {
"leaf1": {},
"leaf2": {}
},
}
},
traverseObject = function (obj) {
var ul = document.createElement("ul"),
li;
for (var prop in obj) {
li = document.createElement("li");
li.appendChild(document.createTextNode(prop));
li.onclick = function(e) {
var classNames = e.currentTarget.className;
if (classNames.indexOf("hidden") == -1) {
e.currentTarget.className += "hidden";
} else {
e.currentTarget.className = e.currentTarget.className.replace("hidden", "");
}
e.stopPropagation();
}
if (typeof obj[prop] == "object" && Object.keys(obj[prop]).length) {
li.appendChild(traverseObject(obj[prop]));
} else {
li.className += "leaf";
}
ul.appendChild(li);
console.log(ul);
}
return ul;
};
window.onload = function () {
document.getElementById("dvList1").appendChild(traverseObject(dataSource));
}
Thank you
The commas at the end of the members declaration are not supported in IE8.
Try to remove them, and it should work:
dataSource = {
"Watch": {
"Titan": {},
"parent": {
"leaf1": {},
"leaf2": {}
}, <-- remove this
}
Apart from this, you are refering to "Object.keys" which is not supported in IE8:
if (typeof obj[prop] == "object" && Object.keys(obj[prop]).length)
You can solve it by adding this:
if (!Object.keys) {
Object.keys = function(obj) {
var keys = [];
for (var i in obj) {
if (obj.hasOwnProperty(i)) {
keys.push(i);
}
}
return keys;
};
}
Schema is too complicated and a bit strange for interpretation of tree structures in JS.
IF you have chance to re design it to have something like this :
var dataSource = {
name : "Watch",
nodes : [
{
name : "Titan",
nodes: [],
},
{
name : "parent",
node : [
{
name : "leaf1",
nodes : []
},
{
name : "leaf2",
nodes : []
}
]
},
]
};
your code can be hugely reduced, and will work fine without annoying cross-browser stuff without hacks.
I'm trying to get some data into this format, for use with a templating system called mustache:
{
"repo": [
{ "name": "resque" },
{ "name": "hub" },
{ "name": "rip" },
]
}
and what I currently have is this:
for (childIndex in scenes[sceneID].children) {
childSceneID = scenes[sceneID].children[childIndex];
childScene = scenes[childSceneID];
}
So I somehow need to make each childScene the "name" in the "repo" object. Does anyone know how to do this? This is the mustache documentation:
http://mustache.github.com/mustache.5.html
Is this what you meant?:
var repo = [];
for (childIndex in scenes[sceneID].children) {
childSceneID = scenes[sceneID].children[childIndex];
childScene = scenes[childSceneID];
repo.push({"name": childScene});
}
var theobj = { "repo": repo };
I just put this here
var repo = new Object();
var table = new Object();
repo["repo"] = table;
table["name1"] = "resque";
table["name1"] = "hub";
table["name1"] = "rip";
I have a literal array that is loaded when the page loads... See below:
<script type="text/javascript">
var members = [
{
name:"Alan Lim",
id:"54700f06-a199-102c-8976-b1732b7ffc74",
positions:[
{
id:"4cdeb2a2-8897-102d-80ee-95364de284f0"
}
]
},
{
name:"Ben Sinclair",
id:"ed34b5a4-9b2f-102c-8475-9e610b13400a",
conflict:"true",
positions:[
{
id:"f00c2128-8895-102d-80ee-95364de284f0"
},
{
id:"f00c68ea-8895-102d-80ee-95364de284f0"
},
{
id:"4cde6824-8897-102d-80ee-95364de284f0"
},
{
id:"4cde9ea2-8897-102d-80ee-95364de284f0"
}
],
locations:[
{
id:"88fb5f94-aaa6-102c-a4fa-1f05bca0eec6"
},
{
id:"930555b0-a251-102c-a245-1559817ce81a"
}
]
},
{
name:"Debbie Wright",
id:"fa49307a-9cfb-102d-bd08-842c500d506d"
}
]
</script>
Is there anyway to edit the array without reloading the page? For example, I want to add conflict:"true" to Alan Lim...
E.g:
Change this:
{
name:"Alan Lim",
id:"54700f06-a199-102c-8976-b1732b7ffc74",
positions:[
{
id:"4cdeb2a2-8897-102d-80ee-95364de284f0"
}
]
},
To this:
{
name:"Alan Lim",
id:"54700f06-a199-102c-8976-b1732b7ffc74",
conflict:"true",
positions:[
{
id:"4cdeb2a2-8897-102d-80ee-95364de284f0"
}
]
},
Trust that makes sense :) The reason for this is because I use other JavaScript to pull information from this array. When I make a change with the other JavaScript I want to add and subtract to this array to reflect the changes...
You can loop through to find the member you want (by name it seems given the question) then edit it, like this:
for(var i=0; i<members.length; i++) {
if(members[i].name == "Alan Lim")
members[i].conflict = "true";
}
You can give it a try here, or make it a bit more generic like this:
function setProp(name, prop, value) {
for(var i=0; i<members.length; i++) {
if(members[i].name == name)
members[i][prop] = value;
}
}
Since your array is numerically indexed, Can you not just do:
members[0]['conflict'] = "true";