forEach executes only once javascript - javascript

I am using a forEach method to add an event listener to each button, everything is working the first time, the function is calling the filterElements function as wanted BUT when i try to click a second time the code doesen't respond, could it be because of my HTML collection that I am transforming to an Array using Array.from or what could it be ?
function filterPhotograpsIndividualTages(dataJson) {
const individualTags = Array.from(document.getElementsByClassName('individual-tags'));
individualTags.forEach(btn => btn.addEventListener('click', () => { //this is the forEach method
/*onClick clear the old HTML of photographersDiv and call
the function filterElements to execute his block code!*/
document.getElementById('container').innerHTML = "";
filterElements(dataJson, btn);// function is invoked here
}))
};
// this function is being called inside of the filterPhotograpsIndividualTages function
function filterElements(dataJson, btn){
dataJson.photographers.forEach(photographe => {
if(photographe.tags.indexOf(btn.id) != -1) {
const photographersDiv = document.getElementById('container');
const div = document.createElement("div");
div.innerHTML = `
<div class="photographerContainer">
<a href="photographers.html?id=${photographe.id}">
<div class="portraitBox">
<img src="${photographe.portrait}" alt="photo">
</div>
<h1 class="name">${photographe.name}</h1>
</a>
<p class="city">${photographe.city}, ${photographe.country}</p>
<p class="tagline">${photographe.tagline}</p>
<p class="price">${photographe.price}€/jour</p>
<p class="tags">${photographe.tags.map(tag => `<button id=${tag} class="tag individual-tags">#${tag}</button>`).join(" ")}</p>
</div>
`
photographersDiv.appendChild(div);
}})
};

When you add event like this, it only binding to DOM you have selected with getElementsByClassName not with the one re-add after delete
Instead you should bind whole document and check event's target
document.addEventListener('click', function(event) {
if (event.target.classList.contains('your_class')) {
Function();
}
});

Related

Adding a function to an element via javascript

So I have a loop that initializes elements and attributes from an array, and I've managed to add everything I need except one thing, a function. The loop in question is below:
for (const character of result) {
let image = document.createElement("img");
image.src = character.src;
image.setAttribute('data-jval', character.jval);
image.setAttribute('id', character.id);
image.setAttribute('class', character.class)
image.setAttribute('draggable', 'true')
// the below function isn't being added to the divs
image.addEventListener('ondragstart', function(event){
let data = event.target.dataset.jval;
event.dataTransfer.setData("text", data);
})
let wrapper = document.createElement('div');
wrapper.appendChild(image);
section.appendChild(wrapper);
}
and the function I'm trying to add to my elements is:
function drag(event) {
let data = event.target.dataset.jval;
event.dataTransfer.setData("text", data);
}
Before trying to create the elements in JavaScript I had them in HTML, and they looked like this:
<img data-jval="a" id="jval-a" src="./images/あ.png" class="j-char-img" draggable="true" ondragstart="drag(event)"/>
but when I recreate it with my loop it looks like this, missing the ondragstart="drag(event"
<img src="/images/あ.png" data-jval="i" id="jval-i" class="j-char-img" draggable="true">
If the rest of my code would be useful I can post that as well.
Just "dragstart" instead of "ondragstart" event:
for (const character of result) {
let image = document.createElement("img");
image.src = character.src;
image.setAttribute('data-jval', character.jval);
image.setAttribute('id', character.id);
image.setAttribute('class', character.class)
image.setAttribute('draggable', 'true')
// the below function isn't being added to the divs
image.addEventListener('dragstart', drag);
let wrapper = document.createElement('div');
wrapper.appendChild(image);
section.appendChild(wrapper);
}
function drag(event) {
let data = event.target.dataset.jval;
event.dataTransfer.setData("text", data);
}
So basically you add the prefix on before the event when you write inline event handlers, just like before when you put your event handler inside your img tag. Same thing for any other events:
click => onclick
submit => onsubmit
keydown => onkeydown
#try to add Semicolon;
image.setAttribute('class', character.class);
image.setAttribute('draggable', 'true');
// the below function isn't being added to the divs
image.addEventListener('ondragstart', function(event){
let data = event.target.dataset.jval;
event.dataTransfer.setData("text", data);
});

Create class to handle click and other events of element

I want to create class and want to pass my element to it and fire events form class instead of triggering from javascript events everytime.
My try
HTML :
<div id="upload-container">
<label for="myfile">Select a file:</label>
<input type="file" id="fileInput" name="myfile">
<button id="send"> Upload </button> </div>
Javascript class :
function Myevents(ele) {
this.element = ele;
}
Myevents.prototype = {
init: function() {
this.element.addEventListener('click', function(file) {
console.log(file)
}(this.element));
this.element.addEventListener('mouseover', function(file) {
console.log(file)
}(this.element));
}
}
var ele = document.getElementById('send');
var instance = new Myevents(ele);
instance.init();
instance.click() ; // should be able to call it manually
instance.mouseover(); // should be able to call it manually
here both above two calls are not firing as required, how to handle this ,any help would appreciated.
Here I recreate your Myevents function. I make a click function as a prototype and attach click event listener and call it implicitly.
const Myevents = function(el) {
this.element = el;
}
Myevents.prototype = {
click: function() {
// Attach the event listener
this.element.addEventListener('click', (e) => {
e.preventDefault();
console.log('clicked');
});
// Finally fire the click
// this.element.click();
}
}
const el = document.querySelector('#clickbtn');
const instance = new Myevents(el);
instance.click();
<div>
<p>Hello World</p>
<button id="clickbtn">Ok</button>
</div>
Note: I don't know but let me know if it fulfills your requirement.

How to alert when element in a list is clicked?

function whatami(img){
console.log(img.key);
}
let animals = ["frog","frog","sheep","sheep","snail","snail","mouse","mouse","bat","bat","walrus",
"walrus","giraffe","giraffe","zebra","zebra","dog","dog","octopus","octopus","hippo",
"hippo","camel","camel","pig","pig","rhino","rhino","rooster","rooster","panda","panda",
"turtle","turtle","raccoon","raccoon","polar bear","polar bear","lion","lion","bison",
"bison","orca","orca","snake","snake","shark","shark","toucan","toucan","butterfly",
"butterfly","anteater","anteater","seal","seal","armadillo","armadillo","rooster","rooster"]
var array = shuffle(animals);
let images = array.map(image => {
return <img onClick = {whatami(image)} key={image} src={"/animalgameback.jpg"} alt="" className="img-responsive"/>
});
return (
<div>
<div>
{images}
</div>
</div>
)
}
}
I have this list of images and I need a way to find which one is clicked when I press one of them. How do I do this?
The click must receive a callback function. Try writing the onClick with a arrow function like this
onClick = { () => { whatami(image)} }
I would write ()=>{whatami(image)} instead of whatami(image). LEt me know if it works.

src not changing on else if part of if/else statement

I have created a function which is used in an event listener function so when the user clicks the left arrow icon on the webpage, a different character image is displayed. The first click works fine and the image is changed, however on the second click, nothing happens. I can't figure out why as from what I can see, the condition of the 'else if' of the statement is true so therefore the src code should change accordingly and change the image.
There are no error messages in the console.
Here is the Event listener:
document.querySelector(".left").addEventListener("click", () => {
leftArrow();
});
Here is the leftArrow function:
leftArrow: () => {
if (document.querySelector(DOMStrings.characterImg).src = characters[0]) {
document.querySelector(DOMStrings.characterImg).classList.add("boris");
document.querySelector(DOMStrings.characterImg).src = characters[1];
const el = document.querySelector(DOMStrings.characterImg);
console.log(el);
} else if (document.querySelector(DOMStrings.characterImg).src = characters[1]) {
document.querySelector(DOMStrings.characterImg).classList.remove("boris");
document.querySelector(DOMStrings.characterImg).src = characters[0];
const el = document.querySelector(DOMStrings.characterImg);
console.log(el);
}
Here is where the images are stored:
const characters = ["Resources/Images/trump.png", "Resources/Images/boris.png"];
Here is where the DOM strings are stored:
const DOMstrings = {
characterImg: ".character-img"
}
Lastly, here is the relevant html:
<div class="character-select">
<ion-icon class="left" name="arrow-dropleft-circle"></ion-icon>
<ion-icon class="right" name="arrow-dropright-circle"></ion-icon>
<img class="character-img" src="Resources/Images/trump.png">
</div>
function myFunction() {
document.getElementById("myImg").src = "https://www.w3schools.com/jsref/hackanm.gif";
}
function myFunctionbygeneric(){
document.querySelector(".test").src = "https://www.w3schools.com/jsref/hackanm.gif";
}
function myFunctionbygenericid(){
document.querySelector("#myImg").src = "https://www.w3schools.com/jsref/compman.gif";
}
<img id="myImg" class="test" src="https://www.w3schools.com/jsref/compman.gif" width="107" height="98">
<button onclick="myFunction()">click here</button>
<button onclick="myFunctionbygenericid()">Query selector by id</button>
<button onclick="myFunctionbygeneric()">Query selector</button>
Use can use like this
let myImage = document.querySelector('img');
myImage.onclick = function() {
let mySrc = myImage.getAttribute('src');
if(mySrc === 'https://www.w3schools.com/jsref/compman.gif') {
myImage.setAttribute ('src','https://www.w3schools.com/jsref/hackanm.gif');
} else {
myImage.setAttribute ('src','https://www.w3schools.com/jsref/compman.gif');
}
}
<img src="https://www.w3schools.com/jsref/hackanm.gif" />

For Each is not Working for Created Element By Click

What I want : Whenever i click on any "hello" world it should alert me Hello, even on these "Hello's" they are Created By Button
Here is My code I tried For(of) and ForEach() but nothing works for me
Here is my try
let btn = document.getElementById("kick");
btn.onclick = function() {
let NewP = document.createElement("p");
let createText = document.createTextNode("Hello");
NewP.appendChild(createText);
NewP.classList.add("hello");
document.body.appendChild(NewP);
}
let allClasshello = document.getElementsByClassName("hello");
let allClasshelloAr = Array.from(allClasshello);
allClasshelloAr.forEach(function popup(elemetnWithHello) {
elemetnWithHello.onclick = function() {
window.alert("Hello")
}
})
<p class="hello">Hello</p>
<button id="kick">Create ME</button>
New in Javascript
forEach only gets called at the beginning of this code, before the user has added any elements, so the onclick event is only added to the first one. You need to add the onclick function to each newly added element:
btn.onclick = function(){
let NewP = document.createElement("p");
...
NewP.onclick = function(){
window.alert("Hello")
}
}
You can add the onclick event when you create the new element.
let btn = document.getElementById("kick");
btn.onclick = function() {
let NewP = document.createElement("p");
let createText = document.createTextNode("Hello");
NewP.appendChild(createText);
NewP.onclick = popup;
NewP.classList.add("hello");
document.body.appendChild(NewP);
}
function popup() {
window.alert("Hello")
}
<p class="hello" onclick="popup()">Hello</p>
<button id="kick">Create ME</button>
You have to distinguish between synchronous and asynchronous code.
let btn = document.getElementById("kick");
btn.onclick = async;
let allClasshello = document.getElementsByClassName("hello");
let allClasshelloAr = Array.from(allClasshello);
allClasshelloAr.forEach(function popup(elemetnWithHello) {
elemetnWithHello.onclick = async;
})
So, at the moment forEach is called, the btn.onclick handler wasn't called, reaching only <p class="hello"> present on the DOM.
Taking your code, the moment to assing the .hello handler is when you create the element...
let btn = document.getElementById("kick");
btn.onclick = function() {
let NewP = document.createElement("p");
let createText = document.createTextNode("Hello");
NewP.appendChild(createText);
NewP.classList.add("hello"); // you dont need this if isn't used by CSS
NewP.onclick = handleHelloClick;
document.body.appendChild(NewP);
}
let allClasshello = document.getElementsByClassName("hello");
let allClasshelloAr = Array.from(allClasshello);
allClasshelloAr.forEach(function popup(elemetnWithHello) {
elemetnWithHello.onclick = handleHelloClick;
})
function handleHelloClick() {
window.alert("Hello")
}
If you had a container for those <p> you can take advantage to the addEventListener API, subscribing to the container instead of each element...
let container = document.createElement("div");
container.addEventListener("click", containerClickHandler);
document.body.appendChild(container);
function containerClickHandler() {
window.alert("Hello");
}
let btn = document.getElementById("kick");
btn.onclick = function() {
let NewP = document.createElement("p");
let createText = document.createTextNode("Hello");
NewP.appendChild(createText);
container.appendChild(NewP);
}
When the forEach is run there is no paragraph element (NewP). To solve this problem there are two options I suggest:
Add the onclick event listener to NewP element where you are creating it.
Have another action that calls your below code (wrapped inside a function) on-demand to assign onclick event listener
Here are the sample working solutions:
Option 1 Solution - Add event listener upfront
Option 2 Solution - Add event listener on demand
You need an event handler to be set up so that you know when an element is clicked. My example uses a technique called event delegation which basically means we add a single event handler to a parent DOM element - then, when the event bubbles up, we check the event.target to see the exact element that was clicked.
Something like below - check the comments in the code:
// Vars to keep a reference to your DOM elements
const buttonEl = document.querySelector('#buttonEl');
const containerEl = document.querySelector('#containerEl');
// Event handling functions
const pHandler = e => {
if (e.target.tagName === 'P') { //This line also part of the event delegation
console.log(e.target.textContent);
}
};
const buttonHandler = (e, containerEl) => {
const pEl = document.createElement("p");
pEl.appendChild(document.createTextNode("Hello"));
containerEl.appendChild(pEl);
};
//Add the click handlder to the button so it will create more elements when clicked
buttonEl.addEventListener('click', e => buttonHandler(e, containerEl))
//Add a click handler to the parent container - this is called event delegation
containerEl.addEventListener('click', pHandler)
<input id="buttonEl" type="button" value="Create HELLO" />
<div id="containerEl">
<p class="hello">Hello</p>
</div>
For dynamically created elements you'd have to attach the vent listener (in this case it's a click event) when you're creating them (and before appending them to the document).
See the next example :
const btn = document.getElementById('kick'),
alreadyInDocHello = document.querySelectorAll('.hello'); /** selecting the already in the document element to handle click event (those created dynamically will not be included here) **/
sayHello = e => {
e && e.preventDefault();
alert('Hello !');
}; /** sayHello is the function that will be called when having a click event (basically it'll show an alert saying "hello !" **/
/** add click listener for create button **/
btn.addEventListener('click', e => {
let newEl = document.createElement('p'),
createText = document.createTextNode('Hello');
e.preventDefault();
newEl.appendChild(createText);
newEl.classList.add('hello');
/** add the listener here **/
newEl.addEventListener('click', sayHello); /** don't forget that sayHello is declared above **/
document.body.appendChild(newEl);
});
/** click event listener for those elements that are already present in the document **/
alreadyInDocHello.forEach(el => el.addEventListener('click', sayHello));
.hello {
background: #242424;
color: #fff;
padding: 15px;
text-align: center;
transition: all .4s 0s ease;
}
.hello:hover {
background: #ccc;
color: #000;
}
<p class="hello">Hello</p>
<button id="kick">Create ME</button>
Learn more about addEventListener function.

Categories

Resources