Free Web Design Code & Scripts

JavaScript JSON Formatter with Tree View

Code Snippet:JSON Formatter with Tree View
Author: dede brahma arianto
Published: 5 months ago
Last Updated: 5 months ago
Downloads: 191
License: MIT
Edit Code online: View on CodePen
Read More

This JavaScript code snippet helps you to create a JSON Formatter with Tree View. It it incredibly useful for developers who need to visualize JSON data in a readable, hierarchical format. The tree view structure allows for easy navigation and understanding of complex JSON objects, making debugging and data analysis much more efficient.

Setting Up the HTML Structure

This section focuses on building the basic HTML layout for our JSON formatter. We’ll create a container with a textarea for inputting JSON, buttons for loading, collapsing, expanding, and resetting the view, and a dedicated div to display the formatted JSON.

<html>

<head>
  <script>
  JSONViewer = (function() {
	var JSONViewer = function() {
		this._dom = {};
		this._dom.container = document.createElement("pre");
		this._dom.container.classList.add("json-viewer");
	};
	JSONViewer.prototype.showJSON = function(json, maxLvl, colAt) {
		maxLvl = typeof maxLvl === "number" ? maxLvl : -1; // max level
		colAt = typeof colAt === "number" ? colAt : -1; // collapse at

		var jsonData = this._processInput(json);
		var walkEl = this._walk(jsonData, maxLvl, colAt, 0);

		this._dom.container.innerHTML = "";
		this._dom.container.appendChild(walkEl);
	};

	/**
	 * Get container with pre object - this container is used for visualise JSON data.
	 * 
	 * @return {Element}
	 */
	JSONViewer.prototype.getContainer = function() {
		return this._dom.container;
	};

	/**
	 * Process input JSON - throws exception for unrecognized input.
	 * 
	 * @param {Object|Array} json Input value
	 * @return {Object|Array}
	 */
	JSONViewer.prototype._processInput = function(json) {
		if (json && typeof json === "object") {
			return json;
		}
		else {
			throw "Input value is not object or array!";
		}
	};

	/**
	 * Recursive walk for input value.
	 * 
	 * @param {Object|Array} value Input value
	 * @param {Number} maxLvl Process only to max level, where 0..n, -1 unlimited
	 * @param {Number} colAt Collapse at level, where 0..n, -1 unlimited
	 * @param {Number} lvl Current level
	 */
	JSONViewer.prototype._walk = function(value, maxLvl, colAt, lvl) {
		var frag = document.createDocumentFragment();
		var isMaxLvl = maxLvl >= 0 && lvl >= maxLvl;
		var isCollapse = colAt >= 0 && lvl >= colAt;

		switch (typeof value) {
			case "object":
				if (value) {
					var isArray = Array.isArray(value);
					var items = isArray ? value : Object.keys(value);

					if (lvl === 0) {
						// root level
						var rootCount = this._createItemsCount(items.length);
						// hide/show
						var rootLink = this._createLink(isArray ? "[" : "{");

						if (items.length) {
							rootLink.addEventListener("click", function() {
								if (isMaxLvl) return;

								rootLink.classList.toggle("collapsed");
								rootCount.classList.toggle("hide");

								// main list
								this._dom.container.querySelector("ul").classList.toggle("hide");
							}.bind(this));

							if (isCollapse) {
								rootLink.classList.add("collapsed");
								rootCount.classList.remove("hide");
							}
						}
						else {
							rootLink.classList.add("empty");
						}

						rootLink.appendChild(rootCount);
						frag.appendChild(rootLink);
					}

					if (items.length && !isMaxLvl) {
						var len = items.length - 1;
						var ulList = document.createElement("ul");
						ulList.setAttribute("data-level", lvl);
						ulList.classList.add("type-" + (isArray ? "array" : "object"));

						items.forEach(function(key, ind) {
							var item = isArray ? key : value[key];
							var li = document.createElement("li");

							if (typeof item === "object") {
								var isEmpty = false;

								// null && date
								if (!item || item instanceof Date) {
									li.appendChild(document.createTextNode(isArray ? "" : key + ": "));
									li.appendChild(this._createSimple(item ? item : null));
								}
								// array & object
								else {
									var itemIsArray = Array.isArray(item);
									var itemLen = itemIsArray ? item.length : Object.keys(item).length;

									// empty
									if (!itemLen) {
										li.appendChild(document.createTextNode(key + ": " + (itemIsArray ? "[]" : "{}")));
									}
									else {
										// 1+ items
										var itemTitle = (typeof key === "string" ? key + ": " : "") + (itemIsArray ? "[" : "{");
										var itemLink = this._createLink(itemTitle);
										var itemsCount = this._createItemsCount(itemLen);

										// maxLvl - only text, no link
										if (maxLvl >= 0 && lvl + 1 >= maxLvl) {
											li.appendChild(document.createTextNode(itemTitle));
										}
										else {
											itemLink.appendChild(itemsCount);
											li.appendChild(itemLink);
										}

										li.appendChild(this._walk(item, maxLvl, colAt, lvl + 1));
										li.appendChild(document.createTextNode(itemIsArray ? "]" : "}"));
										
										var list = li.querySelector("ul");
										var itemLinkCb = function() {
											itemLink.classList.toggle("collapsed");
											itemsCount.classList.toggle("hide");
											list.classList.toggle("hide");
										};

										// hide/show
										itemLink.addEventListener("click", itemLinkCb);

										// collapse lower level
										if (colAt >= 0 && lvl + 1 >= colAt) {
											itemLinkCb();
										}
									}
								}
							}
							// simple values
							else {
								// object keys with key:
								if (!isArray) {
									li.appendChild(document.createTextNode(key + ": "));
								}

								// recursive
								li.appendChild(this._walk(item, maxLvl, colAt, lvl + 1));
							}

							// add comma to the end
							if (ind < len) {
								li.appendChild(document.createTextNode(","));
							}

							ulList.appendChild(li);
						}, this);

						frag.appendChild(ulList);
					}
					else if (items.length && isMaxLvl) {
						var itemsCount = this._createItemsCount(items.length);
						itemsCount.classList.remove("hide");

						frag.appendChild(itemsCount);
					}

					if (lvl === 0) {
						// empty root
						if (!items.length) {
							var itemsCount = this._createItemsCount(0);
							itemsCount.classList.remove("hide");

							frag.appendChild(itemsCount);
						}

						// root cover
						frag.appendChild(document.createTextNode(isArray ? "]" : "}"));

						// collapse
						if (isCollapse) {
							frag.querySelector("ul").classList.add("hide");
						}
					}
					break;
				}

			default:
				// simple values
				frag.appendChild(this._createSimple(value));
				break;
		}

		return frag;
	};

	/**
	 * Create simple value (no object|array).
	 * 
	 * @param  {Number|String|null|undefined|Date} value Input value
	 * @return {Element}
	 */
	JSONViewer.prototype._createSimple = function(value) {
		var spanEl = document.createElement("span");
		var type = typeof value;
		var txt = value;

		if (type === "string") {
			txt = '"' + value + '"';
		}
		else if (value === null) {
			type = "null";
			txt = "null";
		}
		else if (value === undefined) {
			txt = "undefined";
		}
		else if (value instanceof Date) {
			type = "date";
			txt = value.toString();
		}

		spanEl.classList.add("type-" + type);
		spanEl.innerHTML = txt;

		return spanEl;
	};

	/**
	 * Create items count element.
	 * 
	 * @param  {Number} count Items count
	 * @return {Element}
	 */
	JSONViewer.prototype._createItemsCount = function(count) {
		var itemsCount = document.createElement("span");
		itemsCount.classList.add("items-ph");
		itemsCount.classList.add("hide");
		itemsCount.innerHTML = this._getItemsTitle(count);

		return itemsCount;
	};

	/**
	 * Create clickable link.
	 * 
	 * @param  {String} title Link title
	 * @return {Element}
	 */
	JSONViewer.prototype._createLink = function(title) {
		var linkEl = document.createElement("a");
		linkEl.classList.add("list-link");
		linkEl.href = "javascript:void(0)";
		linkEl.innerHTML = title || "";

		return linkEl;
	};

	/**
	 * Get correct item|s title for count.
	 * 
	 * @param  {Number} count Items count
	 * @return {String}
	 */
	JSONViewer.prototype._getItemsTitle = function(count) {
		var itemsTxt = count > 1 || count === 0 ? "items" : "item";

		return (count + " " + itemsTxt);
	};

	return JSONViewer;
})();
  </script>

