why do the loop not writing expected output in the dom 15 times
I am trying to create an elemnt div 15 times with another div child
here is the code
for(let i=1;i<=15;i++){
let smalldiv=document.createElement("div").textContent=i;
let pr =document.createElement("div");
pr.textContent="product";
};
smalldiv.appendChild(pr);
let smalldiv=document.createElement("div").textContent=i;
doesn't set smalldiv to the DIV. When you write
let x = y = z;
it's equivalent to
y = z;
let x = y;
so you're setting smalldiv to the textContent that you assigned i to.
And even if you did set smalldiv to the DIV, you never append it to the DOM, so you wouldn't see the results.
Since you're appending pr to smalldiv after the loop, you're just appending the last pr. But that won't work either, because the scope of pr is the loop body, so you can't refer to it after the loop is done. You should append it inside the loop.
Don't use a DIV for numbered items, use an ordered list <ol>, with <li> elements within it.
let ol = document.createElement("ol");
for(let i=1;i<=15;i++){
let pr =document.createElement("li");
pr.textContent="product";
ol.appendChild(pr);
};
document.body.appendChild(ol);
First create the div smalldiv, then add the textContent.
Then you need to append the elements to the DOM using an element already in the DOM or append to the document.body.
for(let i=1;i<=15;i++){
// create the div and assign the variable
let smalldiv=document.createElement("div");
// set the textContent once created
smalldiv.textContent = `${i}. `;
// create new div 'pr'
let pr = document.createElement("span");
// assign its content
pr.textContent="product";
// append pr to smalldiv
smalldiv.append(pr);
// append smalldiv to the body
document.body.append(smalldiv);
};
Related
I have a simple forEach loop in which I'm trying to append list items to an unordered list. However, when I run the script, I'm only seeing one list item being added. Can anyone explain why this is happening?
JS
let bookElement = document.getElementsByClassName("book");
let navElement = document.getElementsByTagName("nav")[0];
let unorderedList = document.createElement("UL");
let listItems = document.createElement("LI");
let book1 = new Book();
let book2 = new Book();
let booksArray = [book1, book2];
createNavigation(booksArray, navElement, unorderedList, listItems);
function createNavigation(booksArray, navElement, unorderedList, listItems){
console.log(booksArray)
booksArray.forEach(function(book){
unorderedList.appendChild(listItems);
})
navElement.appendChild(unorderedList);
}
HTML
<body>
<nav></nav>
<div class="book"></div>
</body>
The log in the function is returning that there are two objects in the array.
You only ever create one list item.
You then append it in multiple places.
Since an element can't exist in multiple places at the same time, each time you append it (after the first) you move it.
Use let listItems = document.createElement("LI"); inside your loop.
Yes, there are two objects in the array, but you're adding the same element to the unorganised list twice. So, the second time it's added, the browser sees that the element is already there and doesn't add it again.
A better approach would be to simply create a new li element in each iteration of the loop:
function createNavigation(booksArray, navElement, unorderedList){
console.log(booksArray)
booksArray.forEach(function(book){
let listItems = document.createElement("LI");
unorderedList.appendChild(listItems);
})
navElement.appendChild(unorderedList);
}
Notice that I've removed the listItems parameter as the value passed into it is now unused.
You can take your program a step further, now, by doing something along these lines:
function createNavigation(booksArray, navElement, unorderedList){
console.log(booksArray)
booksArray.forEach(function(book){
let listItems = document.createElement("LI");
listItems.innerHTML = book.title;
unorderedList.appendChild(listItems);
})
navElement.appendChild(unorderedList);
}
That will create a list of book titles, assuming you have titles stored in a title property in your Book constructor.
I've got the following simplified code to a problem I was facing yesterday when I've asked this question:
Problem of JavaScript previous object inside an object is being overwritten [EDITED]
The simplified code is:
HTML
<div class="header"><button>click me to add new to DOM and Object</button></div>
<div class="list"></div>
Javascript
const list = document.querySelector(".list");
const button = document.querySelector('button');
const obj = {
counter: 1
}
function html(counter) {
return `<div>I am Item number ${obj.counter}</div>`;
}
function addItem() {
//add new element to the DOM
list.innerHTML += html(obj.counter);
//add new object to the object
obj[`ITEM-${obj.counter++}`] = [...list.querySelectorAll("div")].slice(-1)[0];
console.log(obj);
}
button.addEventListener("click", addItem);
The problem is:
I'm console.logging the "obj" to see it's content,
I want you to look at the console after some clicks on the button, you'll see this output
{counter: 6, ITEM-1: div, ITEM-2: div, ITEM-3: div, ITEM-4: div, …}
ITEM-1: div
ITEM-2: div
ITEM-3: div
ITEM-4: div
ITEM-5: div
counter: 6
__proto__: Object
The question is:
Why only the last item from inside the object is indicating the HTML code from inside the dom while the other previous items are no longer indicating the HTML elements?
to try what I'm trying to say:
please, inside the console, hover on the last item from the object, in my case, it's ITEM-5: div,
you'll see how the item on the DOM is being highlighted.
but now try to hover on previous items from inside the object, for example in my case
ITEM-1: div
it's not being highlighted on the DOM.
what is the problem?
Never use += on .innerHTML.
It is precisely equivalent to:
element.innerHTML = element.innerHTML + content
In other words it requires that the existing content of the element be serialised into HTML, and then you add your content to that string, and then the entire thing is re-assigned to the node (which has to be de-serialised back onto DOM format again), also forcing the previously-existing child nodes to be recreated.
When you are using innerHTML to add an element you are basically erasing all the elements and creating new ones. So when you create a reference to the element, the element is removed when the innerHTML is replaced.
Typically your question is asked as "Why do my click event handlers only work on the last element when I add to the list?"
Looking at the code you are doing
var current = list.innerHTML; // string of existing HTML
var newString = html(obj.counter); // String of new HTML
var updated = current + newString; // Combine to make a new string
list.innerHTML = updated; // redraw the DOM with the new string
When you change the innerHTML directly, it does not figure out what changes in the string, it just clears the "whiteboard" and redraws it all. If you do not what to redraw the whole element, you need to get smarter and instruct it to what is being added.
You should be appending a new element, not building it as a string.
const list = document.querySelector(".list");
const button = document.querySelector('button');
const obj = {
counter: 1
}
function elem(counter) {
var div = document.createElement("div");
div.innerHTML = `I am Item number ${obj.counter}`;
return div
}
function addItem() {
var newDiv = elem(obj.counter)
list.appendChild(newDiv);
obj[`ITEM-${obj.counter++}`] = newDiv;
console.log(obj);
}
button.addEventListener("click", addItem);
<div class="header"><button>click me to add new to DOM and Object</button></div>
<div class="list"></div>
I have defined two variables, one is the last element of an array, the second is the first element of a child array. Individually each variable return the element but when I chain/join(?) them it returns the object.
How can I target the desired element using the variables I have set?
//define element
let lineNumber = document.getElementsByClassName('lineNumber');
//get last item in array
let lastLine = lineNumber[lineNumber.length - 1];
//define element
let addButton = document.getElementsByClassName('addLine');
//target specific instance in array (
addButton = addButton[0];
//how do I join the two?
let lastButton = lastLine
+ addButton;
lastButton.style = "visibility: visible;";
console.log(lastLine); //this show the element
console.log(addButton); //this shows the element
console.log(lastButton ); //this shows the object, should be the first `add button` of the last item on the `lastline` array.
From the lastLine that you've selected, call querySelector on it to get to the first .addLine inside it:
const addButton = lastLine.querySelector('.addLine');
If you want to retrieve all .addLines inside, use querySelectorAll instead:
const addButtonsInsideLastLine = lastLine.querySelectorAll('.addLine');
There is one similar question, but I still not understand why I can't do the following:
var numLoaded = document.createElement('span')
numLoaded = document.createElement('span')
numLoaded.style= "color:red;font-weight:bold";
numLoaded.innerHTML = " Number of loaded results: "+0;
$("#resultsCounter").append(numLoaded)
for (var i = ((json.length)-1); i >= 0; i--) {
newDiv = document.createElement("div");
newDiv.id = +i+1;
newDiv.innerHTML = newDiv.innerHTML+"..."
numLoaded.innerHTML = " Number of loaded results: "+newDiv.id;
$("#resultsCounter").empty(); // should remove my span
$("#resultsCounter").append(numLoaded) // should add my span with new content
$("#results").prepend(newDiv)
}
After this all as output I see only last added element after whole cycle ends. During cycle iteration it appends to DOM no child, even if it is already created. I want to append always the same element <span> but in every iteration with updated number.
Why child element is added to DOM only after cycle ends? How to update it in cycle? Do I need to recreate that element after every loop iteration? It is way to speed up this process?
It's because you only create the element once, before the list. Then in the loop you are changing the innerHtml of the element, and adding it - but because it is the same element (the same JS object being referenced each time), it's effectively overwriting itself.
You just need to move these lines which create the element:
var numLoaded = document.createElement('span')
numLoaded = document.createElement('span')
numLoaded.style= "color:red;font-weight:bold";
numLoaded.innerHTML = " Number of loaded results: "+0;
inside the loop.
I am very new to coding. Below is a piece of JS code i am struggling to understand:
var btnContainer = document.getElementbyId(“linkcontainer”);
var btns = btnContainer.getElementsbyClassName(“btn”);
for (var i = 0; i < btns.length; i++){
btns.addEventListener(“click”, function(){
var current = document.getElementsbyClassName(“active”);
current[0].className = current[0].className.replace(‘active’, “”);
this.className += ‘active’;
});}
What difference does the [i] make in
btns[i].AddEventListener??
What is it exactly and what if there was no “i” in between the brackets? Also current[0]. It’s probably a stupid question, but please help me understand.
First of all there are no stupid question but only stupid answers.
In your code you get a list of DOM elements stored in an array called 'btns', then you iterate it with a loop.
So btns[i] allow you to retrieves the elements at the i position (It's important to note that array start at 0 in Javascript).
Example:
var fruits = ['Apple', 'Banana'];
console.log(fruits[0])
console.log(fruits[1])
So if you don't use the [i] you will iterate on the array itself and not on the element stored in it.
As the name of the method getElementsByClassName suggests, this queries the DOM and returns an array like object that contain multiple elements with the class name that was specified.
btns - will be an array that contains one or more elements.
To access a specific element from the array, you access it using the index of the current iteration.
btns[1] - Gives you access to the 2nd element in the list.
addEventListener - is used to bind a event handler to a single element. You cannot directly use this on array of objects.
// query the DOM for element with id - linkcontainer
var btnContainer = document.getElementbyId(“linkcontainer”);
// query the DOM for elements with className - btn
// This can return multiple elements, so btns will be
// as array like object of elements
var btns = btnContainer.getElementsByClassName(“btn”);
// iterate over the array that was just queried for
for (var i = 0; i < btns.length; i++) {
// bind a click event for each element in the array
btns[i].addEventListener(“click”, function() {
// query the dom for elements with className - active
var current = document.getElementsByClassName(“active”);
// access the first element and replace the active class
current[0].className = current[0].className.replace(‘active’, “”);
// add the active class to the element that was clicked
this.className += ‘active’;
});
}
The way I see it you will have to remove the active class for all the elements instead of just the first entity. A slightly better way to improve this code would be is
var btnContainer = document.getElementbyId(“linkcontainer”);
var btns = btnContainer.getElementsByClassName(“btn”);
btns.forEach(function(btn) {
btn.addEventListener(“click”, function() {
// query the dom for elements with className - active
var current = document.getElementsByClassName(“active”);
current.forEach(function(elem) {
elem.classList.remove('active');
});
this.classList.add('active');
});
});
As the other posters have mentioned, your code var btns = btnContainer.getElementsbyClassName(“btn”); should return an array of DOM elements so in your for loop, btns[i] will retrieve the specific element at index i of btns as i goes from 0 to btns.length. Removing the i will retrieve the entire array on each iteration.
To your second question: current is exactly the same thing as btns, an array of DOM elements so current[0] will retrieve the first element in this array.