Store DOM elements in array : strange behaviour? - javascript

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.

Related

page doesn't display anything

so I wrote a script to display 5 random arrays, but the page doesn't display anything.
here's the code:
<html>
<head>
<script>
function start(){
var arr(5),result;
result=document.getElementById("arraying");
result="<p>";
for(var i=0; i<5;i++){
arr[i]=Math.floor(Math.random()*10);
result+="arr["+i+"]= "+arr[i]+"</p><p>";
}
result+="</p>";
}
window.addEventListener("load",start,false);
</script>
</head>
<body>
<div id="arraying"></div>
</body>
</html>
I tried removing result=document.getElementById and write document.getElementById.innerHTML=result in the end of the function but didn't work. what's the error?
You cannot use the same variable for different purposes at the same time. First you assign a DOM element to result, and immediately on the next line you overwrite result with a string.
Build a string htmlStr inside your loop, and when that is done, assign this string to result.innerHTML property:
function start() {
let arr = [],
result, htmlStr = '';
result = document.getElementById("arraying");
htmlStr += "<p>";
for (let i = 0; i < 5; i++) {
arr[i] = Math.floor(Math.random() * 10);
htmlStr += "arr[" + i + "]= " + arr[i] + "</p><p>";
}
htmlStr += "</p>";
result.innerHTML = htmlStr;
}
window.addEventListener("load", start, false);
<div id="arraying"></div>
Looking at the code you seem to be missing some basic javascript concepts.
array size
This is probably your main issue:
var arr(5)
This does not make sense in javascript. Array length does not need to be predefined since all arrays are of dynamic length. Simply define an array like this:
var arr = []
Then later when you want to append new elements use push like this:
arr.push( Math.floor(Math.random()*10) )
adding html using innerHTML
There are different ways to dynamically inject html into your page. (It looks like) you tried to append the html as a string to the parent element. This is not possible.
You said you tried using innerHTML. That should work if used correctly.
A working implementation would work like this:
function start() {
var arr = []
var result = "<p>"
for(var i = 0; i < 5; i++) {
arr.push( Math.floor(Math.random()*10) ) // Btw this array isn't actually needed.
result += "arr[" + i + "] = " + arr[i] + "</p><p>"
}
document.getElementById("arraying").innerHTML = result
}
window.addEventListener("load", start, {passive: true});
adding html using createElement
A generally better way of dynamically adding html elements is via createElement.
This way you dont have to write html and are therefore less prone for making errors. It is also more performant and easier to integrate into javascript.
I think the best explaination is a commented implementation:
function start() {
var myDiv = document.getElementById("arraying") // get parent node
var arr = []
for(var i = 0; i < 5; i++) {
arr.push( Math.floor(Math.random()*10) )
var p = document.createElement("p") // create p element
p.innerText = "arr[" + i + "] = " + arr[i] // add text content to p element
myDiv.append(p) // append p element to parent element
}
}
window.addEventListener("load", start, {passive: true});
small tips
The let keyword works mostly the same as the var keyword, but is generally preferred because of some edge cases in which let is superior.
Fusing strings and variables using the plus operator is generally considered bad practice. A better way to do the string concatenation would have been
result += `arr[${i}] = ${arr[i]}</p><p>`

how can i create an image array, each with a unique id using getElementById in javascript?

Here is the code I have but when I try to use the image reference its value is null. I want to be able to use it to access another image in a table and change the src property to one in the preloadImages array. I have already made the table but I can't get a reference to the individual elements of the preloadImages array. I want each image to have a unique ID using document.getElementById('id') Please help!! Much appreciated!
var preloadImages = new Array();
SIZE = 52;
for(var h=0; h<SIZE; h++){
preloadImages[h] = new Image();
preloadImages[h].src = h+'.png';
preloadImages[h].height =100;
preloadImages[h].width = 70;
preloadImages[h].id = "cardImage"+h+"";
var cardImageRef = document.getElementById("cardImage"+h+"");
document.write(cardImageRef+'h'); //This line is for testing, it just returns null
}
You cannot use getElementById() unless the element exists in the DOM tree. This modification will add them to DOM:
for(var h=0; h<SIZE; h++){
preloadImages[h] = new Image(70, 100); // size here
preloadImages[h].src = h+ ".png";
preloadImages[h].id = "cardImage" + h;
document.body.appendChild(preloadImages[h]); // add to DOM, or some parent element
var cardImageRef = document.getElementById("cardImage" + h);
document.write(cardImageRef + "h"); //This line is for testing, it just returns null
}
However, since you already have the elements referenced in an array you could simply look the element up there using the index for the id (instead of using an id at all). This tend to be faster if your page layout has several elements.
var image = preloadImages[index];
If you where using images not identifiable by index you could do (but probably not relevant in this case):
function getImage(id) {
for(var i = 0; i < preloadImages.length; i++) {
if (preloadImages[i].id === id) return preloadImages[i];
}
return null;
}
So you could use it here, for example:
for(var h=0; h<SIZE; h++){
preloadImages[h] = new Image(70, 100); // size here
preloadImages[h].src = h + ".png";
preloadImages[h].id = "cardImage" + h;
}
document.write(getImage("cardImage0"));
The function document.getElementById() can only be used to reference elements that have already been added to the DOM.
However, if you wish to reference images by their id you can build an object instead and use the id as a key:
var preloadImages = {};
SIZE = 52;
for(var h = 0; h < SIZE; ++h) {
var i = new Image();
i.id = 'cardImage' + h;
i.src = h + '.png';
i.width = 70;
i.height = 100;
preloadImages[i.id] = i;
}
Afterwards you can refer to each image object by its id like so:
var img = preloadImages['cardImage2'];
// do something with image
document.body.appendChild(img); // add to page
There's no reason to use IDs here. In general, it's a "code smell" to construct IDs dynamically and then use getElementById to find them in your document. You can instead just work with the elements themselves.
After the code you posted runs, then preloadImages array contains the added elements. You don't need to use getElementById to find the element you just constructed! And as you found out, you can't use it at all until the element is inserted into the DOM, which you're not ready to do yet!
So in your table logic, you can simply insert the images you created directly using the references you already have in preloadImages:
function makeTable(images) {
var table = document.createElement('table');
var tr = document.createElement('tr');
table.appendChild(tr);
for (var i = 0; i < images.length; i++) {
var td = document.createElement('td');
tr.appendChild(td);
// put the images in the TD
td.appendChild(images[i]);
}
return table; // to be inserted somewhere
}
document.body.appendChild(makeTable(preloadImages));
I think you're getting confused because some people have gotten into the habit of thinking of IDs as the only way of referring to elements. They construct IDs by concatenating strings, then retrieve the element when they need it by using getElementById. In essence, they are using the document itself as a kind of global bag of things from which you retrieve things using ID as a kind of variable name. It's better in such cases to just work with the elements themselves, held in JavaScript variables or arrays.

While outputting an array with a for loop, how can i output all of the contents into a single, new div? javascript

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>');
});

Dynamically loading multiple <li>'s with a javascript for loop - nothing loading yet

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();
});

Optimization appending <li> into <ul> with jQuery

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);

Categories