</head>

  
<body>
  <div class="container">
    <div id="first">
      <form class="form">
        <label class="form__label--hidden" for="msg">Input json:</label>
        <textarea class="form__input" class="form__input" id="msg" placeholder="Input json..." rows="6"></textarea>
      </form>
    </div>
    <div id="two">
      <button type="button" class="load-json">Load</button>      
      <button type="button" class="collapse">Collapse</button>
      <button type="button" class="expand">Expand</button>
      <button type="button" class="reset">Reset</button>
    </div>
    <div id="three">
      <div id="json"></div>
    </div>
  </div>

Now, style the tool by adding the following CSS styles. You can customize it according to your needs.

input, textarea {
  display: block;
  outline: 0;
  border: 0;
}
input:focus, textarea:focus {
  transition: 0.2s;
}
body {
  background-color: #fff;
  font-family: 'Open Sans', Helvetica, sans-serif;
}
.container {
  width: 100%;
  margin: 30px auto;
}
#first {
  width: 350px;
  float: left;
  margin-left: 2%;
  margin-right: 2%;
}
#two {
  width: 100px;
  float: left;
  margin-right: 2%;
  margin-left: 2%;
  padding-top: 15%;
}
#three {
  width: 680px;
  float: left;
  margin-left: 1%;
   margin-top: -12px; 
}
.form__label {
  display: block;
  margin-bottom: 0.625em;
}
.form__label--hidden {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
}
.form__input {
  width: 100%;
   height: 560px; 
  font-size: 1em;
  padding: 0.83333em;
  margin-bottom: 5px;
  border: 6px solid #05bcaf;
  border-radius: 0.4em;
  background: #ebecec;
  color: #656D78;
  font-weight: 300;
}
.form__modal {
  width: 83%;
  font-size: 1em;
  padding: 0.83333em;
  margin-bottom: 5px;
  border-bottom: 6px solid #05bcaf;
  border-radius: 0.4em;
  background: #ebecec;
  color: #656D78;
  font-weight: 300;
}

