Dynamically create elements - javascript

I'm currently working on several Apps that include AJAX calls and I'm using JSON format to retrieve data from the server.
Each page needs to create content based on the JSON response, and I'm currently creating the content like:
function createBox1(json) {
var bigbox = document.createElement('div');
bigbox.className = 'class1';
var firstbox = document.createElement('div');
firstbox.className = 'first-box';
var firstNestedBox = document.createElement('div');
var secondNestedBox = document.createElement('div');
var thirdNestedBox = document.createElement('div');
var secondbox = document.createElement('div');
...
So basically its kinda a long code and I wanted to know if there is a better way to do it.
PS: I have seen some libraries where they do something like:
function o(t,e){var i=document.createElement(t||"div"),o;for(o in e)i[o]=e[o];return i}
and I suppose that's how they create multiple div elements, but I'm not sure how does that works.
Thanks in Advance (:

There's nothing wrong with the way you're doing it, but it's faster to just clone an empty div instead of creating new ones, and it's faster to use a fragment when building markup, and then insert the fragment in the DOM once everything is built instead of inserting each element in the DOM etc.
Just for making shorter code, a function could be used, but it's really just an uneccessary function call:
function createBox1(json) {
var div = document.createElement('div'),
bigbox = div.cloneNode(false),
firstbox = div.cloneNode(false),
firstNestedBox = div.cloneNode(false),
secondNestedBox = div.cloneNode(false),
thirdNestedBox = div.cloneNode(false),
secondbox = div.cloneNode(false):
bigbox.className = 'class1';
firstbox.className = 'first-box';
...

Related

How to use a same image multiple times by loading it only once when I need different id for every element

I am a newbie to programming and web developing. The project I am doing is only for practice, if my approach seems ameteur to you, please suggest any better options.
I am trying to develop a parking lot booking system. And in the UI, I want to show all the empty/filled slots (like it is while booking movies or bus tickets).
I couldn't find a top view icon of a car, so I thought of using an image instead of icon.
But as of the image, if I use say 50 images on a single page, the page will get very heavy.
But one important thing is that I need all the elements as seperate entities, only then I will be able to book them with their id(unique address). So I want 50 different divs with seperate distinct ids but want to use only one image for all the slots, or a maximum of 2 different images(keeping the directions in mind).
how to display same image multiple times using same image in javascript
I went through this answer, and found a piece of code that might be useful:
var imgSrc = 'http://lorempixel.com/100/100';
function generateImage() {
var img = document.createElement('img')
img.src = imgSrc;
return img;
}
for (var i = 0; i < 20; i++ ) {
document.body.appendChild(generateImage());
}
While I can make use of a function and a loop in javascript to create as many copies of one image, I don't know how to alot them to the different div tags with distinct ids.
use a function :)
const addMessage = (element, msg, cls) => {
const patt = new RegExp("<");
const messageElement = document.createElement("div");
if (patt.test(msg)) {
messageElement.innerHTML = msg
} else messageElement.textContent = msg;
if (cls) messageElement.classList.add(cls);
element.appendChild(messageElement);
}
const imgPath = "/somepath";
const body = document.querySelector("body");
addMessage(body, `<img src=${imgPath} class="whatever">`, "img1"); //creates new divs with classes. the 3rd arg is optional
the best approach for this is the client side already receiving all this content as a string but as it made clear that it is for study I deduce that it is not the intention to use back end to solve this problem
let allContent = '';
for (var i = 0; i < 20; i++ ) {
allContent += `<div class="wrapper-image"><img src="/path"></div>`
}
document.getElementById('idWrapper').innerHTML = allContent;
speaking in performace the browser will only download the image once, so you can use it as many times as you like, which is disruptive to the changes you make in the DOM (remove, add or edit a content)
In my example you create all the content to be displayed on the page and then add it in a single time, it is not too bad if it is performace but the ideal is to do it on the back side
in the DIV you can put an image address of a variable to do some logical type this:
let allContent = '';
let imgOne = '/oneimg';
let imgOne = '/twoImg';
for (var i = 0; i < 20; i++ ) {
if(i>10){
allContent += `<div class="wrapper-image"><img src="${imgOne}"></div>`
}else {
allContent += `<div class="wrapper-image"><img src="${twoImg}"></div>`
}
}
document.getElementById('idWrapper').innerHTML = allContent;

Array is not an array anymore after getting stringified and parsed (JSON)

After that I stringified my array I parsed it, I then tried to acces one of the elements inside the array (array[number]) but it seems that the elements in the array are all mashed as one or something like that. I would like to know why and how to change this. Thanks in advance!
var linkrecup = localStorage.getItem('linksstring');
JSON.parse(linkrecup);
alert(linkrecup);
var linkarr = [''+linkrecup+''];
alert(linkarr.length); // this gives '1'
var div = document.createElement('div');
document.body.appendChild(div);
div.id = 'placememe'+memenumber+'';
div.className = 'meme';
var nombrememes = document.getElementsByClassName("meme").length;
alert(nombrememes);
var crpt = nombrememes - 1;
alert(crpt);
alert(linkarr[3]); // this gives 'undefined'
Every var that is used is declared in another piece of code, if you want it just ask and I'll gladly give it to you.
EDIT: So basically what im doing (trying) with this code to do is to push a link that is given by the user (prompt) and add it to the array of links that already have some links stored in (.push) then stock it (the whole array) in localStorage (stringified with JSON.stringify) then re-acces this array later (with a localStorage.getItem) then parse it (JSON.parse) and then use one of the elements of the array (link) as the link for an image. Here's the code with the stringify and shit.
var newmeme = prompt('Please paste the link of the meme below!');
memes.push ('placememe'+memenumber+'');
links.push (newmeme);
var div = document.createElement('div');
document.body.appendChild(div);
div.id = 'placememe'+memenumber+'';
div.className = 'meme';
var nombrememes = document.getElementsByClassName("meme").length;
var vrb = nombrememes - 1;
div.innerHTML = '<img src="'+links[vrb]+'" width="700" height="700" alt="" />';
var linksstring = links
localStorage.setItem('linksstring',JSON.stringify(linksstring));
var linkrecup = localStorage.getItem('linksstring');
JSON.parse(linkrecup);
alert(linkrecup);
JSON.parse(object) returns a parsed object
Try using
var parsedlinkrecup=JSON.parse(linkrecup)

Inserting arbitrary HTML into a DocumentFragment

I know that adding innerHTML to document fragments has been recently discussed, and will hopefully see inclusion in the DOM Standard. But, what is the workaround you're supposed to use in the meantime?
That is, take
var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();
I want both the div and the span inside of frag, with an easy one-liner.
Bonus points for no loops. jQuery is allowed, but I've already tried $(html).appendTo(frag); frag is still empty afterward.
Here is a way in modern browsers without looping:
var temp = document.createElement('template');
temp.innerHTML = '<div>x</div><span>y</span>';
var frag = temp.content;
or, as a re-usable
function fragmentFromString(strHTML) {
var temp = document.createElement('template');
temp.innerHTML = strHTML;
return temp.content;
}
UPDATE:
I found a simpler way to use Pete's main idea, which adds IE11 to the mix:
function fragmentFromString(strHTML) {
return document.createRange().createContextualFragment(strHTML);
}
The coverage is better than the <template> method and tested ok in IE11, Ch, FF.
Live test/demo available http://pagedemos.com/str2fragment/
Currently, the only way to fill a document fragment using only a string is to create a temporary object, and loop through the children to append them to the fragment.
Since it's not appended to the document, nothing is rendered, so there's no performance hit.
You see a loop, but it's only looping through the first childs. Most documents have only a few semi-root elements, so that's not a big deal either.
If you want to create a whole document, use the DOMParser instead. Have a look at this answer.
Code:
var frag = document.createDocumentFragment(),
tmp = document.createElement('body'), child;
tmp.innerHTML = '<div>x</div><span>y</span>';
while (child = tmp.firstElementChild) {
frag.appendChild(child);
}
A one-liner (two lines for readability) (input: String html, output: DocumentFragment frag):
var frag =document.createDocumentFragment(), t=document.createElement('body'), c;
t.innerHTML = html; while(c=t.firstElementChild) frag.appendChild(c);
Use Range.createContextualFragment:
var html = '<div>x</div><span>y</span>';
var range = document.createRange();
// or whatever context the fragment is to be evaluated in.
var parseContext = document.body;
range.selectNodeContents(parseContext);
var fragment = range.createContextualFragment(html);
Note that the primary differences between this approach and the <template> approach are:
Range.createContextualFragment is a bit more widely supported (IE11 just got it, Safari, Chrome and FF have had it for a while).
Custom elements within the HTML will be upgraded immediately with the range, but only when cloned into the real doc with template. The template approach is a bit more 'inert', which may be desirable.
No one ever provided the requested "easy one-liner".
Given the variables…
var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();
… the following line will do the trick (in Firefox 67.0.4):
frag.append(...new DOMParser().parseFromString(html, "text/html").body.childNodes);
#PAEz pointed out that #RobW's approach does not include text between elements. That's because children only grabs Elements, and not Nodes. A more robust approach might be as follows:
var fragment = document.createDocumentFragment(),
intermediateContainer = document.createElement('div');
intermediateContainer.innerHTML = "Wubba<div>Lubba</div>Dub<span>Dub</span>";
while (intermediateContainer.childNodes.length > 0) {
fragment.appendChild(intermediateContainer.childNodes[0]);
}
Performance may suffer on larger chunks of HTML, however, it is compatible with many older browsers, and concise.
createDocumentFragment creates an empty DOM "container". innerHtml and other methods work only on DOM nodes (not the container) so you have to create your nodes first and then add them to the fragment. You can do it using a painful method of appendChild or you can create one node and modify it's innerHtml and add it to your fragment.
var frag = document.createDocumentFragment();
var html = '<div>x</div><span>y</span>';
var holder = document.createElement("div")
holder.innerHTML = html
frag.appendChild(holder)
with jquery you simply keep and build your html as a string. If you want to convert it to a jquery object to perform jquery like operations on it simply do $(html) which creates a jquery object in memory. Once you are ready to append it you simply append it to an existing element on a page
Like #dandavis said, there is a standard way by using the template-tag.
But if you like to support IE11 and you need to parse table elements like '<td>test', you can use this function:
function createFragment(html){
var tmpl = document.createElement('template');
tmpl.innerHTML = html;
if (tmpl.content == void 0){ // ie11
var fragment = document.createDocumentFragment();
var isTableEl = /^[^\S]*?<(t(?:head|body|foot|r|d|h))/i.test(html);
tmpl.innerHTML = isTableEl ? '<table>'+html : html;
var els = isTableEl ? tmpl.querySelector(RegExp.$1).parentNode.childNodes : tmpl.childNodes;
while(els[0]) fragment.appendChild(els[0]);
return fragment;
}
return tmpl.content;
}
Here is a x-browser solution, tested on IE10, IE11, Edge, Chrome and FF.
function HTML2DocumentFragment(markup: string) {
if (markup.toLowerCase().trim().indexOf('<!doctype') === 0) {
let doc = document.implementation.createHTMLDocument("");
doc.documentElement.innerHTML = markup;
return doc;
} else if ('content' in document.createElement('template')) {
// Template tag exists!
let el = document.createElement('template');
el.innerHTML = markup;
return el.content;
} else {
// Template tag doesn't exist!
var docfrag = document.createDocumentFragment();
let el = document.createElement('body');
el.innerHTML = markup;
for (let i = 0; 0 < el.childNodes.length;) {
docfrag.appendChild(el.childNodes[i]);
}
return docfrag;
}
}
I would go with something like this..
function fragmentFromString(html) {
const range = new Range();
const template = range.createContextualFragment(html);
range.selectNode(template.firstElementChild);
return range;
}
// Append to body
// document.body.append(fragmentFromString(`<div>a</div>`).cloneContents())
This way you keep the content inside a Range object and you get all the needed methods for free.
You can find the list of all Range methods and properties here https://developer.mozilla.org/en-US/docs/Web/API/Range
Note: Remember to use detatch() method once you are done with it to avoid leaks and improve performance.
var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();
var e = document.createElement('i');
frag.appendChild(e);
e.insertAdjacentHTML('afterend', html);
frag.removeChild(e);
To do this with as little lines as possible, you could wrap your content above in another div so you do not have to loop or call appendchild more than once. Using jQuery (as you mentioned is allowed) you can very quickly create an unattached dom node and place it in the fragment.
var html = '<div id="main"><div>x</div><span>y</span></div>';
var frag = document.createDocumentFragment();
frag.appendChild($​(html)[0]);

How can I use multiple variables to retrieve JS objects?

I'm only working on my 3rd Javascript project, so this is probably easy to answer (at least I hope so).
I have learned to use JS object in place of arrays. In this project I have named multiple object with a nested system of IDs as follows:
animalia = new Object();
animalia.chordata = new Object();
animalia.chordata.actinopterygii = new Object();
animalia.chordata.actinopterygii.acipenseriformes = new Object();
etc.......
I'm having problems calling on objects named this way though. Here is my code:
function expand(event){
var target = event.target;
console.log(target);
var parent = target.parentNode;
console.log(parent);
var parentclass = parent.getAttribute("class");
console.log(parentclass);
if (parentclass == "kingdom"){
var newdiv = document.createElement("div");
var newexpctrl = document.createElement("div");
var parentid = parent.getAttribute("id");
console.log(parentid);
----> var parentobj = window[parentid];
console.log(parentobj);}
else{
var upperclass = searchArray(parentclass);
console.log(upperclass);
var newdiv = document.createElement("div");
var newexpctrl = document.createElement("div");
var parentId = parent.getAttribute("id");
console.log(parentId);
var parentnode_ = document.getElementById(parentId);
console.log(parentnode_);
var gparentId = parentnode_.parentNode.id;
console.log(gparentId);
----> var parentobj = window[gparentId.parentId];
console.log(parentobj);
}
var childnumb = parentobj.children;
}
I am having my problem with the two statements indicated by "---->". In the first case, using a single variable works for pulling up the proper object. However, in the second case, using two variables, I fail to be able to access the proper object. What is the proper syntax for doing this? I have tried a plethora of different syntax combinations, but nothing seems to work correctly. Or should is there a better method for calling on JS objects other than using "window[variable]"?
P.S.- If you haven't figured it out by now, I am working on educational tools for use in learning biology. Thanks once again stackoverflow, you guys rule.
Assuming that the window object has something w/ the property matching a string that's the value of gparentId, you should be able to do:
var parentobj = window[gparentId][parentId];
The problem here is that the square bracket's notation is being applied to too much. gparentId is a string. It doesn't have a property called parentId. You therefore have to do this in two steps. First get:
window[gparentId]
Then get the appropriate property of that object
var parentobj = window[gparentId][parentId];
On a somewhat unrelated note, this isn't very well written JavaScript code:
Creating Objects
When creating new objects, always use the following syntax:
var obj = {};
That's what's generally been accepted as standard, so it's easier for people to read.
Declaring Variables in If Statements
You shouldn't really declare variables inside an if statement, especially when declaring the same variable in the else block, that's really confusing. Instead, declare all the variables at the top in a list and then use them without the var keyword lower down.
var newdiv = document.createElement("div"),
newexpctrl = document.createElement("div"),
parentid = parent.getAttribute("id"),
parentobj;
Note the commas instead of semi-colons which means I don't have to repeat the var keyword. Since the values of newdiv, newexpctrl and parentid are the same in either case, I give them their values straight away, making the contents of the if statement much shorter and easier to digest.
Result
function expand(event){
var target = event.target;
var parent = target.parentNode;
var parentclass = parent.getAttribute("class");
var newdiv = document.createElement("div"),
newexpctrl = document.createElement("div"),
parentid = parent.getAttribute("id"),
parentobj, upperclass;
if (parentclass == "kingdom"){
parentobj = window[parentid];
}else{
upperclass = searchArray(parentclass);
var _parentId = document.getElementById(parentId).parentNode.id;
parentobj = window[_parentId][parentId];
}
var childnumb = parentobj.children;
}
Note that I've left var _parentId inside the if since I think it probably improves readability, but you may choose to take it outside the if, since it will pollute the namespace of the function anyway.

How to wrap dynamically selected ids using jquery or javascript

I am working on a project in Google Blogger. First i want to explain a thing.
In blogger every post that is created has a unique id assigned to it by blogger itself. This id can be retrieved using Blogger JSON. So i have retrieved the ids of four recent posts using JSON.
I want to wrap these first four id containers around a DIV container using JQuery or Javascript.
The problem is when i use these ids absolutely in the selector $ and use the wrapAll() function the id container's gets wrapped up. But as i said i'm using JSON to get the container id's so the values of ID's are stored in variable's and when i use those variable as selection for wrapAll() function it doesn't work.
I have demos of both those situation's which can be seen by going to this blog http://youblog-demo.blogspot.com/ and using the firebug console to run these code.
Situation 1 when i use absolute container ids
var script = document.createElement("script");
script.src = "http://youblog-demo.blogspot.com/feeds/posts/default?alt=json&callback=hello";
document.body.appendChild(script);
function hello(json){
if(json.feed.entry.length>4){
var post_num=4;
var id_coll = new Array();
for(i=0; i<post_num; i++){
var ids = json.feed.entry[i].id.$t;
var post_id = ids.substring(ids.indexOf("post-"));
var only_id = post_id.substring(5);
id_coll[i] = only_id;
}
$("#3337831342896423186,#123892177945256656,#9095347670334802803,#2525451832509945787").wrapAll('<div>');
}
};
Situation 2 when i use variable's to select the containers
var script = document.createElement("script");
script.src = "http://youblog-demo.blogspot.com/feeds/posts/default?alt=json&callback=hello";
document.body.appendChild(script);
function hello(json){
if(json.feed.entry.length>4){
var post_num=4;
var id_coll = new Array();
var front_name = "#";
for(i=0; i<post_num; i++){
var ids = json.feed.entry[i].id.$t;
var post_id = ids.substring(ids.indexOf("post-"));
var only_id = post_id.substring(5);
id_coll[i] = only_id;
}
var joined_id_0 = String.concat(front_name,id_coll[0]);
var joined_id_1 = String.concat(front_name,id_coll[1]);
var joined_id_2 = String.concat(front_name,id_coll[2]);
var joined_id_3 = String.concat(front_name,id_coll[3]);
$(joined_id_0,joined_id_1,joined_id_2,joined_id_3).wrapAll('<div>');
}
};
So when i use the situation 2 code then it doesn't work but the situation1 code works fine. Can anybody help me with this
You need to pass in the selector as a string, not a list of arguments;
$(joined_id_0+', '+joined_id_1+', '+joined_id_2+', '+joined_id_3).wrapAll('<div>');
Or even better, replace all of:
var joined_id_0 = String.concat(front_name,id_coll[0]);
var joined_id_1 = String.concat(front_name,id_coll[1]);
var joined_id_2 = String.concat(front_name,id_coll[2]);
var joined_id_3 = String.concat(front_name,id_coll[3]);
$(joined_id_0,joined_id_1,joined_id_2,joined_id_3).wrapAll('<div>');
With:
$('#'+id_coll.join(', #')).wrapAll('<div>');
And remove the line: var front_name = '#';
You have to concatenat the ids, separated by a comma, as in #id1, #id2, ....
You can do that this way:
[joined_id_0,joined_id_1,joined_id_2,joined_id_3].join(',')
The whole line:
$([joined_id_0,joined_id_1,joined_id_2,joined_id_3].join(',')).wrapAll('<div>');
If it doesn't works, check the what is returned by [joined_id_0,joined_id_1,joined_id_2,joined_id_3].join(',') (alert() it, or use console.log).

Categories