Skip to content

Instantly share code, notes, and snippets.

@fabioyamate
Last active January 22, 2025 02:35
Show Gist options
  • Save fabioyamate/3f31b5e97bf48ea3697fe5b005504762 to your computer and use it in GitHub Desktop.
Save fabioyamate/3f31b5e97bf48ea3697fe5b005504762 to your computer and use it in GitHub Desktop.
A way to build a HTML rendering for JSON data.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced JSON Viewer</title>
<style>
#dropzone {
width: 100%;
height: 150px;
border: 2px dashed #ccc;
display: flex;
align-items: center;
justify-content: center;
color: #888;
margin-bottom: 20px;
}
#dropzone.dragover {
border-color: #666;
color: #333;
}
.collapsible {
margin-bottom: 10px;
}
.collapsible-header {
background-color: #f0f0f0;
padding: 10px;
cursor: pointer;
font-weight: bold;
border: 1px solid #ccc;
}
.collapsible-content {
border: 1px solid #ccc;
border-top: none;
padding: 10px;
display: none;
}
.collapsible-content.show {
display: block;
}
.key {
font-weight: bold;
}
ul {
padding-left: 20px;
}
li {
margin-bottom: 5px;
}
</style>
</head>
<body>
<div id="dropzone">Drag and drop a JSON file here</div>
<div id="output"></div>
<!-- Handlebars Template -->
<script id="template" type="text/x-handlebars-template">
<h1>{{title}}</h1>
<p>{{content}}</p>
<div class="collapsible">
<div class="collapsible-header">Items</div>
<div class="collapsible-content">
<ul>
{{#each items}}
<li>
{{#if (isObject this)}}
<ul>
{{#each this}}
<li><span class="key">{{@key}}:</span> {{renderValue this}}</li>
{{/each}}
</ul>
{{else}}
{{renderValue this}}
{{/if}}
</li>
{{/each}}
</ul>
</div>
</div>
<div class="collapsible">
<div class="collapsible-header">Details</div>
<div class="collapsible-content">
{{renderValue details}}
</div>
</div>
</script>
<!-- Handlebars.js Library -->
<script src="https://cdn.jsdelivr.net/npm/handlebars@latest/dist/handlebars.min.js"></script>
<script>
// Register Handlebars Helpers
Handlebars.registerHelper('isObject', function (value) {
return typeof value === 'object' && !Array.isArray(value) && value !== null;
});
Handlebars.registerHelper('renderValue', function (value) {
if (Array.isArray(value)) {
return new Handlebars.SafeString('<ul>' +
value.map(item => `<li>${Handlebars.helpers.renderValue(item)}</li>`).join('') +
'</ul>');
} else if (typeof value === 'object' && value !== null) {
return new Handlebars.SafeString('<ul>' +
Object.entries(value).map(([key, val]) =>
`<li><span class="key">${key}:</span> ${Handlebars.helpers.renderValue(val)}</li>`
).join('') +
'</ul>');
} else if (typeof value === 'boolean') {
return value ? 'True' : 'False';
} else if (typeof value === 'number') {
return value.toString();
} else if (value === null) {
return 'Null';
} else {
return value;
}
});
const dropzone = document.getElementById('dropzone');
const output = document.getElementById('output');
// Compile Handlebars template
const templateSource = document.getElementById('template').innerHTML;
const template = Handlebars.compile(templateSource);
// Handle drag-and-drop events
dropzone.addEventListener('dragover', (e) => {
e.preventDefault();
dropzone.classList.add('dragover');
});
dropzone.addEventListener('dragleave', () => {
dropzone.classList.remove('dragover');
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('dragover');
const file = e.dataTransfer.files[0];
if (file && file.type === "application/json") {
const reader = new FileReader();
reader.onload = (event) => {
try {
const data = JSON.parse(event.target.result);
renderContent(data);
} catch (err) {
alert("Invalid JSON file.");
}
};
reader.readAsText(file);
} else {
alert("Please drop a valid JSON file.");
}
});
// Function to render JSON content using Handlebars
function renderContent(data) {
const html = template(data);
output.innerHTML = html;
attachCollapsibleHandlers();
}
// Attach click event to collapsible headers
function attachCollapsibleHandlers() {
const headers = document.querySelectorAll('.collapsible-header');
headers.forEach(header => {
header.addEventListener('click', () => {
const content = header.nextElementSibling;
content.classList.toggle('show');
});
});
}
</script>
</body>
</html>
{
"title": "My Dynamic Page",
"content": "This is content loaded from a JSON file.",
"items": ["Item 1", "Item 2", "Item 3"]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment