DOM Event.StopPropagation to child has no effect - javascript

I'm doing a tutorial about Events and I'm stuck with Event.StopPropagation() method. It seems that with my example that the bubbling effect to the children are being affected.
Definition of StopPropagation:
The stopPropagation() method prevents propagation of the same event from being called.
Propagation means bubbling up to parent elements or capturing down to child elements.
At first I thought it was a browser problem but it was not the case. I can't find a solution about this.
Code:
// Event Bubbling and Propagation
// element.addEventListener( type, func, useCapture);
let m = document.getElementById('m');
let d = document.getElementById('d');
let p = document.getElementById('p');
let s = document.getElementById('s');
let highlight = (ev)=>{
//add CSS class "gold" to the clicked element
ev.stopPropagation();
let target = ev.currentTarget;
target.className = 'gold';
reset(target);
}
let reset = (_element)=>{
setTimeout(()=>{
_element.className = '';
}, 2000);
}
d.addEventListener('click', (ev)=>{
ev.stopImmediatePropagation();
log('Hi I\'m a DIV');
});
[m,d,p,s].forEach((element)=>{
element.addEventListener('click', highlight);
})
#m,#d,#p,#s{
border: 2px solid black;
padding: 15px;
margin: 10px;
}
.gold{
background-color: gold;
}
<main id="m"> m
<div id="d"> d
<p id="p"> p
<span id="s"> s</span>
</p>
</div>
</main>

This is not a problem with events, you are overthinking this.
You example is simply applying background color to top element, and as children do not have it defined, its applying the top one below it.
And if you remove stopImmediatePropagation() color will be applied as definition is: execute the first event handler, and stop the rest of the event handlers from being executed, and the first one was simply log().
In example below, if you apply background color to child elements, you will see they will stay the same. The color it self is applied only on clicked one.
That means the JS event itself did not bubble up to parent elements or capture down. And class was added only on clicked one. Check it with dev tools or add DOM change event listener on every element.
You confused CSS styling with JS event bubbling.
Example:
// Event Bubbling and Propagation
// element.addEventListener( type, func, useCapture);
let m = document.getElementById('m');
let d = document.getElementById('d');
let p = document.getElementById('p');
let s = document.getElementById('s');
let log = console.log;
let highlight = (ev)=>{
//add CSS class "gold" to the clicked element
ev.stopPropagation();
let target = ev.currentTarget;
target.className = 'gold';
reset(target);
}
let reset = (_element)=>{
setTimeout(()=>{
_element.className = '';
}, 2000);
}
d.addEventListener('click', (ev)=>{
//ev.stopImmediatePropagation();
log('Hi I\'m a DIV');
});
[m,d,p,s].forEach((element)=>{
element.addEventListener('click', highlight);
})
#m,#d,#p,#s{
border: 2px solid black;
padding: 15px;
margin: 10px;
}
#d {
background-color: blue;
}
#s {
background-color: red;
}
.gold{
background-color: gold !important;
}
<main id="m"> m
<div id="d"> d
<p id="p"> p
<span id="s"> s</span>
</p>
</div>
</main>
EDIT:
In your example child nodes do not have background property (rgba(0, 0, 0, 0)), that means you need to set it on click, so that background color of clicked element wont apply on its children.
In code below, you can read the CSS background value of parent element of clicked one, and read its background property. That you take that and apply on all children of clicked one.
This will work in your example.
You can also make sure to apply white to elements if parent was transparent.
Here is a fiddle to play with:
but make sure you will un-comment m (parent) background color to see side effects. You would need to adjust this to suit your production needs
const style = getComputedStyle(target.parentNode);
const backgroundColor = style.backgroundColor;
console.clear();
console.log(backgroundColor);
[...target.children].forEach(el => {
if (backgroundColor==="rgba(0, 0, 0, 0)") {
el.style.backgroundColor = "white";
}else{
el.style.backgroundColor = backgroundColor;}
})
// Event Bubbling and Propagation
// element.addEventListener( type, func, useCapture);
let m = document.getElementById('m');
let d = document.getElementById('d');
let p = document.getElementById('p');
let s = document.getElementById('s');
let log = console.log;
let highlight = (ev)=>{
//add CSS class "gold" to the clicked element
ev.stopPropagation();
let target = ev.currentTarget;
target.className = 'gold';
const style = getComputedStyle(target.parentNode);
const backgroundColor = style.backgroundColor;
console.clear();
console.log(backgroundColor);
[...target.children].forEach(el => {
if (backgroundColor==="rgba(0, 0, 0, 0)") {
el.style.backgroundColor = "white";
}else{
el.style.backgroundColor = backgroundColor;}
})
reset(target);
}
let reset = (_element)=>{
setTimeout(()=>{
_element.className = '';
}, 2000);
}
[m,d,p,s].forEach((element)=>{
element.addEventListener('click', highlight);
})
#m,#d,#p,#s{
border: 2px solid black;
padding: 15px;
margin: 10px;
}
#m {
background-color: blue;
}
.gold{
background-color: gold !important;
}
<main id="m"> m
<div id="d"> d
<p id="p"> p
<span id="s"> s</span>
</p>
</div>
</main>

