Best practice for appending child text in a for loop - javascript

Here i create a span element for an icon and then add a class to hide it (until hovered). I do the same for a second span that will hold a tooltip and adds CSS to style it.
I then loop through shipCell which is a column of cells, and append the btnSpan and the tipSpan. while adding the icon (a clipboard) using .inerHTML and adding the text "Copy ShipID" to the tipSpan using .innerText.
var btnSpan = document.createElement("span");
btnSpan.classList.add('quickLink'); // Add class to all quickLinks
var tipSpan = document.createElement("span");
tipSpan.classList.add('tooltip'); // Add class to all tooltips
for(var i=0;i<shipCell.length;i++){
btnSpan.innerHTML = 'đź“‹';
shipCell[i].appendChild(btnSpan.cloneNode(true)); //add button to each cell
tipSpan.classList.add('copytip'); //add class for copy buttons specifically
tipSpan.innerText = 'Copy ShipID';
shipCell[i].appendChild(tipSpan.cloneNode(true)); //add tool tip to each cell");
};
This all works as it should (especially after learning of cloneNode(true) which allows it to be appended to every cell rather than just the first.
My question here is would it be a smarter move to create text nodes for the things I used .innerHTML on? If so, how? Because i tried creating a text node and appending it in the loop but it would add a new copy EVERY time, so the second cells tooltip said "Copy ShipIDCopy ShipID" (yes, twice)

Well, while your existing code is not incorrect, most code in the for loop is unnecessary and can be cut down to:
var btnSpan = document.createElement("span");
btnSpan.classList.add('quickLink'); // Add class to all quickLinks
btnSpan.innerHTML = 'đź“‹'; // Moved this out from loop
var tipSpan = document.createElement("span");
tipSpan.classList.add('tooltip'); // Add class to all tooltips
tipSpan.innerText = 'Copy ShipID'; // Moved this out from loop
for (var i = 0; i < shipCell.length; i++){
shipCell[i].appendChild(btnSpan.cloneNode(true)); //add button to each cell
shipCell[i].appendChild(tipSpan.cloneNode(true)); //add tool tip to each cell");
};
Because by using .cloneNode(true), you are also cloning all its child nodes inside. Meaning, you don't have to keep assigning .innerHTML and .innerText because by using true in .cloneNode(), they will be cloned too.
You will only need to reassign them IF you use .cloneNode(), without true argument. However, in that case, your existing code will need some minor tweaking.
var btnSpan = document.createElement("span");
btnSpan.classList.add('quickLink'); // Add class to all quickLinks
var tipSpan = document.createElement("span");
tipSpan.classList.add('tooltip'); // Add class to all tooltips
var btnSpanClone,
tipSpanClone;
for (var i = 0; i < shipCell.length; i++){
btnSpanClone = btnSpan.cloneNode(); // New btnSpan element without childNodes
tipSpanClone = tipSpan.cloneNode(); // New tipSpan element without childNodes
btnSpanClone.innerHTML = 'đź“‹';
tipSpanClone.innerText = 'Copy ShipID';
shipCell[i].appendChild(btnSpanClone);
shipCell[i].appendChild(tipSpanClone);
};
As for your question of whether it is smarter to use .createTextNode(), it makes no difference in this specific case because there is nothing else to be added to your btnSpan and tipSpan. If there is, then it MIGHT be better to use it for better code organisation and readability.
The most important thing is to make your code easily understood by anyone.
So if you think using .createTextNode() can make the code more readable and easier to understand, then use it. If not, then don't.
If you are talking about code performance here, you can compare both implementation yourself using jsPerf or some other JS benchmark tools. Most likely, the performance difference will be very tiny and can be ignored.

Related

How can I add new radio buttons without unchecking old ones? [duplicate]

I have a drop down which builds a form based of the selections that are selected. So, if someone selects 'foobar', it displays a text field, if they choose 'cheese', it displays radio buttons. The user can then enter data into these forms as they go along. The only problem is that when they add a new form element, all the rest of the information is erased. Im currently using the following to do add to the form:
document.getElementById('theform_div').innerHTML =
document.getElementById('theform_div').innerHTML + 'this is the new stuff';
How can I get it to keep whatever has be enetered in the form and also add the new field to the end?
Setting innerHTML destroys the contents of the element and rebuilds it from the HTML.
You need to build a separate DOM tree and add it by calling appendChild.
For example:
var container = document.createElement("div");
container.innerHTML = "...";
document.getElementById("theform_div").appendChild(container);
This is much easier to do using jQuery.
Step One:
Add jQuery to your headers:
<script type=”text/javascript” src=”http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js”></script>
Step Two:
Append, don't replace, data to your DIV like this:
$("#theform_div").append("your_new_html_goes_here");
Don't use innerHTML to create the form elements. With innerHTML you're overwriting the old HTML with new HTML which will recreate all the elements. Instead you need to use the DOM to create and append the elements.
EXAMPLE
function addRadioElement()
{
var frm = document.getElementById("form_container");
var newEl = document.createElement("input");
newEl.type = "radio";
newEl.name = "foo";
newEl.value = "bar";
frm.appendChild(newEl);
}
The most correct way to do it without using a framework (like jQuery, Dojo, YUI) is:
var text = document.createTextNode('The text you want to write');
var div = document.createElement('div');
div.appendChild(text);
document.getElementById('theform_div').appendChild(div);
innerHTML, although supported by most browsers, is not standard compliant and - therefore, not guaranteed to work.
I would suggest using jQuery and its append function.

js - appendChild Method spoiling the whole page

I am making a simple web app. In one part of it, I have a dynamically generated list:
which is achieved with:
for(var i=0; i<goalsObj.length; i++){
var node = document.createElement("li");
node.setAttribute("class", "list");
node.setAttribute('id','g'+i);
var checkbox = document.createElement("input");
checkbox.setAttribute("type","checkbox");
checkbox.setAttribute("class", "tickbox");
node.appendChild(checkbox);
var textnode = document.createTextNode(goalsObj[i]);
node.appendChild(textnode);
document.getElementById("sortable").appendChild(node);
}
And I have a jQuery function executed when any item on the list is clicked, to app a Calendar below it.
which is achieved with:
var cal = document.createElement("p");
cal.innerHTML=calendar_html;
document.getElementById($(this).attr('id')).appendChild(cal);
Anyhow, I am getting a very shabby output:
What's wrong? What should I do? How do I make the pre-existing elements(all made dynamically) to make way for the newly created ones?
Generate the whole content at once and not in parts. Hide the content that you do not want on initialization of the page. Write a function to show the hidden content when the list items are clicked.
Paragraph ("p" element) is for organization of information into paragraphs.
http://www.w3.org/TR/html401/struct/text.html#h-9.3
I suppose you should try to use div instead of p
var cal = document.createElement("div");
cal.innerHTML=calendar_html;

Change DIV in one remove/append

Here is what I am doing, I create a div structure dynamically
var divt = $('<div id="toplevel"></div>');
divt.append('<div id="child1"><div class="content1"></div></div>');
divt.append('<div id="child2"><div class="content1"></div></div>');
I then store a clone to a separate node and add it to the DOM.
this.emptydivt = $(divt).clone();
maindiv.append(divt);
Later on in my code I want to replace the contents of the top level DIV using one remove/append.
Firstly I create a new node from my previously saved...
this.newdivt = this.emptydivt.clone();
Here I want to get the child divs and append some new content , I have tried various function but can't get it to work eg.
var childdiv = this.newdivt.find('#child1').html();
childdiv.append('<div> new content</div>');
then after I will replace the top level div
maindiv.remove('#toplevel');
maindiv.append(this.newdivt);
So is there a way to get the child div from a Jquery or JS node that is not in the DOM ?
I see 2 typos in your code, not sure if on your real code they are not present
this.newdivt = this.emptydivt.clone(); // you missed ()
and
var childdiv = this.newdivt.find('#child1').html(); // you missed '' around #child1
Also, this code is not valid as clone doesn't accept a jQuery object as parameter
this.emptydivt = $(eventGrid).clone(divt);
Maybe you just want divt.clone()?
EDIT
Remove all white spaces on your elements ids as white spaces are not allowed on id attribute.

Swap the constant of td using jquery

I want to swap the content of two td's with each other by clicking on move up button.
I mean I want to swap content between the 2nd td and 3rd td.
I got that done but I am facing little problem in that i.e. "the swapped td's are not toggling the class that shows the current td is clicked or not after the swapping."
I am using this code for swapping the content of td's as below
var currentTr = $("#selectedTab td.backgroundcolor").parent();
var previousTr = currentTr.prev();
var temp = currentTr.html();
$(currentTr).html(previousTr.html());
$(previousTr).html(temp);
HTML manipulation in the DOM can be destructive. You should instead move the DOM nodes themselves.
var currentTr = $("#selectedTab td.backgroundcolor").parent();
var previousTr = currentTr.prev();
var temp = currentTr.contents().detach();
currentTr.append(previousTr.contents());
previousTr.append(temp);
This way you're not serializing, destroying and rebuilding all the nodes. You're just moving them.

How to remove an individual text node in a div without removing all of them?

How do I go about removing an individual dynamically created text node?
I am generating input's on the fly and am using .createTextNode to place descriptive text before the elements. I need the ability to delete specific elements that are being created and am using .removeChild to do it. That works fine for removing an individual input because I have something to reference (id/name). Is there a way to set some sort of reference to each text node so I can delete it along with its corresponding input control?
var box = document.getElementById("myDiv");
box.appendChild(document.createTextNode('Status: '));
var inp = document.createElement('input');
inp.type = 'text';
// add attributes, etc...
box.appendChild(inp);
Why not wrap both in a fieldset to begin with?
var box = document.getElementById("myDiv");
var field = document.createElement('fieldset');
field.appendChild(document.createTextNode('Status: '));
var inp = document.createElement('input');
inp.type = 'text';
// add attributes, etc...
field.appendChild(inp);
box.appendChild(field);
This way just removing the field element will remove both your textnode and your input at the same time.
Keep reference to it:
var txt = document.createTextNode('Status: ');
box.appendChild(txt);
and then remove with:
txt.parentNode.removeChild(txt);
If node is supposed to be immediately before the input, this will work as well:
inp.parentNode.removeChild(inp.previousSibling);
It should work if you don't use innerHTML or normalize(), which could cause nodes to be re-created or merged, invalidating your references.
In case you wanted to remove arbitrary text from a node, there's textnode.splitText(offset).
Do not remove text nodes that are part of a list of textnodes in the DOM. Even if you reference them (before you appended them to the DOM).
The browser may merge multiple text nodes! I am not sure what the standards state, but its possible - at least some experience told me.. (may be old browsers, but it was the case).
Instead of that, you could either wrap each text node in a span or div tag, or you could use some kind of text replacements. I'd prefer the former.
If you change this line:
box.appendChild(document.createTextNode('Status: '));
To:
var textNode = document.createTextNode('Status: ');
box.appendChild(textNode);
Then you can refer to the textNode var later to delete it. Be careful, though--some browsers will merge adjacent text nodes.
var box = document.getElementById("myDiv");
var label = document.createTextNode('Status: ')
box.appendChild(label);
var inp = document.createElement('input');
inp.type = 'text';
// add attributes, etc...
box.appendChild(inp);
// do other stuff
//remove the label
label.nodeValue = "";

Categories