<script>
var c = document.body
var e = document.createElement("div");
e.className = "box";
for (var i=0; i<=2; i++) {
c.appendChild(e);
}
</script>
When I run this for loop, only one div is created in the browser. However,
if I add a console.log(e) into the loop and run it on a console, (i.e)
<script>
var c = document.body
var e = document.createElement("div");
e.className = "box";
for (var i=0; i<=2; i++) {
console.log(e);
c.appendChild(e);
}
</script>
the output is
"<div class='box'></div>"
"<div class='box'></div>"
"<div class='box'></div>"
I know that I can correct the issue by declaring the variables inside the for loop, but I'm not sure why the first code above would not work?
I suppose this entry from MDN covers that:
The Node.appendChild() method adds a node to the end of the list of
children of a specified parent node. If the given child is a reference
to an existing node in the document, appendChild() moves it from its
current position to the new position (there is no requirement to
remove the node from its parent node before appending it to some other
node).
This means that a node can't be in two points of the document
simultaneously. So if the node already has a parent, the node is first
removed, then appended at the new position
It's what you expect of a variable.
e is a reference to the created div, not the actual constructor. So appendChild can only add the element once to the DOM, otherwise it would replace it to the new position, since an element is unique within the DOM. Of course console.log will list the same reference trice.
The solution is, as you said, to create a different element within the loop:
for (var i=0; i<=2; i++) {
const e = document.createElement("div");
c.appendChild(e);
}
document.window.appendChild(c);
<script>
var div = "<div class='box'></div>";
for (var i = 0; i <= 2; i++) {
document.queryselector("body").innerHTML += div
}
</script>
Related
need some help over here.
I create and than, store DOM elements in a array.
Later, when i want to use them, they are like unusable, except for the last one.
Here is a realy simple code to illustrate :
http://jsfiddle.net/rd2nzk4L/
var list = new Array();
// first loop create and store elements
for (i = 1; i <= 5; i++) {
var html = '<div id="test-' + i + '" style="float: left; border: 2px solid black; width: 50px; height: 50px; margin: 5px;">' + i + '</div>';
document.getElementById('test').innerHTML += html;
var element = document.getElementById('test-' + i);
//console.log(element);
list.push(element);
}
// second loop use stored elements
for (index in list) {
//console.log(list[ index ]);
list[ index ].style.backgroundColor = 'yellow';
}
You can see only the last element became yellow.
I'll really appreciate if someone have an idea.
Thanks!
Setting the innerHTML of the container will recreate its entire DOM tree.
Therefore, all of the old elements from the previous iteration are no longer attached to the DOM.
You should only set innerHTML once.
because you aren't creating the elements, so they can't be found with the selector you are using. Try using document.createElement, then div.appendChild(element)
Each time you set the innerHTML of the container, it destroys all of the child elements and recreates them. This is a problem because now your array contains references to elements that no longer exist, apart from the final iteration.
The solution is to work with DOM elements, not raw HTML.
Fiddle
var list = [];
var container = document.getElementById("test");
// first loop create and store elements
for (var i = 1; i <= 5; i++) {
var element = document.createElement('div');
element.id = 'test-' + i;
element.className = 'mydiv';
element.appendChild(document.createTextNode(i));
container.appendChild(element);
list.push(element);
}
// second loop use stored elements
for (var x=0; x<list.length; x++) {
list[ x ].style.backgroundColor = 'yellow';
}
Side notes:
for..in can be used on arrays, but you need to use hasOwnProperty, or just use a basic for loop like above.
var list = \[\] is preferred to var list = new Array() – more info.
Remember to declare count variables like i using var to make them locally scoped, otherwise they will be scoped globally.
I am outputting the values of an array with a for loop by using innerHTML +=, in order to add to the contents of a current div- let's call it div1 (known as currentQuestions in the code).
The function which outputs the array values is to be used multiple times, as the values stored within the array change from user input. This means that div1 will have a growing list of output.
However the problem is that for each group of data iterated by the array, i need them to be stored together in their own div (within div1), separate from other groups generated by a different call of the same function.
they dont need to have unique IDs, but they are needed to call a function onclick which will affect only the div that is clicked.
This is what i have so far:
arrayLength = answers.length
for (var j = 0; j < arrayLength; j++)
{
document.getElementById('currentQuestions').innerHTML += answers[j] + '<br />'
}
As you can see, all i have is the for loop so far. I cant add a div to the innerHTML because then it will create a div for each iteration rather than one div for the full iteration of the array.
I'm out of ideas. Thoughts?
Create the div and attach the event handler before the loop, something like this:
var subdiv = document.createElement('div'), // Creates a new element to the DOM
arrayLength = answers.length;
subdiv.addEventListener('click', function () {...}); // Attach a click listener
for (var j = 0; j < arrayLength; j++) {
subdiv.innerHTML += answers[j] + '<br />'; // Add some content to newly-created DIV
}
document.getElementById('currentQuestions').appendChild(subdiv); // Append the newly-created element to the document.
Make your life easier and try jQuery...
This example would take the html within a div they clicked on and append it to the #currentQuestions div...
$('body').on('click', '.class_of_div_they_click_on', function(event) {
var div_content = $(this).html();
$("#currentQuestions").append(div_content + '<br>');
});
I have an array of class names that I want to search a page for. Then I'd like to find all those elements, grab the text inside, and append it to a div.
var div = document.createElement('div');
div.innerHTML = 'List of names from this page';
document.body.appendChild(div);
var classNameArray = ['user', 'username', 'fullname', 'profile-field', 'author', 'screen-name'];
for (var i = 0; i < classNameArray.length; i++) {
element = classNameArray[i];
getSuggestedAuthors(element);
function getSuggestedAuthors(element) {
var classes = document.getElementsByClassName(element);
var index;
for (var index = 0; index < classes.length; index++) {
var class = classes[index];
var textInsideClass = class.innerHTML;
div.appendChild(textInsideClass);
}
}
}
When I run this, it gives me:
Uncaught NotFoundError: An attempt was made to reference a Node in a context where it does not exist.
I believe the problem is occuring at var textInsideClass = class.innerHTML, because when I remove that, it simply grabs all the classes and appends them to the div. However, I'd like to only get the text inside the class.
Anyone know how to do this without jQuery? I'm injected this hs through Google Chrome's executeScript injection.
Thanks so much!
I think your issue is that appendChild only works with nodes. You might be better off just appending to innerHTML using something along the lines of a.innerHTML += f.innerHTML.
You should also be sure to move the getSuggestedAuthors function out of the loop. It works ok as it is, but it's much better form not to declare functions inside a loop.
If you only need to support chrome then all of the handy methods on the Array.prototype should exist :)
var a = document.createElement('div');
a.innerHTML = 'List of names from this page';
document.body.appendChild(a);
function getSuggestedAuthors(elements) {
for (var d = 0; d < elements.length; d++) {
a.appendChild(document.createTextNode(elements[d].innerText));//append loop items text to a
}
}
['user', 'username', 'fullname', 'profile-field', 'author', 'screen-name'].map(function(cls) {
return document.getElementsByClassName(cls);
}).forEach(getSuggestedAuthors);
I'm trying to load X amount of <li>'s into a <ul> via a for loop in a jquery function, and while I think I've got the syntax about right I'm not getting anything loading. (no problem with loading a single <li>, but none for multiples with the method I've tried)
Initially I attempted to pass a variable into the loop to determine the amount of increments: var peekListAmount = 5;
That didn't work so I went for a bog-standard loop incrementer. That doesn't work either so, after searching here and getting close, I have put together a fiddle to see if someone can point out what I'm doing wrong: http://jsfiddle.net/janowicz/hEjxP/8/
Ultimately I want to use Knockout.js to dynamically input a number to pass to the loop amount variable, but 1st things 1st.
Many thanks in advance.
When you do:
var peekListItem = $('<li>...</li>');
you're creating a single instance of an <li> node, encapsulated in a jQuery object.
Appending an already-present node to the DOM just removes it from its current place in the DOM tree, and moves it to the new place.
You need to create the node inside the loop, not outside, otherwise you're just re-appending the same node each time, not a copy of that node.
In fact, given you're not manipulating that node, you can just put the required HTML directly inside the .append() call without wrapping it in $(...) at all:
$(function() {
var peekList = $('<ul class="peekaboo-list">').appendTo('div.peekaboo-wrap');
function addLiAnchorNodes(nodeAmount) {
var html = '<li>' +
'<p class="peekaboo-text"></p></li>';
for (var i = 0; i < nodeAmount; ++i) {
peekList.append(html);
}
}
addLiAnchorNodes(5);
});
See http://jsfiddle.net/alnitak/8xvbY/
Here is you updated code
$(function(){
var peekList = $('<ul class="peekaboo-list"></ul>');
var peekListItem = '<li><p class="peekaboo-text"></p></li>';
//var peekListAmount = 5;
var tmp = '';
var addLiAnchorNodes = function (nodeAmount){
//var nodeAmount = peekListAmount;
for (var i = 0; i < 10; i++){
tmp += peekListItem;
}
peekList.append(tmp);
$('div.peekaboo-wrap').append(peekList); // This bit works fine
}
addLiAnchorNodes();
});
This should work. Instead of appending the list item in each loop, append the list only once at the end.
$(function(){
var peekList = $('<ul class="peekaboo-list"></ul>');
peekList.appendTo('div.peekaboo-wrap');
var addLiAnchorNodes = function (nodeAmount){
var list = "";
for (var i = 0; i < 10; i++){
list += '<li>Sample<p class="peekaboo-text"></p></li>';
}
peekList.append(list);
}
addLiAnchorNodes();
});
Here is the updated fiddle
Try this:
$(function(){
var peekList = $('<ul class="peekaboo-list"></ul>');
$(peekList).appendTo('div.peekaboo-wrap'); // This bit works fine
var addLiAnchorNodes = function (nodeAmount){
//var nodeAmount = peekListAmount;
for (var i = 0; i < 10; i++){
var peekListItem = $('<li><p class="peekaboo-text"></p></li>');
peekListItem.appendTo(peekList);
}
}
addLiAnchorNodes();
});
I populate a list with search results appending li elements. I update DOM for each result.
for (var i = 0; i < topics.length; i++) {
$("#searchResults").append(
$("<li />")
.append(result.Name)
.addClass("example")
);
};
I want to make a group of li elements first and update DOM-tree just once.
I try something like this:
var list = $([]);
for (var i = 0; i < topics.length; i++) {
list.append(
$("<li />")
.append(result.Name)
.addClass("example")
);
};
$("#searchResults").append(list);
But div $("#searchResults") is empty.
Where is the problem?
Something like this should be much faster:
var ul = $("<ul />");
for (var i = 0, l=topics.length; i < l; i++) {
$("<li />", { text: result.Name, "class": "example" }).appendTo(ul);
};
$("#searchResults").append(ul.contents());
By using a document fragment ($("<ul />")) and appending to it, then appending at the end, we're not messing with the entire DOM each append. Also we're not repeatedly selecting #searchResults each loop...or checking .length would could also be expensive.
Note: this method still uses the DOM to create elements (as opposed to a string), eliminating issues of result.Name having HTML that could screw things up, etc.
Creating DOM elements on the fly will usually be slower than just using innerHTML (or a wrapper around that). Also, concatenating string with + will usually be slower than using Array.join('').
In the end, I suspect something like this would be the fastest:
var list = [];
for (var i = 0; i < topics.length; i++)
list.push("<li class=example>",topics[i].Name,"</li>");
$("#searchResults").html(list.join(''));
Try just using a string. Add all your li's to a string and then put them into the innerHTML of the searchResults div.
var list = '';
for (var i = 0; i < topics.length; i++) {
list +="<li class=example>" + result.Name + "</li>";
}
$("#searchResults").innerHTML = list;
If you are looking for efficacy this is probably better because you are not using the DOM engines a lot. (although unless you are adding hundreds of li's it is probably insignificant anyway.
All previous solutions still suffer from recalculating, painting and layout for every single element we add.
Instead of appending the elements directly to the document when they are created, append them to the DocumentFragment instead, and finish by adding that to the DOM.
var el;
var i = 0;
var fragment = document.createDocumentFragment();
while (i < 500) {
el = document.createElement('li');
el.innerText = '1ist ' + i;
fragment.appendChild(el);
i++;
}
div.appendChild(fragment);