Related

html input create li element how to set an id for the element

I'm new with HTML & JS and I face the following problem:
I have an input in html that creates a new li Element (in combination with JS); is it possible to give every newly-created li element its own id? For example to delete an specific element?
For Example:
<li id="one"> .. </li>
<li id="two"> .. </li>
So far it creates only <li> ... </li>
I think it can be done with a for loop, but I have no idea how to use it in my case.
See my JS code below:
function NewEntry() {
var Inputfield = document.getElementById("Inputfield");
var AddButton = document.getElementById("AddButton");
var ul = document.querySelector("ul");
var li = document.createElement("li");
li.appendChild(document.createTextNode(Input.value));
ul.appendChild(li);
Input.value = "";
I tried to insert a for loop into my code, but after that it doesn't add any elements.
function NewEntry() {
var Inputfield = document.getElementById("Inputfield");
var AddButton = document.getElementById("AddButton");
var ul = document.querySelector("ul");
var li = document.createElement("li");
for (var i = 0; i < li.length; i++)
li[i].id = 'abc-' + i;
li.appendChild(document.createTextNode(Input.value));
ul.appendChild(li);
Input.value = "";
Your for loop needs curly braces to work properly:
function NewEntry() {
var Inputfield = document.getElementById("Inputfield");
var AddButton = document.getElementById("AddButton");
var ul = document.querySelector("ul");
var li = document.createElement("li");
for (var i = 0; i < li.length; i++) {
abcElements[i].id = 'abc-' + i;
li.appendChild(document.createTextNode(Inputfield.value));
ul.appendChild(li);
}
Inputfield.value = "";
}
Otherwise only the immediate line after the for statement will run as part of the loop.
There also appeared to be a typo - you had Input instead of Inputfield? But I notice there are some other variables used here which are not defined, so I assume some extra code was omitted?
You could count the number of elements inside the <ul> and use that as id for the <li>:
var AddButton = document.getElementById("AddButton");
AddButton.addEventListener("click", NewEntry);
function NewEntry() {
var Inputfield = document.getElementById("Inputfield");
var ul = document.querySelector("ul");
var li = document.createElement("li");
li.id = ul.childElementCount;
li.appendChild(document.createTextNode(Inputfield.value));
ul.appendChild(li);
Inputfield.value = "";
console.log(li);
}
<input type="text" id="Inputfield" />
<button id="AddButton">Add</button>
<ul></ul>
One simple means by which this could be accomplished is as follows, with explanatory comments in the JavaScript:
// a generator function, to generate a constantly increasing counter:
const generator = function*() {
// the initial value of the counter:
let i = 0;
// this loop causes the generator to return a number infinitely
// though while the loop is infinite it yields one value and then
// pauses until the next call; this is one instance where an
// infinite loop is deliberate and not a problem:
while (true) {
// increments the value of i, and then returns it to the
// calling context:
yield ++i;
}
},
// assigning a reference to the generator:
counter = generator(),
// a simple Arrow function that takes two arguments:
// tag: String, the element-type to create, and
// props: an Object of element-properties and values to
// assign to the created element:
create = (tag, props) => Object.assign(document.createElement(tag), props),
// a reference to the various elements to which event-listeners are attached:
list = document.querySelector('#list'),
form = document.querySelector('form'),
input = document.querySelector('input'),
button = document.querySelector('#add'),
// the addNew function, written as an Arrow function:
addNew = () => {
// caching the source of the text:
const inputField = document.querySelector('input');
// here we take the value, trim it to remove leading and trailing
// white-space, and then retrieve its length; if there is a zero
// length (so either nothing was entered, or only whitespace) then
// we return here:
if (inputField.value.trim().length === 0) {
return false;
}
// otherwise we cache a reference to the element to which the
// created <li> element will be appended:
const ul = document.querySelector('#list'),
// we call the create() function, here creating an <li>
// element, and passing in an object of properties and
// values:
li = create('li', {
// we set the 'id' - using a template literal string - to
// the string of 'abc-' plus next-value of the counter:
id: `abc-${counter.next().value}`,
// and set the text-content of the element to the
// trimmed value of the <input>:
textContent: inputField.value.trim()
}),
// here we create a <button>,
deleteButton = create('button', {
// with its className set to 'delete':
className: 'delete',
// its text-content set to 'delete task':
textContent: 'delete task',
// and its type set to 'button' in order to
// prevent the <button> submitting the <form>:
type: 'button'
});
// we append the created <button> to the created <li>:
li.append(deleteButton);
// we append the <li> to the <ul>:
ul.append(li);
// and reset the value of the <input> to an empty string:
inputField.value = '';
};
// here we prevent form submission:
form.addEventListener('submit', (e) => e.preventDefault());
// here we bind an anonymous function as the event-handler of the
// 'keyup' event on the <input> element:
input.addEventListener('keyup', (evt) => {
// if the, or an, 'Enter' key is pressed (note that evt.keyCode is
// almost entirely deprecated now, but is here for backwards
// compatibility, it can and possibly should be removed):
if (evt.key === 'Enter' || evt.keyCode === 13) {
// here we call the addNew() function:
addNew();
}
});
// we bind the addNew() function - note the deliberate lack of
// parentheses - as the event-handler for the 'click' event:
button.addEventListener('click', addNew);
// because we're adding the delete <button> elements we delegate the
// event-handling to the closest ancestor element present in the DOM
// on page-load, here that's the #list element. Here we bind the
// anonymous function as the event-handler for the 'click' event
// which bubbles to the <ul>:
list.addEventListener('click', (evt) => {
// if the element on which the click event fired has an ancestor
// .delete element (therefore it was fired on, or in, the .delete
// <button>):
if (evt.target.closest('.delete')) {
// we navigate from the event-target to the closest <li>
// ancestor element and use Element.remove() to remove that
// element from the document:
evt.target.closest('li').remove();
}
})
*,
::before,
::after {
box-sizing: border-box;
font-family: system-ui;
font-size: 16px;
margin: 0;
padding: 0;
}
main {
inline-size: clamp(15em, 80vw, 1000px);
margin-inline: auto;
}
h2 {
font-size: 1.4em;
text-align: center;
}
fieldset {
display: grid;
padding: 0.5em;
}
legend {
border-inline: 1px solid currentColor;
border-radius: 0.25em;
margin-inline: 1em;
padding-inline: 0.5em;
}
label {
display: flex;
gap: 1em;
justify-content: space-between;
margin-block: 0.5em;
}
label input[type=text] {
flex-grow: 1;
}
#list {
border: 1px solid currentColor;
display: grid;
gap: 0.5em;
list-style-type: none;
margin-block: 1em;
margin-inline: auto;
padding: 0.5em;
}
#list:empty::before {
color: hsl(0deg 30% 30% / 0.8);
content: "A beautiful, quiet day of reflection...";
font-style: italic;
}
#list li {
background-color: lavender;
display: grid;
padding: 0.25em;
}
#list li:nth-child(odd) {
background-color: lightcyan;
}
li[id]::before {
content: "#" attr(id) ": ";
}
.delete {
justify-self: end;
}
<main>
<section>
<h2>To do list</h2>
<form action="#" method="post">
<fieldset>
<legend>New task</legend>
<label>
<span class="labelText">What do you want to do today?</span>
<input type="text">
</label>
<button id="add" type="button">Add task to list</button>
</fieldset>
</form>
<ul id="list"></ul>
</section>
</main>
JS Fiddle demo.
It's worth noting that there is one potential issue using a generator function to generate a counter for an id property; the counter will only ever increase, so if you have three elements abc-1, abc-2, abc-3 and then delete those, and create another three elements those elements will continue from abc-4. So the id will be independent of the number of created elements. This may be a benefit in that a duplicate value can't be created, so there's little chance of a duplicate id being created by a generator.
References:
Arrow functions.
document.createElement().
document.querySelector().
Element.append().
Element.remove().
EventTarget.addEventListener().
Generator functions (function*(){...}).
Object.assign().
String.prototype.trim().
Template literals.
while loop.

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

