I have a one-file website... We all know bandwidth and internet are... yeah... That's why I only render parts of user interface when needded. Now I wanna take a step further and use JSON to store small parts of my website (trees of html elements) and generete the actual HTML on demand.
But I'm struggling to figure out how to "convert" the JSON object into plain old HTML. I found many topics and answers on how to achieve this, but none of those solved problems. I need a solution that could generate trees with ANY number of chid elements, inside of child elements and so on... All these solutions used .innerHTML which could literally void all my event handlers. I found nothing I wouldn't know already...
The JSON notation would look like this:
var htmlTree = {
'type': 'section',
'attr': {
'id': 'someID',
'class': 'someClass'
},
'content': 'section name',
'children': [
{
'type': 'h1',
'attr': {
'class': 'heading'
},
'content': 'Heading',
'children': [
{
'type': 'span',
'content': 'text'
},
{
'type': 'span',
'content': 'more text'
}
]
}
]
}
And the result would look like this:
<section id="someID" class="someClass">
section name
<h1 class="heading">
Heading
<span>text</span>
<span>more text</span>
</h1>
</section>
Does anybody know a solution to this problem? I really don't want to use any frameworks or libraries. I know it's a little hard to do something like this, that's why I'm asking here...
Bandwidth is cheap nowadays as the server supports gzip it takes even less. Why combine with more engaging Json and libraries to create HTML?
Instead of processing JSON, inject ready (parts) html - loaded via Ajax.
Try
http://www.json2html.com
You can try something like this but this make sense only if you have many same rows (and your data is really simple).
<html>
<body>
<div id="ct"></div>
<script>
var data = [{
"id" : 1,
"f" : "Arya",
"l" : "Stark"
},
{
"id" : 1,
"f" : "Jon",
"l" : "Snowk"
}];
var parent = document.getElementById("ct");
data.forEach( function (row) {
newDiv = document.createElement("div");
newDiv.innerHTML = "<h1>" + row.f + " " + row.l + "</h1>";
parent.append(newDiv);
})
</script>
</body>
</html>
The JSON.parse reviver can be used to transform key value pairs in JSON string :
var json = '{"type":"section","attr":{"id":"someID","class":"someClass"},"content":"section name","children":[{"type":"h1","attr":{"class":"heading"},"content":"Heading","children":[{"type":"span","content":"text"},{"type":"span","content":"more text"}]}]}'
var el = JSON.parse(json, function(k, v) {
if (!v.type) return v
var el = document.createElement(v.type)
el.textContent = v.content
for (k in v.attr || {})
el.setAttribute(k, v.attr[k])
for (var c of v.children || [])
el.append('\n', c)
return el
})
document.body.innerHTML = el.outerHTML
console.log( el.outerHTML )
Related
I am trying to add json object for html arrtibute content but not working.I have tried many ways but I do not know how to set that.if any one know about that please help to resolve this issue.
javascript:
var validatorValue='{
"picker":{
"allow":
{
"message": "Content loram ipsom"
},
"past":
{
"message": "lorem issom"
}
}
}' ;
var daterestrictValue="{'range': {'start': '2019-10-30','end': '2019-12-30'}}";
var myinputValue="{'date':'tomorrow'}";
$("#mydiv").html("<div input="true" validator='+validatorValue+' date-restrict='+daterestrictValue+' my-input='+myinputValue+'></div>");
The main issue with your code (aside from line-breaks in string literals) it the mis-matched quotes in the HTML string you build. However, even after correcting those you will have issues placing serialised JSON within attributes as it too contains double quotes which will break the HTML syntax.
Also note that you're creating non-standard attributes in the HTML you create which may cause unexpected issues in your UI and JS.
An alternative way to approach this is to work with the values as objects (instead of strings). You can use jQuery to set the data of the element using those objects, like this:
var validatorValue = { "picker": { "allow": { "message": "Content loram ipsom" }, "past": { "message": "lorem issom" } } }
var daterestrictValue = { 'range': { 'start': '2019-10-30', 'end': '2019-12-30' } }
var myinputValue = { 'date': 'tomorrow' };
var $newDiv = $('<div data-input="true">Click me</div>').appendTo('#mydiv');
$newDiv.data({
"validator": validatorValue,
"date-restrict": daterestrictValue,
"my-input": myinputValue
});
// for testing
$('#mydiv div').on('click', function() {
console.log($(this).data('validator'));
console.log($(this).data('date-restrict'));
console.log($(this).data('my-input'));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="mydiv"></div>
this code save object in html element.
good luck
var validatorValue="{ 'picker':{'allow':{'message': 'Content loram ipsom'},'past':{'message': 'lorem issom'}}}";
var daterestrictValue="{'range': {'start': '2019-10-30','end': '2019-12-30'}}";
var myinputValue="{'date':'tomorrow'}";
$("#mydiv").html(`
<div input='true' validator="${validatorValue}" date-restrict="${daterestrictValue}" my-input="${myinputValue}">ggg</div>`);
I am trying to retrieve the target data from PrimeUI pickList, but with no success. Maybe someone with good knowledges in jQuery can help me. Well, I think my problem is simple. I successfully implemented the pickList from PrimeUI, but I don't know and I can't retrieve the target data from the pickList.
Well, let me show some code. My javascript looks like this:
<script>
$(document).ready(function() {
var mySourceData = [{label: 'Label 1', value: 1}, {label: 'Label 2', value: 2}];
var myTargetData = new Array();
$('#myPickList').puipicklist({
filter: true,
dragdrop: true,
filterMatchMode: 'contains',
sourceData: mySourceData,
targetData: myTargetData
});
$('#myPickListSaveButton').click(function(){
//How to retrieve #myPickList target data?
});
}
</script>
My HTML:
<div id="atividadesPicklist">
<select name="source"></select>
<select name="target"></select>
</div>
Like I wrote inside the #myPickListSaveButton function, how can I retrieve the value from targetData?
Thanks.
The plugin will move the options to the target select, meaning you can simply get the options from that select
$('#myPickListSaveButton').click(function () {
var targetData = $.map($('select[name=target] option'), function (v) {
return v.value; // maps the values and returns them in an array ["1", "2"]
});
console.log(targetData);
});
Example
I'm using select2 for tagging and I have it setup such that a user can add new tags as well. The issue that I'm dealing with is validating the user entry and adding the sanitized tag to selection.
To be more specific, when a user enters a space in a tag, i use formatNoMatches to display a js link to sanitize the tag and then add the tag programmatically. This code seems to run without errors but when sanitize is called all selections of the input are cleared.
Any clues where i might be going wrong?
var data=[{id:0,tag:'enhancement'},{id:1,tag:'bug'},{id:2,tag:'duplicate'},{id:3,tag:'invalid'},{id:4,tag:'wontfix'}];
function format(item) { return item.tag; }
function sanitize(a){
$("#select").select2('val',[{
id: -1,
tag: a
}]);
console.log(a);
};
$("#select").select2({
tags: true,
// tokenSeparators: [",", " "],
createSearchChoice: function(term, data) {
return term.indexOf(' ') >= 0 ? null :
{
id: term,
tag: term
};
},
multiple: true,
data:{ results: data, text: function(item) { return item.tag; } }, formatSelection: format, formatResult: format,
formatNoMatches: function(term) { return "\"" + term + "\" <b>Is Invalid.</b> <a onclick=\"sanitize('"+ term +"')\">Clear Invalid Charecters</a>" }
});
Only this solution works for me:
function convertObjectToSelectOptions(obj){
var htmlTags = '';
for (var tag in obj){
htmlTags += '<option value="'+tag+'" selected="selected">'+obj[tag]+'</option>';
}
return htmlTags;
}
var tags = {'1':'dynamic tag 1', '2':'dynamic tag 2'}; //merge with old if you need
$('#my-select2').html(convertObjectToSelectOptions(tags)).trigger('change');
After hacking on it some more i realized that I should be setting the new item to the "data" property and not value.
var newList = $.merge( $('#select').select2('data'), [{
id: -1,
tag: a
}]);
$("#select").select2('data', newList)
You can set new value (if tags you can pass array) and trigger 'change' event.
var field = $('SOME_SELECTOR');
field.val(['a1', 'a2', 'a3']) // maybe you need merge here
field.trigger('change')
About events: https://select2.github.io/options.html#events
I have a object that looks like this:
var grocery_list = {
"Banana": { category: "produce", price: 5.99 },
"Chocolate": { category: "candy", price: 2.75 },
"Wheat Bread": { category: "grains and breads", price: 2.99 }
}
And I want to be able to display each item in the object in HTML like this:
<div id="grocery_item" class="container">
<div class="item">Item Here</div>
<div class="category">Category Here</div>
<div class="price">Price Here</div>
</div>
I know how to loop through an Object in JS, but I'm not sure how to display those items in the DOM. I believe I could use the jQuery append function but I'm not sure how.
Any help would be appreciated. thanks!
This is how you can do it using jQuery (as you mention you'd like to use jQuery first). But it's not the best way how to it. If you're open to learn better methods then check some of the MV* frameworks (AngularJS, Ember etc.). Anyway, here is just an example:
var grocery_list = {
"Banana": { category: "produce", price: 5.99 },
"Chocolate": { category: "candy", price: 2.75 },
"Wheat Bread": { category: "grains and breads", price: 2.99 }
}
var wrapper = $('#wrapper'), container;
for (var key in grocery_list){
container = $('<div id="grocery_item" class="container"></div>');
wrapper.append(container);
container.append('<div class="item">' + key +'</div>');
container.append('<div class="category">' + grocery_list[key].category +'</div>');
container.append('<div class="price">' + grocery_list[key].price +'</div>');
}
jsfiddle here: http://jsfiddle.net/5jhgbg9w/
EDIT: Please, take this really as an example (which is OK since you're learning). As others pointed out - it's not the best method. Try to combine other examples, particularly the ones which do not compose HTML as a string. For easy tasks like this it's straightforward but the more code you would add, the messier the code becomes. I believe your "learning evolution" would get you there anyway :-) Cheers everyone
EDIT (2021): A lot of years have gone by since I answered this question. What about some more modern examples, just for fun? Without jQuery, just ES6:
const grocery_list = {
"Banana": { category: "produce", price: 5.99 },
"Chocolate": { category: "candy", price: 2.75 },
"Wheat Bread": { category: "grains and breads", price: 2.99 }
};
// since "grocery_list" is an object (not an array) we have do Object.keys()
const generatedHtml = Object.keys(grocery_list).reduce((accum, currKey) => accum +
`<div class="grocery_item">
<div class="item">${currKey}</div>
<div class="category">${grocery_list[currKey].category}</div>
<div class="price">${grocery_list[currKey].price}</div>
</div>`, '');
document.getElementById('container').innerHTML = generatedHtml;
.grocery_item{
padding: 5px;
}
.grocery_item:not(:last-child){
border-bottom: 1px solid #ccc;
}
<div id="container"></div>
You can create HTML elements with jQuery: $('<div>', {attr1: value, attr2: value}); This returns a new jQuery object so you can use it like jQuery element.
The jQuery.text() method: $('div').text('some text') This method is recommended if you putting just text inside the element. It will escape all special characters like '<'. Instead it puts <. This avoiding XSS attacks unlike jQuery.html() method.
You can look Security Considerations at jQuery docs about .html method.
// Create a div element with jQuery which will hold all items.
var $tree = $('<div>', {id: 'items-tree'});
//Loop all items and create elements for each
for (var key in grocery_list) {
var $gItem = $('<div>', {
id: 'grocery_item',
class: 'container'
});
var $item = $('<div>', {
class: 'item'
}).text(key);
$gItem.append($item);
var $category = $('<div>', {
class: 'category'
}).text(grocery_list[key].category);
$gItem.append($category);
var $price = $('<div>', {
class: 'price'
}).text(grocery_list[key].price);
$gItem.append($price);
$tree.append($gItem);
}
JSFiddle
It's old but you can try package html-stringify. As its introduction: "Converts Javascript Objects or Arrays to pretty HTML string for rendering in a browser."
The parent div <div id="grocery_item" class="container"> will be repeated for the no. of items in the grocery list.
The "id" of this parent div will be generated randomly for example item #index 0 will be "grocery_item0"
For each loop item create the parent div's first, then start appending the details to the parent div's using (another loop).
Below will be sample jquery,
$( ".grocery_item0" ).append( "" );
I will pass by the fact that your list definition is not exactly the best and I say this:
Using jQuery you could do that:
<script>
var _parent = $(".container").parent();
_parent.empty();
var _html;
$.each(grocery_list,function(prop, val){
_html = "<div id=\"grocery_item\" class=\"container\">";
_html += "<div class=\"item\">"+prop+"</div>";
_html += "<div class=\"category\">"+val.category+"</div>";
_html += "<div class=\"price\">"+val.price+"</div>";
_html += "</div>";
_parent.append(_html);
});
</script>
Note*: It's not recomanded to maanipulate the DOM by creating HTML strings. This is just an fast sample for your school assignment.
I have this code in my application:
$scope.appendBets = function()
{
$.each($scope.phaseBets, function(i, bet)
{
var betElement = angular.element('<div ng-model="phaseBets[i]">Bet id: {{phaseBets[i].id}}</div>');
angular.element(document.querySelector('#betsHolder')).append(betElement);
$compile(betElement)($scope);
});
}
the $scope.phaseBets is loaded from $http.get.
Now the problem is that the {{phaseBets[i].id}} content not seen on the html page, I am getting this Bet id:.
I have seen this OS but it's not working for me, maybe because of the array.
Is there anything wrong with my code?
Note
The thing is that by the I'd I will create an element (different for each id) so ng-repeat not helping that mutch.
Here's how I'd do it using ng-repeat and ng-include:
$scope.items = [
{id: 1, title: 'foo', data: {body: 'baz1'}},
{id: 2, title: 'bar', data: {body: 'baz2'}}
];
<div ng-repeat="item in items">
<h2>{{item.title}}</h2>
<div ng-include src="getTemplateById(item.id)"></div>
</div>
Where the templates are defined inline like this:
<script type="text/ng-template" id="template-1.html">
Content of template-1.html
<div>{{item.data.body}}</div>
</script>
<script type="text/ng-template" id="template-2.html">
<p>Content of template-2.html</p>
</script>
and getTemplateById is:
$scope.getTemplateById = function(id) {
return 'template-' + id + '.html';
};
You can see it in action here.
I think you got it from wrong side, in angularjs controllers/data drives the view, here you are creating elements (and even worse adding them to page) in loop (expensive DOM operations)
I'd replace your code with following
<div id="betsHolder">
<div ng-repeat="bet in phaseBets track by bet.id">Bet id: {{bet.id}}</div>
</div>
as soon as you assign your array/object to $scope.phaseBets the DOM will be created
but using ng-repeat is better option,
angular.forEach($scope.phaseBets, function(bet, i)
{
var betElement = angular.element('<div ng-model="bet">Bet id: {{bet.id}}</div>');
angular.element(document.querySelector('#betsHolder')).append(betElement);
$compile(betElement)($scope);
});