.load-json, .reset, .collapse, .expand, [type^="button"] {
  padding: 0.9375em 1.875em;
  border: 0;
  border-radius: 0.4em;
  color: #fff;
  text-transform: uppercase;
  font-size: 0.875em;
  font-weight: 400;
  transition: opacity 0.2s;
  display: block;
  width: 100%;
}
.load-json:hover, .reset:hover, .collapse:hover, .expand:hover, [type^="button"]:hover {
  opacity: 0.75;
  cursor: pointer;
}
.load-json {
  background-color:  #3BAFDA;
}
.reset {
  background-color: #e87376;
}
.collapse {
  background-color: #D770AD;
}
.expand {
  background-color: #F6BB42;
}
[type^="button"] {
   margin-bottom: 1.42857em; 
  width: 120px;
  margin-right: 0.625em;
  outline:none;
}
.json-viewer {
  display: inline-block;
  overflow: scroll;
  height:  563px;
  width: 710px;
	color: #656D78;
  padding: 10px 10px 10px 20px;
  background-color: #ebecec;
  border: 6px solid #05bcaf;
  border-radius: 0.4em;
  margin-bottom: 3px;
}

.json-viewer ul {
	list-style-type: none;
	margin: 0;
	margin: 0 0 0 1px;
	border-left: 3px dotted #ccc;
	padding-left: 2em;
}

.json-viewer .hide {
	display: none;
}

.json-viewer ul li .type-string,
.json-viewer ul li .type-date {
	color:  #05bcaf;
}

.json-viewer ul li .type-boolean {
	color: #F6BB42;
	font-weight: bold;
}

.json-viewer ul li .type-number {
	color:   #e87376;
}

.json-viewer ul li .type-null {
	color: #EC87C0;
}

.json-viewer a.list-link {
	color: #656D78;
	text-decoration: none;
	position: relative;
}

.json-viewer a.list-link:before {
	color: #aaa;
	content: "\25BC";
	position: absolute;
	display: inline-block;
	width: 1em;
	left: -1em;
}

.json-viewer a.list-link.collapsed:before {
	content: "\25B6";
}

.json-viewer a.list-link.empty:before {
	content: "";
}

.json-viewer .items-ph {
	color: #aaa;
	padding: 0 1em;
}

.json-viewer .items-ph:hover {
	text-decoration: underline;
}

Finally, add the following JavaScript code to intilize JSONViewer app. You can place your JSON code inside the textarea that will display on page load.

var jsonObj = {};
var jsonViewer = new JSONViewer();
document.querySelector("#json").appendChild(jsonViewer.getContainer());

var textarea = document.querySelector("textarea");
textarea.value = JSON.stringify([
  {
    "id": "001",
    "user": "megan_F0x",
    "profile":
      {    
          "nama":"Megan Fox",
          "alamat":"Jl Nin aja dulu",
          "telepon":628123456789,
          "email":"duh@malu.co",
          "social media": 
            {
              "facebook":"megan fox",
              "twitter":"@megan_F0x"
            }
      },
    "pengalaman":[
            {
              "perusahaan":"PT Transformer",
              "daritahun": 2010,
              "sampaitahun": 2011,
              "posisi": "junior developer",
              "promosi karir": null,
              "listpekerjaan": [
                {
                  "jenis": "project base",
                  "deskripsi": "front end developer",
                  "lamapekerjaan": "6 bulan",
                  "deadline bonus": true,
                  "rekan kerja": [
                    {
                      "nama": "shia lebaouf",
                      "posisi": "lead developer",
                      "contact": 
                        {
                          "telepon" : 628890066631,
                          "email": "shia@lebaouf.com",
                          "socialmedia": {
                            "facebook": "shia lebaouf",
                            "twitter": "@shia lebaouf"
                          }
                        }
                    },
                    {
                      "nama": "josh duhamel",
                      "posisi": "senior developer",
                      "contact": 
                        {
                          "telepon" : 62345667687788,
                          "email": "josh@duhamel.com",
                          "socialmedia": {
                            "facebook": "josh duhamel",
                            "twitter": "@josh duhamel"
                          }
                        }
                    }]
                },
                {
                  "jenis": "project base",
                  "jabatan": "web developer",
                  "lamapekerjaan": "3 bulan",
                  "deadline bonus": false,
                  "rekan kerja": [
                    {
                      "nama": "stephen amell",
                      "posisi": "lead developer",
                      "contact": 
                        {
                          "telepon" : 629485765690,
                          "email": "stephen@amell.com",
                          "socialmedia": {
                            "facebook": "stephen amell",
                            "twitter": "@stephen_amell"
                          }
                        }
                    },
                    {
                      "nama": "tyler perry",
                      "posisi": "senior developer",
                      "contact": 
                        {
                          "telepon" : 62948453666,
                          "email": "tyler@perry.com",
                          "socialmedia": {
                            "facebook": "tyler perry",
                            "twitter": "@tyler perry"
                          }
                        }
                    }]
                }]
            }]
  }
]);

// textarea value to JSON object
var setJSON = function () {
  try {
    var value = textarea.value;
    jsonObj = JSON.parse(value);
  }
  catch (err) {
    alert(err);
  }
};

// load default value
setJSON();

var loadJsonBtn = document.querySelector("button.load-json");
var collapseBtn = document.querySelector("button.collapse");
var expandBtn = document.querySelector("button.expand");
var resetBtn = document.querySelector("button.reset");

loadJsonBtn.addEventListener("click", function () {
  setJSON();
  jsonViewer.showJSON(jsonObj);
});

collapseBtn.addEventListener("click", function () {
  jsonViewer.showJSON(jsonObj, null, 1);
});

expandBtn.addEventListener("click", function () {
  setJSON();
  jsonViewer.showJSON(jsonObj);
});

resetBtn.addEventListener("click", function () {
  document.getElementById("msg").value = "";
});

That’s all! Hopefully, you have successfully integrated this JSON formatter into your web/app project. If you have any questions or need help, feel free to comment below.

Loading... ...

Loading preview...

Device: Desktop
Dimensions: 1200x800
Lines: 0 Characters: 0 Ln 1, Ch 1

Leave a Comment

About W3Frontend

W3Frontend provides free, open-source web design code and scripts to help developers and designers build faster. Every snippet is reviewed before publishing for quality. Learn more.