Elements lose javascript functionality after cloning

The following is the code. What I have done is made an item (the first one) and when i hover on its button, its background color changes to red and so on.
What I did next is cloned the element and appended it to a new div. The html (elements having same classes) is the same but the mouseover event doesn't work anymore.
My question is that why it did not work and how can I fix it? Also I tried to do the same by copying inner HTML to the new element but it is the same everytime.
const colorDiv = document.querySelector(".color-div");
const button = document.querySelector("button");
const mainContainer = document.querySelector(".main-container");
button.addEventListener("mouseover", function() {
colorDiv.style.backgroundColor = "red";
});
button.addEventListener("mouseout", function() {
colorDiv.style.backgroundColor = "seagreen";
});
const newItem = mainContainer.cloneNode(true);
document.querySelector(".new-container").appendChild(newItem);
.color-div {
height: 300px;
width: 300px;
background-color: seagreen;
margin: 10px;
padding: 10px;
transition: all .3s;
}
<!-- I will copy the div with main container class -->
<div class="main-container">
<div class="color-div">Hello</div>
<button>Change</button>
</div>
<!-- and append copied item to the following item -->
<div class="new-container"></div>
Events are not cloned, here's a quick fix:
const colorDiv = document.querySelector(".color-div");
const button = document.querySelector("button");
const mainContainer = document.querySelector(".main-container");
// If someone clicks on anywhere on the website THAT IS A BUTTON, then change the color.
document.addEventListener("mouseover", function(e) {
if (e.target.matches("button")) {
colorDiv.style.backgroundColor = "red";
}
});
document.addEventListener("mouseout", function(e) {
if (e.target.matches("button")) {
colorDiv.style.backgroundColor = "seagreen";
}
});
const newItem = mainContainer.cloneNode(true);
document.querySelector(".new-container").appendChild(newItem);
This should work for every clone.

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 select a new added element and edit it?

