This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed last year.
I am trying to create a todolist type webpage and I ran into this problem. Basically when I change or update the list element, the list does not get updated and is still looping through the first 2 buttons in its default html
index.html:
<body>
<script src="script.js" defer></script>
<link href="styles.css">
<div id="essentials"></div>
<h2>ToDoList</h2>
<input id="essentialInput">
<button id="addTask">Add Task</button>
</div>
<list id="list">
<h3 id="0">something epic <button class="deleteButton" id="0">Delete</button></h3>
<h3 id="1">Something else <button class="deleteButton" id="1">Delete</button></h3>
</list>
</body>
script.js:
const essentialInput = document.getElementById('essentialInput')
const addTaskButton = document.getElementById('addTask')
const list = document.getElementById('list')
let button = document.querySelectorAll('.deleteButton')
addTaskButton.addEventListener('click', function() {
text = essentialInput.value
if(text == null) return "Nothing in input field"
children = list.childElementCount
list.innerHTML += `<h3 id=${children}>${text} <button class="deleteButton" id=${children}>Delete</button></h3>`
})
for(let i = 0; i<button.length; i++) {
button[i].addEventListener('click', function() {
console.log(button[i].id)
})
}
I know the issue but I dont know how to fix it. I've tried to put the button eventListeners in a change event listener for when the list changes but then nothing happens until I click a delete button.
The problem is that using the += operator on an element's innerHTML is NOT just concatenating the new HTML... It wholy replaces it with the result.
That is why you were loosing the listeners already set on the two first <h3>.
Then, the new <h3> never had any listeners on them.
So the solution for this case is to register the event listener on the static parent on behalf of its childrens... And take advantage of event bubbling. That is called event delegation.
const essentialInput = document.getElementById('essentialInput')
const addTaskButton = document.getElementById('addTask')
const list = document.getElementById('list')
let button = document.querySelectorAll('.deleteButton')
addTaskButton.addEventListener('click', function() {
text = essentialInput.value
if(text == null) return "Nothing in input field"
children = list.childElementCount
list.innerHTML += `<h3 id=${children}>${text} <button class="deleteButton" id=${children}>Delete</button></h3>`
})
// Add the event listener on the static parent
list.addEventListener("click", function(e){
if(e.target.classList.contains("deleteButton")){
console.clear()
console.log(e.target.id)
}
})
<body>
<script src="script.js" defer></script>
<link href="styles.css">
<div id="essentials"></div>
<h2>ToDoList</h2>
<input id="essentialInput">
<button id="addTask">Add Task</button>
</div>
<list id="list">
<h3 id="0">something epic <button class="deleteButton" id="0">Delete</button></h3>
<h3 id="1">Something else <button class="deleteButton" id="1">Delete</button></h3>
</list>
</body>
Related
I am creating new rows using jquery and want to delete that row when delete button is pressed. Adding new row part is working fine and the problem is in delete part. When I click on delete button then nothing happens. It doesn't even show alert which is written in code. It seems to me like delete button is not even getting pressed.
How can I delete that particular record when delete button is pressed?
JSfiddle is given below
https://jsfiddle.net/ec2drjLo/
<div class="row">
<div>
Currency: <input type="text" id="currencyMain">
</div>
<div>
Amount: <input type="text" id="amountMain">
</div>
<div>
<button id="addAccount">Add Account</button>
</div>
</div>
<div id="transactionRow">
</div>
As you have added the elements as a string, they are not valid HTML elements and that's why you can't add an event listener. You can add the click event to the document body and capture the event target, like
$(document).on('click', function (e){
if(e.target.className === 'deleteClass'){
//process next steps
}
}
You can try the demo below, not sure if it's the result you need, but the delete button works. Hope it helps!
let accountCount = 0;
$("#addAccount").click(function (e)
{
accountCount++;
let mystring = "<label class=\"ok\" id=\"[ID]\">[value]</label>";
let deleteString = "<button class=\"deleteClass\" id=\"deleteAccount"+ accountCount +"\">Delete Account</button>";
let currency = mystring.replace("[ID]", "currency"+ accountCount).replace("[value]", $("#currencyMain").val());
let amount = mystring.replace("[ID]", "amount"+ accountCount).replace("[value]", $("#amountMain").val());
$("#transactionRow").append(currency);
$("#transactionRow").append(amount);
let div = document.createElement('div');
div.innerHTML =deleteString;
$("#transactionRow").append(div);
$("#currencyMain").val('');
$("#amountMain").val('')
});
$(document).on('click', function (e)
{
if(e.target.className === 'deleteClass'){
var content = $("#transactionRow").html();
var pos = content.lastIndexOf("<label class=\"ok");
if(pos > 5)
$("#transactionRow").html(content.substring(0,pos));
else
alert("You cannot delete this row as at least one Account must be present");
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="row">
<div>
Currency: <input type="text" id="currencyMain">
</div>
<div>
Amount: <input type="text" id="amountMain">
</div>
<div>
<button id="addAccount">Add Account</button>
</div>
</div>
<div id="transactionRow" style="border: 1px solid grey">
</div>
I got a problem with one of the sites with JavaScript, and I need to automate a click and then find out how many turns I got before I run out of them. As in, for example, let's say I have 8 turns. So what I would need is to automatically have JavaScript to trigger said div id, 8 times. (As in, I add like this)
Link:https://jsfiddle.net/yxsgp8tc/
<body>
<button id="test">Test</button>
<p>
On box should be number of tests
</p>
<form>
<label><input type="text"/>00-99</label>
<button>
trigger it
</button>
</form>
</body>
in plain javascript, you would target unique elements (using an id) by using document.getElementById('<element_id'). If you wanted to target a class, you would document.querySelector('.<class_name>') for the first instance of the class, or document.querySeletorAll('.<class_name>')
Also, your input tag was misspelled "imput", and is a singleton tag so you don't have to close it off.
Assuming you wanted a way to trigger a click event, here's a basic example:
<head>
<script>
const test = document.getElementById('test');
const trigger = document.getElementById('trigger')''
test.addEventListener('click', () => {
const num_test = document.getElementById('num_tests').value;
for (let i = 0; i < num_test; i++) {
trigger.click();
}
});
trigger.addEventListener('click', () => {
console.log('trigger clicked');
});
</script>
</head>
<body>
<button id="test">Test</button>
<p>
On box should be number of tests
</p>
<form>
<input type="text" id="num_tests" value="">
<button id="trigger">
trigger it
</button>
</form>
</body>
https://jsfiddle.net/qr1z3d6e/2/
first in the jsfiddle.net link there are some errors such as imput instead of input.
I haven't tested it, but if I understand correctly is it something like this? try it.
<body>
<input id="myinput" type="text">00-99</input>
<button id="clickme">
</body>
<script>
var button = document.getElementById("clickme"),
count = 99;
var myInput = document.getElementById("myinput")
button.onclick = function(count){
count -= 1;
myInput.innerHTML = "00 " + count;
};
</script>
I am new to Javascript and I try to add an event listener for each button on every card, but the code make the last card (button) only have the event 'click' so is there's any way to make it happen with innerHTML card
this is the code:
let tracksRender = (track) => {
track.forEach(element => {
//this the card that will add the button for
let card = `<div class="card">
<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
${element.title}
</div>
</div>
</div >`;
//here i add the card to DOM
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
// store the content of the button
let inBtn = `<i class="add icon"></i>
<span>Add to playlist</span>`;
// created button container
let btn = document.createElement("div");
btn.classList.add("ui", "bottom", "attached", "button", "js-button");
// added the content of the button
btn.innerHTML += inBtn;
// here i add the the event Listener to the button
btn.addEventListener('click', () => {
console.log("click");
});
//here i add the button to the last card have been created
searchResults.querySelector(".card:last-child").append(btn);
});
}
and the structure:
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>SoundCloud Player</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css'>
<link rel='stylesheet' href='styles/main.css'>
<style></style>
</head>
<body id="soundcloud-player">
<div class="ui container col">
<div class="col">
<div class="main">
<div class="js-search-results search-results ui cards">
</div>
</div>
</div>
</div>
<script src="https://connect.soundcloud.com/sdk/sdk-3.3.2.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js'></script>
<script src="javascript/main.js"></script>
</body>
</html>
Fiddle: https://jsfiddle.net/y73bstju/7/
but it will add the event to the last card only
It might help to create elements instead of appending innerHTML:
let tracksRender = (track) => {
// Select the results here, so you wont have to repeat it
const searchResults = document.querySelector('.js-search-results');
track.forEach(element => {
// Create the card, give it its class and innerHTML
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
${element.title}
</div>
</div>`;
// Created the button, give its classes and innerHTML
const btn = document.createElement('div');
btn.className = 'ui bottom attached button js-button';
btn.innerHTML = '<i class="add icon"></i><span>Add to playlist</span>';
// Add the event listener
btn.addEventListener('click', () => {
console.log('click');
});
// Append the button to the created card
card.appendChild(btn);
// Add the card to the results
searchResults.appendChild(card);
});
}
I agree with you that theoretically the object should have the event, but the behavior we experience is that whenever another write happens at the relevant section of the DOM, the event handler is lost, which is the reason the last element has the click event. So, let's write first into the DOM and only when we are done with that should we add the event listeners, like:
let SoundCloudAPI = {};
SoundCloudAPI.init = () => {
SC.initialize({ client_id: 'cd9be64eeb32d1741c17cb39e41d254d' });
};
SoundCloudAPI.init();
SoundCloudAPI.getTrack = (inputVlue) => {
SC.get('/tracks', {
q: inputVlue
}).then((tracks) => {
console.log(tracks);
SoundCloudAPI.renderTracks(tracks);
});
}
SoundCloudAPI.getTrack("alan walker");
SoundCloudAPI.renderTracks = (track) => {
track.forEach(element => {
//this the card that will add the button for
let card = `<div class="card">
<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
${element.title}
</div>
</div>
</div >`;
//here i add the card to DOM
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
// store the content of the button
let inBtn = `<i class="add icon"></i>
<span>Add to playlist</span>`;
// created button container
let btn = document.createElement("div");
btn.classList.add("ui", "bottom", "attached", "button", "js-button", "fresh");
// added the content of the button
btn.innerHTML += inBtn;
//here i add the button to the last card have been created
searchResults.querySelector(".card:last-child").append(btn);
});
for (let btn of document.querySelectorAll('.ui.attached.button.js-button.fresh')) {
// here i add the the event Listener to the button
btn.addEventListener('click', () => {
console.log("click");
});
}
}
Fiddle: https://jsfiddle.net/r84um9pt/
I think:
you're doing:
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
Serializing again and again in your div "searchResults"
innerHTML ~erases~ listeners, and probably it is ~erasing~ your previous listeners. Besides, i don't remember where i read that innerHTML script code cannot run (for security purposes)
From https://medium.com/#kevinchi118/innerhtml-vs-createelement-appendchild-3da39275a694
using innerHTML reparses and recreates all DOM nodes inside the div
element and is less efficient than simply appending a new element to
the div. In the above cases, createElement is the more performant
choice.
Be careful:
Using "append" again and again uses badly browser resources, redrawing many times.
But, you can append in a documentFragment and append it to the div -js-search-results
DocumentFragment:
https://developer.mozilla.org/es/docs/Web/API/DocumentFragment
Welcome in the community. In you're code you're adding many classes to button. You can add event Listener to any one of the unique class name which is specifically applied on button element only.
You can replace:
btn.addEventListener('click', () => {
console.log('click');
});
with:
document.querySelectorAll('.js-button').forEach(el=>{
el.addEventListener('click', (event) => {
event.preventDefault();
console.log("click");
});
});
Also, it would be good if you add eventListener out of the loop in which you are appending elements.
This question already has answers here:
Increment a number inside a div?
(4 answers)
Closed 3 years ago.
I am creating buttons on click I would like to increment button text on every time a user creates a new button
HTML
<button id="btn">Add button</button>
<div id="movie-block">
</div>
$("#btn").on("click", function(){
var newMovieBlockButton = $("<div class='movie-button w'>Button1<div>");
$("#movieblock" + movieid).append(newMovieBlockButton);
})
I want when user click add button new button should be created starting with eg
button1, if he creates another button it should be button2 etc etc
How can I accomplish that using jquery?
In each click, you can take the length of the button with class movie-button and concatenate that with the text:
$("#btn").on("click", function(){
var len = $('.movie-button').length + 1;
var newMovieBlockButton = $("<div class='movie-button w'>Button"+ len +"<div>");
$("#movie-block").append(newMovieBlockButton);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<button id="btn">Add button</button>
<div id="movie-block">
</div>
A quick google search would have helped.
<button id="btn">Add button</button>
<div id="movie-block">
</div>
let counter = 1;
$("#btn").on("click", function(){
var newMovieBlockButton = $(`<div class='movie-button w'>Button${counter}<div>`);
$("#movieblock" + movieid).append(newMovieBlockButton);
counter++;
})
<!DOCTYPE html>
<html>
<body>
<button onclick="append()">Try it</button>
<div id="myDIV">
New Paragraphs will add on this div
</div>
<script>
function append() {
var para = document.createElement("P");
para.innerHTML = "This is a paragraph.";
document.getElementById("myDIV").appendChild(para);
}
</script>
</body>
</html>
Hope this will help
My view has a button and the view is looped.so it has raws.
when i click the button of a single raw i need to color that button.
so i added a onclick="select_Button(<?php echo $rawID?>)" to the raw's button in my view
select_Button is my funtion in js
function select_Button(rawNumberOfVote) {
var RawNumber = rawNumberOfVote;
alert ("Form submitted successfully" + RawNumber);
var upVote = document.getElementById("up_vote");
upVote.style.background = "green";
}
like above i send the rawID to the funtion.
how can i edit this line to accept the view called up_vote in that particular raw id that i got from parameter.
var upVote = document.getElementById("up_vote");
becuz if i only use this line it will color the first raw's button instead the one i wanted
Thank you
you can use data attribute in your html referencing to this page and this page. and retraive with this this jquery code snippet:
$("[data-test ='my value']")
or this code snnipet in javascript:
document.querySelectorAll(".example").find(function(dom){
return dom.dataset.test == "expected-value"
});
Update:
accourding to this page querySelectorAll return nodeList and NodeList are not array and we cannot use find method so I change my answer to this code:
<html>
<body>
<div class="post" data-key="1">
<lable>test</lable>
<button type="button" onclick="upvote(1)">up vote</button>
</div>
<div class="post" data-key="2">
<lable>test</lable>
<button type="button" onclick="upvote(2)">up vote</button>
</div>
<div class="post" data-key="3">
<lable>test</lable>
<button type="button" onclick="upvote(3)">up vote</button>
</div>
</body>
</html>
<script type="text/javascript">
var upvote = function(id) {
var nodes = document.querySelectorAll(".post");
console.log(nodes.length);
for(i = 0 ; i < nodes.length ; i++){
console.log(nodes[i].dataset.key);
if (nodes[i].dataset.key == id)
nodes[i].style.backgroundColor = "red";
}
};
</script>