Display JSON data on a page as a expandable/collapsible list
Solution 1
a good-looking, compact, collapsible tree view
pgrabovets' json-view
is amazingly clean and well designed.
Check out the demo
Solution 2
If you can consider using JS libraries , consider using JSON Formatter or Render JSON. Both these libraries offer configuration options like themes, maximum depth and sorting. To display simple JSON string in a collapsible form using Render JSON, you can use
<script>
document.getElementById("test").appendChild(
renderjson({ hello: [1,2,3,4], there: { a:1, b:2, c:["hello", null] } })
);
</script>
Solution 3
Some of the links to the questions are no longer accessible. I assume you are looking for how to make a collapsible JSON view.
TL;DR
you can jump to Full code.
the code is very short (200 lines↓, including JSDoc, comment, test code.)
Inspire you on how to solve the problem.
This question in some skill is very much like how to make the table of contents. (TOC)
-
First of all, JSON data is like an object. All we need to do is to add some more attributes (key, depth, children, ...) for each item.
-
Once these actions are done, all left is to render, and here is the pseudo-code for rendering.
render(node) { const divFlag = document.createRange().createContextualFragment(`<div style="margin-left:${node.depth * 18}px"></div>`) const divElem = divFlag.querySelector("div") const spanFlag = document.createRange().createContextualFragment( `<span class="ms-2">${node.key} : ${node.value}</span>` ) node.children.forEach(subNode => { const subElem = render(subNode) spanFlag.append(subElem) }) divElem.append(spanFlag) return divElem }
Full code
both CSS is not necessary.
- bootstrap: ms-2 (margin start)
- fontawesome: fa-caret-right, fa-caret-down if you don't want to use it, you can use ▸(25B8)▾(25BE) as the before contents
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossOrigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ=="
crossOrigin="anonymous" referrerpolicy="no-referrer"/>
<script type="module">
// 👇 main script {Node, Tree, JsonView}
class Node {
/**
* @description Add more attributes to the item.
* @param {*} item
* @param {*} key
* @param {Node} parent
* */
constructor(item, key, parent) {
this.key = key
/** @param {string} */
this.type = Array.isArray(item) ? "array" : typeof item
/** @param {Number} */
this.depth = parent ? parent.depth + 1 : 0
this.value = item
this.parent = parent
/** @param {[Node]} */
this.children = []
}
}
class Tree {
/**
* @description Given the root node, it will complete the children of it.
* @param {Node} rootNode
*/
constructor(rootNode) {
this.root = rootNode
const obj = this.root.value
if (!(obj instanceof Object)) { // Array is an Object too.
return
}
Object.keys(obj).forEach(keyOrIdx => {
const value = obj[keyOrIdx]
const subNode = new Node(value, keyOrIdx, rootNode)
const subTree = new Tree(subNode)
rootNode.children.push(subTree.root)
})
}
/**
* @param {string | Object} jsonData
* @return {Tree}
*/
static CreateTree(jsonData) {
jsonData = typeof jsonData === "string" ? JSON.parse(jsonData) : jsonData
const rootNode = new Node(jsonData, "root", null)
return new Tree(rootNode)
}
}
class JsonView {
static DefaultColorMap = {
text: {
string: "green",
number: "#f9ae58",
boolean: "#ca4ff8",
array: "black",
object: "black",
},
bg: {
object: "none"
// ... You can add more by yourself. They are like the text as above.
}
}
static NewConfig() {
return JSON.parse(JSON.stringify(JsonView.DefaultColorMap))
}
static SEPARATOR = " : "
/** @type {Tree} */
#tree
/**
* @param {Tree} tree
* */
constructor(tree) {
this.#tree = tree
}
/**
* @param {Node} node
* @param {Object} colorMap
*/
#render(node, colorMap = JsonView.DefaultColorMap) {
/**
* @param {Node} node
* */
const getValue = (node) => {
const typeName = node.type
switch (typeName) {
case "object":
return `object {${Object.keys(node.value).length}}`
case "array":
return `array [${Object.keys(node.value).length}]`
default:
return node.value
}
}
const arrowIcon = ["object", "array"].includes(node.type) ? `<i class="fas fa-caret-down"></i>` : ""
const divFlag = document.createRange().createContextualFragment(`<div style="margin-left:${node.depth * 18}px">${arrowIcon}</div>`)
const divElem = divFlag.querySelector("div")
const textColor = colorMap.text[node.type] !== undefined ? `color:${colorMap.text[node.type]}` : ""
const bgColor = colorMap.bg[node.type] !== undefined ? `background-color:${colorMap.bg[node.type]}` : ""
const valueStyle = (textColor + bgColor).length > 0 ? `style=${[textColor, bgColor].join(";")}` : ""
const keyName = node.depth !== 0 ? node.key + JsonView.SEPARATOR : "" // depth = 0 its key is "root" which is created by the system, so ignore it.
const spanFlag = document.createRange().createContextualFragment(
`<span class="ms-2">${keyName}<span ${valueStyle}>${getValue(node)}</span></span>`
)
const isCollapsible = ["object", "array"].includes(node.type)
node.children.forEach(subNode => {
const subElem = this.#render(subNode, colorMap)
if (isCollapsible) {
divFlag.querySelector(`i`).addEventListener("click", (e) => {
e.stopPropagation()
subElem.dataset.toggle = subElem.dataset.toggle === undefined ? "none" :
subElem.dataset.toggle === "none" ? "" : "none"
e.target.className = subElem.dataset.toggle === "none" ? "fas fa-caret-right" : "fas fa-caret-down" // Change the icon to → or ↓
subElem.querySelectorAll(`*`).forEach(e => e.style.display = subElem.dataset.toggle)
})
}
spanFlag.append(subElem)
})
divElem.append(spanFlag)
return divElem
}
/**
* @param {Element} targetElem
* @param {?Object} colorMap
*/
render(targetElem, colorMap = JsonView.DefaultColorMap) {
targetElem.append(this.#render(this.#tree.root, colorMap))
}
}
// 👇 Below is Test
function main(outputElem) {
const testObj = {
db: {
port: 1234,
name: "My db",
tables: [
{id: 1, name: "table 1"},
{id: 2, name: "table 2"},
],
},
options: {
debug: false,
ui: true,
},
person: [
"Foo",
"Bar"
]
}
const tree = Tree.CreateTree(testObj)
const jsonView = new JsonView(tree)
jsonView.render(outputElem)
/* If you want to set the color by yourself, you can try as below
const config = JsonView.NewConfig()
config.bg.object = "red"
jsonView.render(outputElem, config)
*/
}
(() => {
window.onload = () => {
main(document.body)
}
})()
</script>
vanilla JavaScript
Admin
Updated on July 16, 2022Comments
-
Admin almost 2 years
I need help with with displaying JSON data on a page like expandable/collapsible list.
Here is a valid JSON I`ve made converting from XML with Python:
And to display it I`m usig this:
<!DOCTYPE HTML> <head> <title>JSON Tree View</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script> <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js" type="text/javascript"></script> </head> <script> function json_tree(object){ var json="<ul>"; for(prop in object){ var value = object[prop]; switch (typeof(value)){ case "object": var token = Math.random().toString(36).substr(2,16); json += "<li><a class='label' href='#"+token+"' data-toggle='collapse'>"+prop+"="+value+"</a><div id='"+token+"' class='collapse'>"+json_tree(value)+"</div></li>"; break; default: json += "<li>"+prop+"="+value+"</li>"; } } return json+"</ul>"; } </script> <body style="margin: 40px;"> <h3>Paste JSON Into The Textarea Below and Click 'Build Tree'</h3> <textarea id="json" style="width: 100%;min-height:300px;"> </textarea> <button onclick="$('#output').html(json_tree(JSON.parse($('#json').val())));">Build Tree</button> <div id="output"> </div> </body> </html>
This is what I get:
I need help "filltering" (or merge with upper node) those "0" and "1", and also - how to show only values of the attributes without the names (or if do you have some better idea how can I dusplay this list)?
-
Yash Nasery over 2 yearsthe render json one works beautifully! thanks for this answer!!. for anyone looking for the script tag to add the script directly from github : <script src="cdn.rawgit.com/caldwell/renderjson/master/…>
-
woto over 2 yearsCool, thanks for sharing stuff. Little addition. When a value is null it throwing exception. So it may be added after
switch (typeName)
-if(node.value) ...