Trying to append child to all elements with same class name - javascript

I am trying to use the appendChild() method to add a document.createElement("div") element to multiple <div> elements with the same class name.
I have tried this way:
const childElement = document.createElement("div");
childElement.className = "second";
const parentObject = document.getElementsByClassName("first");
[...parentObject].map(parent => parent.appendChild(childElement))
Which didnt work, so I tried:
for(let i = 0; i < parentObject.length; i++){
parentObject[i].appendChild(childElement);
}
The only way it worked was if I wrote the html element myself and then added it to the innerHTML of each parent:
[...parentObject].map(parent => parent.innerHTML = "<div class='second'></div>")
But since I am generating all different kind of HTML element tags, like IMG, DIV, SPAN I need the convenience of calling the createElement() method.
Has anyone any experience with this?

An element can only exist in one place in the DOM. What you need to do is create a new element to append to each parent:
const parentObject = document.getElementsByClassName('first');
[...parentObject].forEach((parent, i) => {
const childElement = document.createElement('div');
childElement.className = 'second';
childElement.innerHTML = `second ${i}`;
parent.appendChild(childElement)
});
div { padding: 5px; }
.first { background-color: pink; display: inline-block; }
.second { background-color: lightblue; }
<div class="first">first</div>
<div class="first">first</div>
<div class="first">first</div>
<div class="first">first</div>

Related

Re: How to target child nodes in HTML collection

