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>
Related
I'm trying to automate calling a function (initBox) on all elements with class box on the page in vanilla javascript. I have made a functional solution except that it only applies to elements that are on the page when it is loaded. If I add an element to the DOM using javascript, the function (initBox) is not called on it.
Of course I can call the function (initBox) manually after adding the element, but I would like to automate it.
I'm looking for something similar to what jQuery does for events.
For example:
$('table').on('click', 'td', function (event) {
doSomething();
});
This event is called even if I add the TD element to the table later via javascript.
Here is my current solution:
function addBox() {
var btn = document.getElementsByTagName('button')[0];
var el = document.createElement('div');
el.classList.add('box');
el.innerText = (document.getElementsByTagName('div').length + 1);
btn.before(el);
}
function initBox(el) {
el.innerText += ' Initialized';
}
document.querySelectorAll('.box').forEach(initBox);
.box {
display: inline-block;
border: 1px solid #1f2227;
padding: 20px;
}
button {
padding: 20px;
}
<div class="box">1</div>
<div class="box">2</div>
<button onclick="addBox()">Add box</button>
The OP's problem can be solved just by the usage of a MutationObserver instance where the OP needs the callback from the list off added nodes just to initialize the very nodes which do match the OP's definition of a box.
function addBox() {
var btn = document.getElementsByTagName('button')[0];
var el = document.createElement('div');
el.classList.add('box');
el.innerText = (document.getElementsByTagName('div').length + 1);
btn.before(el);
}
function initBox(el) {
el.innerText += ' Initialized';
}
document.querySelectorAll('.box').forEach(initBox);
function initializeBoxClassDivOnly(node) {
if (node.nodeType === 1 && node.matches('div.box')) {
initBox(node);
}
}
function handleNodeInsertion(mutationList/*, observer*/) {
for (const mutation of mutationList) {
mutation
.addedNodes
.forEach(initializeBoxClassDivOnly);
}
};
const observer = new MutationObserver(handleNodeInsertion);
observer
.observe(document.body, { childList: true, subtree: true });
.box {
display: inline-block;
border: 1px solid #1f2227;
padding: 20px;
}
button {
padding: 20px;
}
<div class="box">1</div>
<div class="box">2</div>
<button onclick="addBox()">Add box</button>
Look Into Mutation Observers, they are the new way of observing dom objects, it allows you to run a function when an item is added or removed or modified in a particular DOM element, or if you want you can go old school ( meaning you add event listeners).
Mutation Observers ( Recommended Method ) https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
Old School Evens https://developer.mozilla.org/en-US/docs/Web/API/MutationEvent
if you want me to write the code, let me know in the comments.
enter image description here
As shown in the picture above, when the first element value is 7,
If I click, I want to output 7 in console.log.
var container1 = document.createElement('div')
container1.className = 'container1';
document.body.appendChild(container1);
for(var index = 0;index < 20 ; index++){
var aa = document.createElement('span')
aa.innerHTML = card1[index];
container1.appendChild(aa);
aa.className = 'card'+index;
aa.addEventListener('click',clickEvent)
}
function clickEvent(){
//What code should I use?
}
how are you?
Try this:
function clickEvent(evt){
console.log(evt.target.innerHTML)
}
For this you could use the textContent property of HTML elements in JavaScript. To complete this you would also need to pass the event into your clickEvent function. Click events have a event.target, which is the HTML node that was clicked. The event
var container1 = document.createElement('div')
container1.className = 'container1';
document.body.appendChild(container1);
for(var index = 0;index < 20 ; index++){
var aa = document.createElement('span')
aa.innerHTML = card1[index];
container1.appendChild(aa);
aa.className = 'card'+index;
aa.addEventListener('click',clickEvent)
}
function clickEvent(event){
//What code should I use?
let clickedBox = event.target;
console.log(clickedBox.textContent);
}
As far as I know, this should work like you're intending. I haven't worked with the front-end side of JS lately, so correct me if I'm wrong.
You need to capture the click event in your function clickEvent by doing so:
function clickEvent(event){
console.log(event.target.textContent);
}
Capturing the event is done by passing it as an argument while creating your function, do not pass it when you add it to the event listener.
The event is the clicking event, one of its properties is something called target that points to the element that the event click has occurred on, from there you can access anything almost on that element, including its value which I assume you meant its text (the number on it).
If you have provided the HTML and CSS too then it will be a direct answer to your need but a similar approach can be done using this keyword which print innerHTML of each span tag on click
function paret(reed) {
console.log(reed.innerHTML)
}
div {
display: flex;
justify-content: space-between;
}
span {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 160px;
background-color: red
}
<div>
<span onclick="paret(this)">7</span>
<span onclick="paret(this)">17</span>
<span onclick="paret(this)">9</span>
<span onclick="paret(this)">8</span>
<span onclick="paret(this)">5</span>
</div>
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.
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>
Here is JS Fiddle!
I am trying to append new Elements to the div, this is working.
My problem is that I want to append the new element only once on button click, and save it to localStorage so that I would not loose the state on refresh or any other action.
div {
text-align: center;
}
#Neighborhood {
color: brown;
}
#NewElement {
color: green;
}
<div id="Neighborhood">
<div id="Neighbor1">Neighbor 1</div>
<div id="Neighbor2">Neighbor 2</div>
<div id="Neighbor3">Neighbor 3</div>
</div>
<input type="button"onclick="add_prev();" value="ACTION">
/* Adds Element BEFORE NeighborElement */
Element.prototype.appendBefore = function (element) {
element.parentNode.insertBefore(this, element);
}, false;
/* Adds Element AFTER NeighborElement */
Element.prototype.appendAfter = function (element) {
element.parentNode.insertBefore(this, element.nextSibling);
}, false;
/* Typical Creation and Setup A New Orphaned Element Object */
add_prev = function () {
var NewElement = document.createElement('div');
NewElement.innerHTML = 'New Element';
NewElement.id = 'NewElement';
NewElement.appendBefore(document.getElementById('Neighbor2'));
}
I am thankful for every tip or solution! Cheers!
Pass this to the onclick function. That way, after you do your things, just remove the listener to click or disable the button.
Also, when the page loads, load the information from the storage, if it's true, directly call the function and then disable the button.
Maybe it isn't exaclty what you need, but it can help a lot, you can follow this logic to get there. The code below is just an example.
OBS: it won't work well here because localStorage is not allowed in StackOverflow.
In this fiddle you can try it better: https://jsfiddle.net/so5u1c4z/
On the fiddle above, create the element, then save and reload the page. the element will be there once the page loads.
$(document).ready(function(){
add_prev = function (elem) {
var NewElement = document.createElement('div');
NewElement.innerHTML = 'New Element';
NewElement.id = 'NewElement';
document.getElementById('Neighbor2').append(NewElement);
localStorage.setItem('elementCreated', true);
if (elem){
$(elem).attr('disabled', true);
}
}
var isCreated = localStorage.getItem('elementCreated');
if (isCreated){
add_prev();
$("#btnAdd").attr('disabled', true);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="button" onclick="add_prev(this);" value="ACTION" id="btnAdd">
<div id="Neighbor2"></div>