I have an <a> element:
<a id='addNewElementk' onclick='//Some Js Code' class='continueButton'>Click To Add</a>
When this anchor is clicked , A new element added:
New Added Element
And the first anchor which was clicked , Is removed.
I want to select that new element.
I tried:
window.onload = function(){
var newElem = document.getElementsByClassName('continueButton')[1];
alert(newElem.innerHTML);
}
I'm using ('continueButton')[1] , As there is another input with the same class before that anchor.
But for sure I get Click To Add from the first one , As that's was found when the page is loaded.
So how can I select that new element?
You're attempting to select the element before it exists in the DOM.
You instead need to run that code within the click event handler of the first <a>, like this:
window.onload = function() {
document.querySelector('#addNewElementk').addEventListener('click', function(e) {
e.preventDefault();
var a = document.createElement('a');
a.textContent = 'New Added Element';
a.href = '#';
a.classList.add('continueButton');
a.addEventListener('click', function(e) {
console.log(a.innerHTML);
});
this.parentNode.insertBefore(a, this);
this.remove();
});
}
<a id='addNewElementk' href="#" class='continueButton'>Click To Add</a>
Note the use of addEventListener() over the outdated on* event attributes which should be avoided.
You are attempting to select on an element that doesn't exist in the DOM. Dynamically added elements can be accessed in a couple of ways, above someone has an answer that adds an event listener to the created element which is a solid solution. The other most common way would be to use event delegation (if you are familiar with jQuery that would be $(parentElement).on('action', 'elementWeWantToWatch', function)) in Vanilla js the pattern is effectively the same, find or make a container element for your dynamic html, then add a listener to that container. Inside the listener you will want to ensure the target matches whatever your dynamic selection would be and execute when you find a match.
In this Example
The event listener is initiated on page load to watch the container element. The listener watches for clicks on elements with the continueButton class and when it finds one it removes the clicked element and adds a new element (the counter is to demonstrate that new content is being displayed :D)
(function() {
let i = 1;
const makeButton = () => {
const a = document.createElement('a');
a.classList.add('continueButton');
a.href = '#';
a.textContent = `Button ${i}`
i++;
return a;
}
const init = () => {
const container = document.querySelector('.test');
container.addEventListener('click', e => {
if (e.target.classList.contains('continueButton')) {
let button = makeButton();
container.appendChild(button);
container.removeChild(e.target);
return;
}
});
};
if (document.readyState == 'loading') {
window.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})()
.test {
width: 100%;
display: block;
text-align: center;
}
.continueButton {
display: block;
color: white;
background-color: green;
border-radius 2px;
padding: 15px 30px;
line-height: 2;
margin: 50px auto;
width: 200px;
text-decoration: none
}
<section class="test">
<a id='addNewElementk' class='continueButton'>Click To Add</a>
</section>

Categories

Resources