So I am creating a few buttons dynamically via javascript.
function dobutton() {
for (i=0; i<eigenschaften; i++){
var btn = document.createElement("BUTTON");
btn.className = 'button black';
btn.id = eigenschaftsarray[i];
btn.name = 'pickbutton';
btn.innerHTML = eigenschaftsarray[i].split("_").join(" ");
document.getElementById('auswahl').appendChild(btn);
document.getElementById('auswahl').innerHTML += " ";
}
}
eigenschaften = 39
eigenschaftsarray = just some names
Now I want to get the ID of the button I click. I was not able to get anything from this JavaScript - onClick to get the ID of the clicked button running because of my method using js to create those buttons. Adding the onClick event to my code just instantly alerts 39 "unknown".
Can someone provide me some help, I am just using Javascript, no jQuery :)
Thanks!
When you create elements dynamically, you have to keep in mind that you can bind events to them only after they are available in the DOM.
Here is a working demo: jsfiddle demo
In the demo, we bind an event listener ("click") to the parent that contains the buttons. The parent is a static element, already available in the DOM.
The JavaScript code is:
var eigenschaften = 3;
var eigenschaftsarray = ["b0","b1","b2"];
// fn: detect if a button was clicked -> get id
function buttonClick(e){
// check if the clicked element is a button
if (e.target.tagName.toLowerCase() == "button") {
var btn = e.target;
// alert the user what button was clicked
alert("button id="+btn.id);
}
}
// fn: create buttons dynamically
function dobutton() {
// identify parent
var parent = document.getElementById('auswahl');
// create buttons dynamically
for (i=0; i<eigenschaften; i++){
var btn = document.createElement("button");
btn.className = 'button black';
btn.id = eigenschaftsarray[i];
btn.name = 'pickbutton';
btn.innerHTML = eigenschaftsarray[i].split("_").join(" ");
// append btn to parent
parent.appendChild(btn);
parent.innerHTML += " ";
}
// add "click" listener on parent
parent.addEventListener("click", buttonClick);
}
// create buttons
dobutton();
Step 1: Add Buttons Dynamically To DOM
HTML :
<body>
<h1>
HelloWorld HelloWorld
</h1>
<div id="stackMe"></div>
</body>
Javascript :
const data = [{'Name' : 'Hello', 'Id' : 1},
{'Name' : 'World', 'Id' : 2}];
window.addEventListener('DOMContentLoaded', function (){
console.log('DOM loaded');
generateButtons(data);
});
function generateButtons(data) {
const buttonResults = data.map(bt =>`<button id= "${bt.Id}" onClick="btClicked(${bt.Id})">Button ${ bt.Name }</button>`);
document.getElementById("stackMe").innerHTML = buttonResults;
}
Step 2: Add an event listener for the button clicked
function btClicked(currentId) {
let elementClicked = data.find(d => d.Id == currentId);
console.log(elementClicked);
}
Output: Out after the button is clicked
Related
my main project is too complicated to show here so I created a small script demonstrating the problem I am working on. In simple terms, I need to create a button that once clicked, generates a button that also has an event listener that returns that button's id to the console.
See code below:
button_number = 0
create_buttons = document.getElementById('create_buttons')
div = document.getElementById('div')
create_buttons.addEventListener('click', e=>{
button_number += 1
new_button = document.createElement('button')
new_button.setAttribute('id', 'button'+button_number)
new_button.innerHTML = 'What number am I?'
new_button.addEventListener('click', show_button_number)
div.appendChild(new_button)
})
function show_button_number () {
let number = button_number
button = document.getElementById('button' + number)
console.log(button.id)
}
<div id="div">
<button id="create_buttons">Create a button!</button>
</div>
As written, all generated buttons return the button id of the most recently generated button versus their own id. Is there anyway I can change the anonymous function to return the button id of the button that was clicked? In order to integrate this into my main project, I need to create the event listener for the dynamically generated buttons using an anonymous function.
You could make a higher-order function, one that takes the current button number as an argument and returns a function using it:
const makeListener = num => () => {
const button = document.getElementById('button' + num)
console.log(button.id)
};
new_button.addEventListener('click', makeListener(button_number))
Or, you may not need the ID at all, just pass the element itself:
create_buttons.addEventListener('click', e=>{
const btn = div.appendChild(document.createElement('button'));
btn.textContent = 'What number am I?'
btn.addEventListener('click', () => {
console.log(new_button);
});
});
The major way to do that is to use event delegation mechanim
const divParent = document.getElementById('div')
var button_number = 0
divParent.addEventListener('click', e =>
{
if (!e.target.matches('button')) return // ignore clicks from other things
if (e.target.id === 'create_buttons')
{
let new_button = document.createElement('button')
new_button.id = 'button' + ++button_number
new_button.textContent = 'What number am I?'
divParent.appendChild(new_button)
}
else
{
console.clear()
console.log( e.target.id )
}
})
<div id="div">
<button id="create_buttons">Create a button!</button>
</div>
Think of the Uno Card Game logic for example, how could I get the clicked Card on the players hand, when I create the cards on the players hand dynamically by clicking on a draw button?
something like:
var btn = document.createElement("BUTTON");
btn.onclick = () => {
alert(btnCounter);
}
btnCounter++;
but the onclick function should be written once and then be saved for this button.
One option is to rely on event bubbling. We can add the event listener to the container for our hand and then, on click, make sure the clicked element was a button. In this example, I put our card's value in the button's dataset, but you can grab the info from anywhere on the target.
const hand = document.querySelector("#hand");
hand.addEventListener("click", function(e) {
if (e.target.tagName !== "BUTTON") return;
console.log(e.target.dataset.card);
});
function drawCards(numberOfCards) {
for (let i = 0; i < numberOfCards; i++) {
const button = document.createElement("button");
const card = Math.floor(Math.random() * 10);
button.setAttribute("data-card", card);
button.innerHTML = card;
hand.appendChild(button);
}
};
drawCards(5);
<div id="hand"></div>
In the context where the button (and its id) are created, bind the id in the o'clock function...
const ids = ['1', '2', '3'];
var parent = document.getElementById('parent')
ids.map(id => {
let btn = document.createElement('button');
btn.setAttribute('id', id)
btn.innerHTML = `Button ${id}`
parent.appendChild(btn)
btn.onclick = event => {
alert(id)
}
})
<div id="parent">
</div>
I have a button that append a template containing few divs and buttons.
The first button "btnGenerateResult_0" works fine probably because it exists when the page loads, while "btnGenerateResult_1" gets created but doesn't work.
How and where can I fix the event listener and attach it to these buttons, I need four buttons?
The bellow code is inside a document.readey function():
$(`#btnGenerateResult_${active}`).click(function ()
{
var newrow = "";
for (var i = 1; i < TablesObj[activeTableObj[active]].length; i ++ )
{
newrow = "";
newrow= "<tr><td>" + TablesObj[activeTableObj[active]][i][0] +
"</td><td>" + TablesObj[activeTableObj[active]][i][3] +
"</tr>";
$(`#resultTableMain_${active}`).append(newrow);
}
});
One option is to add the event listener to the newly created button after you have created it:
const container = document.getElementById('container');
function handleButtonClicked(e) {
console.log(`Button ${ e.target.textContent } clicked!`);
}
Array.from(container.children).forEach((button) => {
button.onclick = handleButtonClicked;
});
setTimeout(() => {
const newButton = document.createElement('BUTTON');
newButton.textContent = 'C';
container.appendChild(newButton);
// Add the event listener to this new button as well:
newButton.onclick = handleButtonClicked;
}, 2000);
<div id="container">
<button>A</button>
<button>B</button>
</div>
Another option, which might scale better, is to use event delegation. It consists of adding a single event listener to the parent or any common ancestor for all those buttons. The click event will then bubble app and you can use e.target to find out which button the event originated from:
const container = document.getElementById('container');
container.onclick = (e) => {
if (e.target.tagName !== 'BUTTON') {
console.log('Something else clicked...');
return;
}
console.log(`Button ${ e.target.textContent } clicked!`);
};
setTimeout(() => {
// See how this works with dynamically created buttons as wel, withiout adding the
// event listener to each of them individually. However, the event might be
// triggered from undesired elements as well (click in the space between the
// buttons), so you need to check for that, as you can see above.
const newButton = document.createElement('BUTTON');
newButton.textContent = 'C';
container.appendChild(newButton);
}, 2000);
<div id="container">
<button>A</button>
<button>B</button>
</div>
I have a "+" button that , when clicked, triggers the creation of a block with an input and 2 buttons, one for validating the input and one for removing it.
// My code looks almost like this :
addBtn.addEventListener('click', e => {
addClick++;
// All the elements of the same line (input and 2 buttons) have an int in common in their id string ==> addClick
// I'm missing all the declarations of the variables here
blockDiv.appendChild(posteInput);
blockDiv.appendChild(validateBtn);
blockDiv.appendChild(deleteBtn);
globalPostesBlock.appendChild(blockDiv)
let allDeleteBtn = document.getElementsByClassName('delete-button');
for (let i = 0; i < allDeleteBtn.length; i++) {
allDeleteBtn[i].addEventListener('click', e => {
// Retrieving the block with the same id
let deleteBtnId = parseInt((allDeleteBtn[i].getAttribute('id').match(/\d+/g)).toString());
let singlePosteBlock = document.getElementById(`poste-block-${deleteBtnId}`);
singlePosteBlock.remove();
}
})
}
The event listener represents the action of clicking the delete button so it can remove its entire containing block
I have the same logic for the validate button, but I'm using ajax in it.
Each time I create a new block, I want to create an event listener associated to this block, but all I found so far is an event listener with a loop on every buttons, so what happens is that the action triggers as many time as the block numbers because of the loop, but I don't know how to dissociate every event listeners.
If I have 3 blocks and I validate one input value which is being inserted in the DB afterwards, the value is being inserted 3 times.
Does this help ?
//id pool
let latestId = 0;
//retrive the button
var myButton = document.querySelector("button");
myButton.addEventListener("click", createKids);
//function declaration :: createKids
function createKids() {
latestId++;
//declare and initialization
let div = document.createElement("div");
let input = document.createElement("input");
let buttonSend = document.createElement("button");
let buttonDelete = document.createElement("button");
//append input & buttons to div
div.appendChild(input);
div.appendChild(buttonSend);
div.appendChild(buttonDelete);
//Some beautifying
buttonSend.innerText = "Send me";
buttonDelete.innerText = "Delete me";
//some logic
div.dataset.id = latestId;
//event handeling
buttonSend.addEventListener("click", sendItem);
buttonDelete.addEventListener("click", deleteItem);
//insert div
document.body.appendChild(div);
}
function sendItem(event) {
//do action and delete ?
let input = event.target.parentNode.querySelector("input");
//retrive data
let val = input.value;
let id = event.target.parentNode.dataset.id;
//disable input for fun ^^
input.disabled = true;
//console istead of send
console.log(id,val);
//handle some more
setTimeout(() => {
event.target.parentNode.remove();
}, 3000);
}
function deleteItem(event) {
event.currentTarget.parentNode.remove();
}
<p>Does this help?</p>
<button>Magic Button</button>
I am currently learning Javascript to understand it better. When you add an item to the list, I would like to have a delete button to remove it.
It seems to me when I do deleteButton.parentNode.parentNode.removeChild(), it should remove the li element, but not sure why this is not working.
JSFiddle here: http://jsfiddle.net/kboucheron/XVq3n/18/
addButton = document.getElementById('addButton');
clearButton = document.getElementById('clearButton');
deleteButton = document.getElementById('deleteButton');
addButton.addEventListener("click", function(e) {
var text = document.getElementById('listItem').value;
var addItem = document.getElementById('output');
var entry = document.createElement("li");
text += ' <button id="deleteButton">Delete</button>';
entry.innerHTML = text;
addItem.appendChild(entry);
});
clearButton.addEventListener("click", function(e) {
var text = document.getElementById('listItem');
var addItem = document.getElementById('output');
addItem.innerHTML = '';
text.value = '';
});
deleteButton.addEventListener("click", function(e) {
deleteButton.parentNode.parentNode.removeChild();
});
If you see your browser console there is an error saying Uncaught TypeError: Cannot call method 'addEventListener' of null it is because there is no element with id deleteButton when the page is loaded.
Also note that the ID of an element must be unique in a document, so since you are adding multiple delete buttons use a class attribute to group them.
Your code should be
//wait for the window onload event
window.onload = function () {
var addButton = document.getElementById('addButton');
var clearButton = document.getElementById('clearButton');
addButton.addEventListener("click", function (e) {
var text = document.getElementById('listItem').value;
var addItem = document.getElementById('output');
var entry = document.createElement("li");
entry.innerHTML = text;
//create a button element
var btn = document.createElement("Button");
btn.innerHTML = 'Delete';
//add button to li
entry.appendChild(btn);
//add a click handler to the button
btn.addEventListener("click", function (e) {
//delete the parent li
this.parentNode.parentNode.removeChild(this.parentNode);
});
addItem.appendChild(entry);
});
clearButton.addEventListener("click", function (e) {
var text = document.getElementById('listItem');
var addItem = document.getElementById('output');
addItem.innerHTML = '';
text.value = '';
});
})
Demo: Fiddle
Since you tag jQuery, you can use a technique call event delegation:
$(document).on('click','.deleteButton', function() {
$(this).parent().remove();
});
In this case, It will helps you to bind the click event to the dynamically created delete button.
Btw, because id is unique so you should use class instead of id for your button
Updated Fiddle