I am new to programming and this is my first question. The problem I am having is I am trying to use DOM manipulation on all the child nodes of an html collection. I am expecting the nodes to change background color when they are hovered. Here is what I have tried so far:
let x = 0;
do{
const square = document.createElement("div");
square.className = "squares";
square.setAttribute("id","block");
document.getElementById("container").appendChild(square);
x++;
}
while(x < 16);
var container = document.getElementById("container");
var cells = container.childNodes;
cells.forEach(function(){
cells.onmouseover = function(){
cells.style.backgroundColor = "black";
}
});
console.log(`${cells.length}`);
This doesn't work even though console.log shows 16 child nodes being targeted.
var container = document.getElementById("container");
var cells = container.children[0];
cells.onmouseover = function(){
cells.style.backgroundColor = "black";
}
I have tried this and can use index but of course only that cell will change bg color. I want any cell that is hovered to change also.
I am at a loss for what I am doing wrong here. If anyone can point me in the right direction I would greatly appreciate it.
Welcome to Stack Overflow.
There is an issue in your forEach cycle. Consider the following:
cells.forEach(cell => {
cell.onmouseover = () => {
cell.style.backgroundColor = "black"
}
})
Note that you need to refer to cycle variable instead of the cells array.
Instead of attaching listeners to all the squares you can use event delegation and just have one listener on the container that captures the events from its children as they "bubble up" the DOM.
// Cache the container element, and add a listener to it
const container = document.querySelector('.container');
container.addEventListener('mouseover', handleMouse);
// Create some squares HTML by pushing template
// strings into an array
const html = [];
for (let i = 1; i < 10; i++) {
html.push(`<div class="square">${i}</div>`);
}
// Add that HTML to the container making sure
// we join the array of strings into one string
container.innerHTML = html.join('');
// When a event is fired check that it was
// was from an element with a square class
// and then add an active class to it
function handleMouse(e) {
if (e.target.matches('.square')) {
e.target.classList.add('active');
}
}
.container { display: grid; grid-template-columns: repeat(3, 50px); grid-gap: 0.2em; }
.square { font-size: 1.2em; padding: 0.7em 0.2em; background-color: #565656; color: white; text-align: center; }
.square.active { background-color: thistle; color: black; cursor: pointer; }
<div class="container"></div>
Additional documentation
Template/string literals

Detecting HTML changes when adding elements with JavaScript

I'm am having a issue where I have a click event.
This is the code:
for (const button of itemClick) button.addEventListener("click", function() {
clickCount++;
if (clickCount == 1) {
value1 = button.getAttribute("value");
}
if (clickCount >= 2) {
value2 = button.getAttribute("value");
clickCount = 0;
onItemClick();
}
});
itemClick refers to a document classname variable called item
So if I click twice on that item it should add a second item which works but clicking on that item created by JavaScript doesn't want to work so I have to somehow let JavaScript know the HTML has changes so when I click on it it also has effect but I don't know how to do that and can't find any information for it.
I am adding the element in HTML like this:
let itemDiv = document.createElement("div");
let imageDiv = document.createElement("div");
let imgEl = document.createElement("img");
let itHeading = document.createElement("h6");
let itemslistcontent = document.getElementById("items");
itemslistcontent.appendChild(itemDiv);
itemDiv.appendChild(imageDiv);
imageDiv.appendChild(imgEl);
itemDiv.appendChild(itHeading);
itemDiv.classList.add("item");
itemDiv.setAttribute("value", prop);
itHeading.innerHTML = prop;
This is the full function:
function onItemClick() {
for (var prop in itemNames) {
if (itemNames[prop].includes(value1) && itemNames[prop].includes(value2)) {
value1 = "";
value2 = "";
let itemDiv = document.createElement("div");
let imageDiv = document.createElement("div");
let imgEl = document.createElement("img");
let itHeading = document.createElement("h6");
let itemslistcontent = document.getElementById("items");
itemslistcontent.appendChild(itemDiv);
itemDiv.appendChild(imageDiv);
imageDiv.appendChild(imgEl);
itemDiv.appendChild(itHeading);
itemDiv.classList.add("item");
itemDiv.setAttribute("value", prop);
itHeading.innerHTML = prop;
console.log(itemslistcontent);
} else {
value1 = "";
value2 = "";
}
}
}
When you add new elements to the DOM programmatically, those elements are called dynamic.
Since your code is already run when the dynamic elements are added to the DOM, the event listeners are only registered to the elements which were already present at the time of code execution.
Thus, you need to add the event listener again to the dynamic component.
Also, why don't you make use of the dblclick event in JavaScript?
Try the following code, its adding the even listener as and when the dynamic elements are created.
function onItemClick() {
for (var prop in itemNames) {
if (itemNames[prop].includes(value1) && itemNames[prop].includes(value2)) {
value1 = "";
value2 = "";
let itemDiv = document.createElement("div");
let imageDiv = document.createElement("div");
let imgEl = document.createElement("img");
let itHeading = document.createElement("h6");
let itemslistcontent = document.getElementById("items");
itemslistcontent.appendChild(itemDiv);
itemDiv.appendChild(imageDiv);
imageDiv.appendChild(imgEl);
itemDiv.appendChild(itHeading);
itemDiv.classList.add("item");
itemDiv.setAttribute("value", prop);
itHeading.innerHTML = prop;
console.log(itemslistcontent);
// adding the event listener
itemDiv.addEventListener("dblclick", function() {
value2 = button.getAttribute("value");
clickCount = 0;
onItemClick();
});
} else {
value1 = "";
value2 = "";
}
}
}
Event Delegation
The easiest way to track any event (ie "click") on any element of any amount, whether dynamically added or not, is to use Event Delegation.
Register an element that will or is containing all of the buttons we want to monitor/control by way of the "click" event.
// Event Property
document.querySelector('main').onclick = clickHandler;
OR
// EventListener
document.querySelector('main').addEventListener('click', clickHandler);
Whenever this parent element is clicked function clickHandler() will run. This event handler (just a more descriptive name for a function triggered by an event) will delegate the event to the exact button the user clicked by:
using the event.target property to reference the element the user clicked.
narrow it down by the use of if/if else/else conditions, and the .matches() method.
if (event.target.matches('button')) {...
Advantages
We only need to register events to a single parent element. window and document could be used but it's better to use an element further down like <main> or <form> IMO. This parent element can be referenced using event.currentTarget property.
const listener = event.currentTarget;
console.log(listener.tagName); // MAIN
Any descendant element of the event.currentTarget can be selected. If the user clicked an element (ie <button>) then it can be referenced directly with the event.target property. If the desired element isn't event.target, but it is it's proximity, we can reference it indirectly many ways.
<main>
<section class='group'>
<figure class='item'>
<img src='pix.jpg'>
<figcaption>
<input>
<button>X</button>
<label>XXXX</label>
...
const clicked = event.target;
if (clicked.matches('button')) {
let group = clicked.closest('.group');
let item = clicked.closest('.item');
let tag = clicked.nextElementSibling;
let txt = clicked.previousElementSibling;
...
/*
Reference to the <section>, <figure>, <input>,
and <label> and finding the exact element
clicked by user
*/
This delegation also includes any dynamically added elements as well. A common mistake newbies make is expecting elements added to the DOM are clickable, but aren't because they need to be registered to the event (ie .onclick or .addEventListener('click')). This is never a concern using event delegation. Add whatever and whenever to the event.currentTarget; and nothing more.
Demo
const main = document.querySelector('main');
main.onclick = clickHandler;
function clickHandler(event) {
const listener = event.currentTarget;
const clicked = event.target;
let grpIdx = indexNodes('.group');
let itmIdx = indexNodes('.item');
const itemHTMLString = `
<figure class='item'>
<img src="https://placeimg.com/100/100/any">
<figcaption>
<b>Item ${itmIdx+1}</b><button>💀</button>
<output class='count'>0</output>
</figcaption>
</figure>`;
const groupHTMLString = `
<section class='group'>
<fieldset>
<legend>Group ${grpIdx+1} Total Clicks</legend>
<output class='total'>1</output>
</fieldset>
<figure class='item'>
<img src="https://placeimg.com/100/100/any">
<figcaption>
<b>Item ${itmIdx+1}</b><button>💀</button>
<output class='count'>1</output>
</figcaption>
</figure>
</section>`;
let item, group, count, total;
if (clicked.matches('button')) {
item = clicked.closest('.item');
group = clicked.closest('.group');
count = clicked.nextElementSibling;
total = group.querySelector('.total');
let cnt = parseInt(count.textContent, 10);
let ttl = parseInt(total.textContent, 10);
if (ttl < 3) {
total.textContent = ttl + 1;
count.textContent = cnt + 1;
group.insertAdjacentHTML('beforeend', itemHTMLString);
indexNodes('.group');
indexNodes('.item');
} else if (ttl === 3) {
let buttons = group.querySelectorAll('button');
for (let btn of buttons) {
btn.disabled = true;
}
listener.insertAdjacentHTML('beforeend', groupHTMLString);
indexNodes('.group');
indexNodes('.item');
} else {
return false;
}
} else {
return false;
}
}
function indexNodes(selector) {
const nodes = document.querySelectorAll(selector);
nodes.forEach((node, index) => node.dataset.idx = index);
return nodes.length;
}
* {
margin: 0;
padding: 0;
}
main {
margin: 10px auto;
padding: 8px;
}
.group {
display: flex;
flex-flow: row nowrap;
justify-content: flex-start;
margin: 8px auto;
padding: 4px;
}
fieldset {
margin-top: -20px
}
legend {
font-size: large;
font-weight: bold;
}
figcaption {
display: flex;
flex-flow: row nowrap;
justify-content: flex-end;
}
button,
b,
output {
display: block;
font-size: large;
}
b {
text-align: left;
padding: 8px 8px 0 8px;
font-size: small;
}
.count {
padding: 8px 8px 0 8px;
}
.total {
margin: 0 auto;
text-align: center;
}
button {
cursor: pointer;
max-height: 28px;
}
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
</head>
<body>
<main>
<section class='group' data-idx='0'>
<fieldset>
<legend>Group 1 Total Clicks</legend>
<output class='total'>1</output>
</fieldset>
<figure class='item' data-idx='0'>
<img src="https://placeimg.com/100/100/any">
<figcaption>
<b>Item 1</b><button>💀</button>
<output class='count'>1</output>
</figcaption>
</figure>
</section>
</main>
</body>
</html>

How to appendChild a generated div under a manually created div?

I have a div element that is generated by a javascript library. Let's assume the ID for it is auto
And I manually created a HTML div element with an ID of manual.
How do I move the generated div#auto into div#manual?
The div elements that are generated are not part of the document tree.
So I can't just appendChild to put auto under manual
Any idea?
var element = document.createElement("div");
element.id = 'generated';
var el = document.getElementById('element');
console.log(el)
<div id="manual">Place #generated as child of me</div>
As you can see, I can't target the generated div element #generated because it is not in the document tree. If I can't select it, I can't place it under #manual.
In your code:
var element = document.createElement("div");
element.id = 'generated';
var el = document.getElementById('element');
console.log(el)
element is already a reference to the element. There's no reason to try to look it up again via getElementById; just use the reference you have. getElementById won't find it, because it's not in the DOM yet.
So just use element:
var element = document.createElement("div");
element.id = 'generated';
document.querySelector("#manual").appendChild(element);
// -------------------------------------------^
At that point, it's in the DOM and getElementById would find it (though, again, you don't need to; you already have a reference to it in element).
Live Example: (I've added borders and given the generated div some content to make it clear what the result is)
var element = document.createElement("div");
element.id = 'generated';
element.innerHTML = "generated";
document.querySelector("#manual").appendChild(element);
div {
border: 1px solid black;
padding: 2px;
}
<div id="manual">Place #generated as child of me</div>
Note that there's no need for the id on the generated div for you to do this. If you have a different reason for it having an id, that's fine, but you don't need it for this:
var element = document.createElement("div");
element.innerHTML = "generated";
document.querySelector("#manual").appendChild(element);
div {
border: 1px solid black;
padding: 2px;
}
<div id="manual">Place generated div as child of me</div>

jQuery nest appends

is it possible to nest appends in jQuery??
I tried to do this:
var div = $('#ui-container');
var div2 = div.append( $('<div/>').addClass('second') );
var div3 = div2.append( $('<div/>').addClass('third') );
I want this:
<div id='ui-container'>
<div class='second'>
<div class='third'></div>
</div>
</div>
But I get this:
<div id='ui-container'>
<div class='second'></div>
<div class='third'></div>
</div>
You have to do it like below,
var div = $('#ui-container');
var div2 = $('<div/>').addClass('second').appendTo(div);
var div3 = div2.append($('<div/>').addClass('third'));
by using .appendTo(). Because .append() will return the object over which the append function was called. That is why you are seeing such result in your case.
#Rajaprabhu Aravindasamy has the correct answer. I thought I would go a little more in depth and add a jsfiddle.
Use appendTo() instead of append. Here's a quote from http://api.jquery.com/appendto/
The .append() and .appendTo() methods perform the same task. The major difference is in the syntax-specifically, in the placement of the content and target. With .append(), the selector expression preceding the method is the container into which the content is inserted. With .appendTo(), on the other hand, the content precedes the method, either as a selector expression or as markup created on the fly, and it is inserted into the target container.
Here's the markup:
var div = $('#ui-container');
var div2 = $('<div>div2<div/>').addClass('second').appendTo(div);
var div3 = $('<div>div3<div/>').addClass('third').appendTo(div2);
Try:
var div = $('#ui-container');
var div2 = $('<div/>').addClass('second');
div2.append( $('<div/>').addClass('third') );
div.append(div2);
jquery methods typically return the DOM node that they are being called on.
Therefore
var div2 = div.append( $('<div/>').addClass('second') ); // returns $('#ui-container')
It is also standard to reference DOM elements with a $ such as $div.
Here is a verbose solution
// Grab container
$div = $('#ui-container');
// Create first child
var div2 = '<div class="second"></div>'
// Append .second to #ui-container
$div.append(div2);
// Grab .second from the DOM
var $div2 = $('.second');
// Create grandchild element
var div3 = '<div class="third"></div>'
// Append to .second
$div2.append(div3)
codepen
All of your variables div, div2 and div3 refer to the same element $('#ui-container'). Easiest way to understand what you did would be to rename your variables, so you can see what happens:
/* What you did is this: */
var container = $('.result1');
var containerWithSecondAppended = container.append( $('<div/>').addClass('second') );
var containerWithThirdAppended = containerWithSecondAppended.append( $('<div/>').addClass('third') );
/* What you wanted to do is this: */
var div = $('.result2');
var div2 = $('<div/>').addClass('second');
var div3 = $('<div/>').addClass('third');
div.append( div2.append(div3) );
div {
padding: 10px;
border: 1px solid black;
}
.result {
margin: 10px;
padding: 10px;
}
.second {
background: none yellow;
}
.third {
background: none green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="result result1"></div>
<div class="result result2"></div>
Also on Fiddle.
jQuery append method will return a jQuery element, the one that was appended (changed).

Insert node as string but keep the instance of the node

I want to insert a node, that is converted to a string, on a specific position.
the problem is that i want to keep or regain the instance of the node, but dont know how.
I hope some of you have a solution
Note: I have to insert the element as string!
HTML:
<div class="div">
Here are <span class="span"></span> some text
</div>
CSS:
.div, .span {
display: inline-block;
border: 1px solid #333333;
width: 200px;
height: 100px;
}
.span {
min-width: 10px;
max-width: 50px;
height: 50px;
}
JS:
var div = document.querySelector('.div');
var span = document.querySelector('.span');
// element to insert
var newElement = document.createElement('div');
newElement.innerHTML = 'Hallo!';
// convert to string
var converter = document.createElement('div');
converter.appendChild(newElement);
// insert element at the end of span
span.innerHTML = span.innerHTML + converter.innerHTML;
// get the instance of the node? to interact with it
newElement.style.color = '#00ff00';
Example: CODEPEN
I wrote small function for test:
function makeNode(innerHTML){
var node = document.createElement('div');
node.id = 'unique_id';
node.innerHTML = innerHTML;
document.body.innerHTML += node.outerHTML;
return document.getElementById('unique_id');
}
Create element, insert in body, and return it
var elm = makeNode('test div');
Apply some style
elm.style = 'color:red;';
Try it: https://jsfiddle.net/nv6gyLve/
You need to set styles before insert element to dom. Other way -getElementById or same functions

Categories

Resources