I am trying to turn the json data that is in the db variable into a nested list on the html page. Instead it is giving me a sort of ladder of each child node inserted into the child node before it. I can not figure out why it is not nesting correctly. What am i missing?
db = JSON.parse(`
{
"title": "Root",
"children": [
{
"title": "Child1",
"children": [
{"title": "Child1a"},
{"title": "Child1b"},
{"title": "Child1c"}
]
},
{
"title": "Child2",
"children": [
{"title": "Child2a"},
{"title": "Child2b"},
{"title": "Child2c"}
]
}
]
}
`);
function recurse(u, j) {
li = document.createElement('li');
ul = document.createElement('ul');
li.setAttribute('title', j.title);
li.appendChild(ul);
u.appendChild(li);
(j.children || []).forEach(c => {
recurse(ul, c);
});
}
recurse(document.getElementById('tree'), db);
* {
margin: 0;
}
li::before {
content: attr(title);
}
li:hover {
cursor: grab;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Scratch</title>
</head>
<body>
<ul id='tree'></ul>
</body>
</html>
You're referring to the same variables over and over because you're not re-declaring them in the function scope.
db = JSON.parse(`
{
"title": "Root",
"children": [
{
"title": "Child1",
"children": [
{"title": "Child1a"},
{"title": "Child1b"},
{"title": "Child1c"}
]
},
{
"title": "Child2",
"children": [
{"title": "Child2a"},
{"title": "Child2b"},
{"title": "Child2c"}
]
}
]
}
`);
function recurse(u, j) {
var li = document.createElement('li');
var ul = document.createElement('ul');
li.setAttribute('title', j.title);
li.appendChild(ul);
u.appendChild(li);
(j.children || []).forEach(c => {
recurse(ul, c);
});
}
recurse(document.getElementById('tree'), db);
* {
margin: 0;
}
li::before {
content: attr(title);
}
li:hover {
cursor: grab;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Scratch</title>
</head>
<body>
<ul id='tree'></ul>
</body>
</html>
Related
I have this code in JS. Please tell me how i can increment a piece of this code so that articles are automatically inserted into HTML.
It is necessary that the data index [0] is incremented for example data [0], data [1], data [2] and beyond. title, author, content vary depending on the index number.Thanks!
document.querySelector(".title").textContent = data[1]["title"];
document.querySelector(".author").textContent = data[1]["author"]["username"];
document.querySelector(".content").textContent = data[1]["content"];
fetch("https://any-site/articles")
.then(function(resp) {
return resp.json();
})
.then(function(data) {
console.log(data);
document.querySelector(".title").textContent = data[1]["title"];
document.querySelector(".author").textContent = data[1]["author"]["username"];
document.querySelector(".content").textContent = data[1]["content"];
})
.catch(function() {
//catch any errors
});
// ---------- JSON on SITE ----------
[
{
"id": 1,
"title": "Some title",
"content": "Some content",
"author": {
"id": 1,
"username": "Leo",
},
},
{
"id": 2,
"title": "Some title2",
"content": "Some content2",
"author": {
"id": 2,
"username": "Vernon",
},
},
{
"id": 3,
"title": "Some title3",
"content": "Some content3",
"author": {
"id": 2,
"username": "Vernon",
},
},
]
<body>
<h1>News</h1>
<h2 class="title"></h2>
<h5 class="author"></h5>
<p class="content"></p>
</body>
<script src="data.js"></script>
The easiest method I've found is to map over the data and produce some HTML using a template literal, and then insert that into the document body with insertAdjacentHTML.
const data = [{"id":1,"title":"Some title","content":"Some content","author":{"id":1,"username":"Leo"}},{"id":2,"title":"Some title2","content":"Some content2","author":{"id":2,"username":"Vernon"}},{"id":3,"title":"Some title3","content":"Some content3","author":{"id":2,"username":"Vernon"}}];
function getHTML(data) {
// Iterate over the array of objects
return data.map((block) => {
// Destructure the title, author username, and content
// from the object
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const { title, author: { username }, content } = block;
// For each object return those variables
// in a template literal
return `
<div class="block">
<p class="title">${title}</p>
<p class="author">${username}</p>
<p class="content">${content}</p>
</div>
`;
// `map` returns an array of information so make
// you join it up into one string of data
}).join('');
}
// Call the `getHTML` function with the data and insert
// it on the element you want
document.body.insertAdjacentHTML('beforeend', getHTML(data));
.block {
border: 1px solid #454545;
margin: 0.5em 0 0.5em 0;
padding: 0.5em;
}
.title { text-decoration: underline; }
.author { color: blue; }
<body>
<h1>News</h1>
</body>
I'm ultimately looking to create a HTML file that will accomplish the following tasks. The point is, I need it to be all inclusive in an HTML snippet/file.
Make a GET api call to a backend CMS (ie: Drupal). This api call returns the following JSON data for a specific content item:
{
"jsonapi": {
"version": "1.0",
"meta": {
"links": {
"self": {
"href": "http://jsonapi.org/format/1.0/"
}
}
}
},
"data": {
"type": "node--espot_html",
"id": "fbaab7dd-09c2-4aa0-852d-4b9462074a45",
"attributes": {
"drupal_internal__nid": 1,
"drupal_internal__vid": 11,
"langcode": "en",
"revision_timestamp": "2019-10-31T13:29:29+00:00",
"revision_log": null,
"status": true,
"title": "Razor Hero Image",
"created": "2019-10-28T16:24:20+00:00",
"changed": "2019-10-31T13:29:29+00:00",
"promote": true,
"sticky": false,
"default_langcode": true,
"revision_translation_affected": true,
"path": {
"alias": "/razor-hero",
"pid": 1,
"langcode": "en"
},
"body": {
"value": "<div class=\"jns-hero-image jns-hero-image-message-left\" id=\"hero_slideshow\"><img alt=\"Clean Shave\" data-entity-type=\"file\" data-entity-uuid=\"fbc875d0-ef17-4813-981c-22a29e42c44f\" src=\"/sites/default/files/inline-images/dont-shave-2.jpg\" />\r\n<div class=\"grid-container\">\r\n<div class=\"grid-x grid-padding-x\">\r\n<div class=\"small-12 medium-8 cell hero-message\">\r\n<div>\r\n<h1 class=\"text-hero text-hero-left\">Get a Much Better<br />\r\nShave</h1>\r\n\r\n<p>Fewer things look and feel better than a clean shave!</p>\r\n<a>SHOP NOW</a></div>\r\n</div>\r\n</div>\r\n</div>\r\n</div>\r\n",
"format": "full_html",
"processed": "<div class=\"jns-hero-image jns-hero-image-message-left\" id=\"hero_slideshow\"><img alt=\"Clean Shave\" data-entity-type=\"file\" data-entity-uuid=\"fbc875d0-ef17-4813-981c-22a29e42c44f\" src=\"/sites/default/files/inline-images/dont-shave-2.jpg\" /><div class=\"grid-container\">\n<div class=\"grid-x grid-padding-x\">\n<div class=\"small-12 medium-8 cell hero-message\">\n<div>\n<h1 class=\"text-hero text-hero-left\">Get a Much Better<br />\nShave</h1>\n\n<p>Fewer things look and feel better than a clean shave!</p>\n<a>SHOP NOW</a></div>\n</div>\n</div>\n</div>\n</div>\n",
"summary": ""
}
},
"relationships": {
"node_type": {
"data": {
"type": "node_type--node_type",
"id": "837a5cbe-f8fe-4c03-a613-2092dff1168e"
},
"links": {
"self": {
"href": "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45/relationships/node_type?resourceVersion=id%3A11"
},
"related": {
"href": "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45/node_type?resourceVersion=id%3A11"
}
}
},
"revision_uid": {
"data": {
"type": "user--user",
"id": "c0d80edb-325a-4ad7-9be3-bc9dc32ed878"
},
"links": {
"self": {
"href": "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45/relationships/revision_uid?resourceVersion=id%3A11"
},
"related": {
"href": "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45/revision_uid?resourceVersion=id%3A11"
}
}
},
"uid": {
"data": {
"type": "user--user",
"id": "c0d80edb-325a-4ad7-9be3-bc9dc32ed878"
},
"links": {
"self": {
"href": "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45/relationships/uid?resourceVersion=id%3A11"
},
"related": {
"href": "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45/uid?resourceVersion=id%3A11"
}
}
}
},
"links": {
"self": {
"href": "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45?resourceVersion=id%3A11"
}
}
},
"links": {
"self": {
"href": "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45"
}
}
}
I'm then looking for the JSON response to be parsed to extract the only the data under the "value": object.
I need to have the escape characters removed
The end result should be that the HTML (shown below) is rendered in the browser:
<div class="jns-hero-image jns-hero-image-message-left" id="hero_slideshow"><img alt="Clean Shave" data-entity-type="file" data-entity-uuid="fbc875d0-ef17-4813-981c-22a29e42c44f" src="/sites/default/files/inline-images/dont-shave-2.jpg" /><div class="grid-container"><div class="grid-x grid-padding-x"><div class="small-12 medium-8 cell hero-message"><div><h1 class="text-hero text-hero-left">Get a Much Better<br />Shave</h1><p>Fewer things look and feel better than a clean shave!</p><a>SHOP NOW</a></div></div></div></div></div>"
If processed successfully, this is what should display on the webpage:
Screenshot if image rendered via HTML
Here is the HTML I have tried. My apologies for breaking etiquette, I was not aware this would be perceived as such. In my browsers console it tells me the forEach is not a valid function or this response.
'<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ghibli App</title>
<link href="https://fonts.googleapis.com/css?family=Dosis:400,700" rel="stylesheet" />
<!-- <link href="style.css" rel="stylesheet" /> -->
</head>
<body>
<div id="root"></div>
<script>
const app = document.getElementById('root');
const container = document.createElement('div');
container.setAttribute('class', 'container');
app.appendChild(logo);
app.appendChild(container);
var request = new XMLHttpRequest();
request.open('GET', 'http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45', true);
request.onload = function () {
// Begin accessing JSON data here
var data = JSON.parse(this.response);
if (request.status >= 200 && request.status < 400) {
data.forEach(value => {
const card = document.createElement('div');
card.setAttribute('class', 'card');
const h1 = document.createElement('h1');
h1.textContent = valuable.title;
const p = document.createElement('p');
value.description = value.description.substring(0, 300);
p.textContent = `${value.description}...`;
container.appendChild(card);
card.appendChild(h1);
card.appendChild(p);
});
} else {
const errorMessage = document.createElement('marquee');
errorMessage.textContent = `Gah, it's not working!`;
app.appendChild(errorMessage);
}
}
request.send();
</script>
</body>
</html>'
This ended up producing the end result that I was looking to produce:
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
<script>
const app = document.getElementById('root');
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
let obj = JSON.parse(this.responseText);
console.log(obj.data.attributes.body.value)
app.innerHTML = obj.data.attributes.body.value.replace('src="/sites','src="http://localhost:4700/sites');
}
};
xmlhttp.open("GET", "http://localhost:4700/jsonapi/node/espot_html/fbaab7dd-09c2-4aa0-852d-4b9462074a45", true);
xmlhttp.send();
</script>
</body>
</html>
Our club needs to type members in an input field and have the parent li background color change based on the name lookup in a supporting json file. I would prefer a jquery solution, but javascript is OK!
All is included in the link https://jsfiddle.net/24n2on57/7/
HTML:
<ul id="sortable">
<li id="L01"><input type="text" id="I01"></li>
<li id="L02"><input type="text" id="I02"></li>
<li id="L03"><input type="text" id="I03"></li>
<li id="L04"><input type="text" id="I04"></li>
</ul>
JS:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js">
</script>
<script>
var standing = [{
"code": "A",
"background": "#AEF3D4"
},
{
"code": "B",
"background": "#6DDCEA"
},
{
"code": "C",
"background": "#9CC7CC"
},
{
"code": "D",
"background": "#B37F77"
}
];
</script>
<script>
var members = [{
"Class": "A",
"Name": "Bob"
},
{
"Class": "C",
"Name": "James"
},
{
"Class": "D",
"Name": "Thomas"
},
{
"Class": "B",
"Name": "Anthony"
}
]
</script>
<script>
// Lookup background color
function getBackground(name) {
var i = null;
for (i = 0; members.length > i; i++) {
if (members[i].Name === name) {
return standing[i].background;
$(this).css('background-color', standing[i].background);
}
}
return;
};
$("#I01").on("blur", function() {
$("#L01").val(getBackground($(this).val()));
})
$("#I02").on("blur", function() {
$("#L02").val(getBackground($(this).val()));
})
$("#I03").on("blur", function() {
$("#L03").val(getBackground($(this).val()));
})
$("#I04").on("blur", function() {
$("#L04").val(getBackground($(this).val()));
})
</script>
You have to set css instead of val. Also, you had multiple unnecessary style tags in your jsfiddle. I removed them and added the working code here.
For the first list element I added styling using javascript and for the others I used jQuery in-order to show you how to do it in both ways.
var standing = [{
"code": "A",
"background": "#AEF3D4"
},
{
"code": "B",
"background": "#6DDCEA"
},
{
"code": "C",
"background": "#9CC7CC"
},
{
"code": "D",
"background": "#B37F77"
}
];
var members = [{
"Class": "A",
"Name": "Bob"
},
{
"Class": "C",
"Name": "James"
},
{
"Class": "D",
"Name": "Thomas"
},
{
"Class": "B",
"Name": "Anthony"
}
]
$(this).css('background-color', 'red');
function getBackground(name) {
var i = null;
for (i = 0; members.length > i; i++) {
if (members[i].Name === name) {
return standing[i].background;
//$(this).css('background-color', standing[i].background); // Don't put any code after 'return' statement
}
}
return;
}
$("#I01").on("blur", function() {
document.getElementById("L01").style.backgroundColor = getBackground($(this).val());
});
$("#I02").on("blur", function() {
$("#L02").css({"background-color":getBackground($(this).val())});
});
$("#I03").on("blur", function() {
$("#L03").css({"background-color":getBackground($(this).val())});
});
$("#I04").on("blur", function() {
$("#L04").css({"background-color":getBackground($(this).val())});
});
#myDiv,
#intro {
display: table;
width: 30rem;
margin: 2rem auto
}
li {
background: lightgreen;
margin: 1rem;
height: 3rem;
line-height: 3rem;
list-style-type: none;
}
input {
background: #fff;
height: 2rem;
line-height: 2rem;
font-size: 1.5rem;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="grid-container" style="margin-top:4rem">
<div id="intro">
The color of the list items (default background = "lightgreen") is to be based on the lookup members.Name and return of the standing.background. Valid names are "Bob", "James", "Thomas", and "Anthony". User types in a name, presses tab (onblur) and the
list item background changes to standing.background.
</div>
<div id="myDiv" class="grid-item">
<ul id="sortable">
<li id="L01"><input type="text" id="I01"></li>
<li id="L02"><input type="text" id="I02"></li>
<li id="L03"><input type="text" id="I03"></li>
<li id="L04"><input type="text" id="I04"></li>
</ul> </div>
</div>
Here is the solution for your problem.
var standing = [{
"code": "A",
"background": "#AEF3D4"
},
{
"code": "B",
"background": "#6DDCEA"
},
{
"code": "C",
"background": "#9CC7CC"
},
{
"code": "D",
"background": "#B37F77"
}
];
var members = [{
"Class": "A",
"Name": "Bob"
},
{
"Class": "C",
"Name": "James"
},
{
"Class": "D",
"Name": "Thomas"
},
{
"Class": "B",
"Name": "Anthony"
}
]
$(this).css('background-color', 'red');
function getBackground(name) {
var i = null;
for (i = 0; members.length > i; i++) {
if (members[i].Name === name) {
return standing[i].background;
$(this).css('background-color', standing[i].background);
}
}
return;
};
$('input').on('input', function() {
var input = $(this).val();
$(this).parent().css('background-color', searchMembers(input));
});
function searchMembers(name){
var className = '';
for (var i=0; i < members.length; i++) {
if (members[i].Name === name) {
return searchStanding(members[i].Class);
}
}
}
function searchStanding(className){
for (var i=0; i < standing.length; i++) {
if (standing[i].code === className) {
return standing[i].background;
}
}
}
#myDiv,
#intro {
display: table;
width: 30rem;
margin: 2rem auto
}
li {
background: lightgreen;
margin: 1rem;
height: 3rem;
line-height: 3rem;
list-style-type: none;
}
input {
background: #fff;
height: 2rem;
line-height: 2rem;
font-size: 1.5rem;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Change BG color of list item based on json lookup</title>
</head>
<body>
<div class="container">
<div class="grid-container" style="margin-top:4rem">
<div id="intro">
The color of the list items (default background = "lightgreen") is to be based on the lookup members.Name and return of the standing.background. Valid names are "Bob", "James", "Thomas", and "Anthony". User types in a name, presses tab (onblur) and the
list item background changes to standing.background.
</div>
<div id="myDiv" class="grid-item">
<ul id="sortable">
<li id="L01"><input type="text" id="I01"></li>
<li id="L02"><input type="text" id="I02"></li>
<li id="L03"><input type="text" id="I03"></li>
<li id="L04"><input type="text" id="I04"></li>
</ul </div>
</div>
</body>
</html>
I just changed the JQuery part. All You need is changing the li back ground color depending on name.
var standing = [{
"code": "A",
"background": "#AEF3D4"
},
{
"code": "B",
"background": "#6DDCEA"
},
{
"code": "C",
"background": "#9CC7CC"
},
{
"code": "D",
"background": "#B37F77"
}
];
var members = [{
"Class": "A",
"Name": "Bob"
},
{
"Class": "C",
"Name": "James"
},
{
"Class": "D",
"Name": "Thomas"
},
{
"Class": "B",
"Name": "Anthony"
}
]
function getBackground(name,selector) {
var i = null;
for (i = 0; members.length > i; i++) {
if (members[i].Name == name) {
for (k = 0; standing.length > k; k++) {
if (members[i].Class == standing[k].code) {
$(selector).parent().css('background-color', standing[k].background);
}
}
}
}
return;
};
$("#I01").on("blur", function() {
getBackground($(this).val(),this);
})
$("#I02").on("blur", function() {
getBackground($(this).val(),this);
})
$("#I03").on("blur", function() {
getBackground($(this).val(),this);
})
$("#I04").on("blur", function() {
getBackground($(this).val(),this);
})
Also check the fiddle https://jsfiddle.net/24n2on57/39/
I have 2 jstree.
from 1st tree i am sending selectd nodes to 2nd tree.
on back button I am deleting entry of 2nd tree with its id but it doesnot works but it fetches all the selected records.
here is my code
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
<style>
html { margin:0; padding:0; font-size:62.5%; }
body { font-size:14px; font-size:1.4em; }
h1 { font-size:1.8em; }
.demo { overflow:auto; border:1px solid silver; min-height:100px;min-width: 400px;float: left }
.demo1 { overflow:auto; border:1px solid silver; min-height:100px;min-width: 400px; float: right}
</style>
<link rel="stylesheet" href="style.min.css" />
</head>
<body><div id="frmt" class="demo"></div>
<div id="frmt1" class="demo1"></div>
<script>
var arrayCollection = [ {"id": "animal", "parent": "#", "text": "Animals"}, {"id": "device", "parent": "#", "text": "Devices"}, {"id": "dog", "parent": "animal", "text": "Dogs"}, {"id": "lion", "parent": "animal", "text": "Lions"}, {"id": "mobile", "parent": "device", "text": "Mobile Phones"}, {"id": "lappy", "parent": "device", "text": "Laptops"}, {"id": "daburman", "parent": "dog", "text": "Dabur Man", "icon": "/"}] ;
</script>
<script>
function refreshJSTree() {
$('#frmt1').jstree(true).settings.core.data = arrayCollection;
$('#frmt1').jstree(true).refresh();
}
</script>
<script>
function deleteNode() {
console.log("Delete : "+$('#frmt1').jstree("get_checked"));
arrayCollection = arrayCollection.pop($('#frmt1').jstree("get_checked"));/*
.filter(function(el) {
return el.id !== $('#frmt1').jstree("get_checked");
});*/
refreshJSTree();
}
</script>
<script>
function callGraph() {
var akn=$('#frmt').jstree("get_checked");
var c = akn + "";
var spl = c.split("!");
var dt = "";
var cnt = spl.length - 1;
arrayCollection=[];
var temp=[];
console.log("SPL : "+spl);
for(q=0;q<cnt;q++)
{
if (spl[q].startsWith(","))
{
var xp=spl[q].split(",")[1];
if(xp.startsWith("OU") || spl[q].startsWith("DC"))
temp.push(xp);
}
else if(spl[q].startsWith("OU") || spl[q].startsWith("DC"))
{
temp.push(spl[q].split(",")[0]);
}
}
console.log("TEMP : "+temp);
/*for(q=0;q<cnt;q++)
{
var test=0;
for(m=0;m<temp.length;m++)
{
var textData=spl[q]+"";
console.log(temp[m]);
if(textData.Contains(temp[m]))
test=1;
}
if(test==0)
{
if (spl[q].startsWith(","))
arrayCollection.push(spl[q].split(",")[1]);
else
arrayCollection.push(spl[q].split(",")[0]);
}
}*/
for (q = 0; q < cnt; q++) {
if (spl[q].startsWith(","))
dt=/*spl[q].split(",")[1]//*/dt ={"id":spl[q].split(",")[1],"text":spl[q].split(",")[1]}
else
dt=/*spl[q].split(",")[0]//*/dt = {"id": spl[q].split(",")[0] ,"text": spl[q].split(",")[0] };
arrayCollection.push(dt);
}
refreshJSTree();
}
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="jstree.min.js"></script>
<button onclick="callGraph()">>></button>
<button onclick="deleteNode()"><<</button>
<script>
$('#html').jstree();
$('#frmt').jstree({
'core': {
'data':<%= session.getAttribute("PATH")%>
},
"checkbox": {
"whole_node": false,
"keep_selected_style": true,
"three_state": true,
"tie_selection": false
}, "search": {
"fuzzy": true
}, "plugins": ["checkbox", "search"]
});
$('#frmt1').jstree({
'core': {
'data':[{"text":"DC TEST"}]
},
"checkbox": {
"whole_node": false,
"keep_selected_style": true,
"three_state": true,
"tie_selection": false
}, "search": {
"fuzzy": true
}, "plugins": ["checkbox", "search"]
});
/*$('button').on('click', function () {
a = $('#frmt').jstree("get_checked");
callGraph(a);
// window.location = "read.jsp?name="+a;
});*/
</script>
</body>
</html>
How can I create a specific JSON object from some HTML?
Example
This is very well formatted HTML page (rendered from markdown). I want to create a JSON representation of the sections on the page.
So each "h2" is a title. Each h3, h4, or h5, that follows it is a subtitle
Given this HTML:
<h2>Charts</h2>
<ul>...</ul>
<h5>Third Party</h5>
<ul>...</ul>
<h5>Reusable Chart Frameworks</h5>
<ul>...</ul>
<h2>Maps</h2>
<h5><a href="#third-party-1">Third Party</h5>
...
Return this JSON:
[
{
"title": {
"text": "Charts",
"href": "#charts"
}
"subtitles": [
{
"text": "Third Party",
"href": "#third-party"
},
{
"text": "Reusable Chart Frameworks",
"href": "#reusable-chart-frameworks"
}
]
},
{
"title": {
"text": "Maps",
"href": "#maps"
},
"subtitles": ]
"text": "Third Party",
"href": "#third-party-1"
]
},
...
]
Solutions I've considered
Seems like something jQuery could help a lot with. If the items were nested it would be very easy to do $('h2').each(...) and just loop through each section, appending it to my JSON object. However there is no nesting here, just siblings. Any ideas?
One other solution is to map it:
var mappedJSON = $('h2').map(function() {
var $selfA = $(this).children('a');
var subtiles = $(this).nextUntil('h2').filter(':header').children('a').map(function() {
return {
"text": $(this).text(),
"href": $(this).attr('href')
}
}).get();
return {
"title": {
"text": $selfA.text(),
"href": $selfA.attr('href')
},
"subtitles": subtiles
};
}).get();
console.log(mappedJSON);
$('<pre/>').appendTo($('body').empty()).text(JSON.stringify(mappedJSON, null, "\t"));
pre {
tab-size: 2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<h2>Charts</h2>
<ul>...</ul>
<h5>Third Party</h5>
<ul>...</ul>
<h5>Reusable Chart Frameworks</h5>
<ul>...</ul>
<h2>Maps</h2>
<h5><a href="#third-party-1">Third Party</h5>
Here's a solution that just relies on jQuery's .nextUntil() function.
var sections = [];
var eTitles = $('article').find('h2');
$(eTitles).each(function(){
var section = {
"title": {
"text": $(this).text(),
"href": $(this).find('a').attr('href')
},
"subtitles": []
}
var eSubtitles = $(this).nextUntil('h2').filter('h3, h4, h5');
$(eSubtitles).each(function(){
var subtitle = {
"text": $(this).text(),
"href": $(this).find('a').attr('href')
}
section.subtitles.push(subtitle);
});
sections.push(section);
});