Problem:
How to update the innerHtml of a span within a dynamically generated button using only JavaScript?
I'm building a 'like' feature (think social media) where users can like/unlike posts, but I can't figure out how to select the value of the specific span the user clicks on. I've been only able to find jQuery solutions using .find('span')
const htmlBtn = `<button class="count"><span>0</span></button>`;
const create = document.getElementById("create");
create.addEventListener("click", (e) => {
const container = document.createElement("div");
container.className = "container";
container.innerHTML = htmlBtn;
document.getElementById("app").append(container);
});
const app = document.getElementById("app");
app.addEventListener("click", (e) => {
const btn = e.target;
/*
each time someone click nth button, span within nth button increments by 1
let count = document.querySelector("span").innerHTML;
count++;
document.querySelector("span").innerHTML = count;
*/
});
<button id="create">Create</button>
<div id="app"></div>
Expected outcome:
User clicks the button to increment the span of only that button by 1.
e.target.children will give you an array of all the children of button and the 0th index of the array should be your span element which you can then edit normally using the innerHTML method.
In your case,
let count = parseInt(e.target.children[0].innerHTML);
e.target.children[0].innerHTML = count + 1;
create.addEventListener("click", (e) => {
const container = document.createElement("div");
container.className = "container";
container.innerHTML = htmlBtn;
document.getElementById("app").append(container);
container.addEventListener("click", (e) => {
const value = +e.target.innerHTML;
e.target.innerHTML = (value + 1).toString();
});
});
The methods querySelector() and querySelectorAll() can be called on a specific parent element, similar to find() in jQuery.
Make sure to use e.currentTarget and not e.target in the event listener, in order to refer to the element that handles the event and not to any child element.
Example - click any of numbers to increment.
function clickToIncrement(e) {
const innerSpan = e.currentTarget.querySelector('span');
if (innerSpan) {
innerSpan.textContent++;
}
}
document.getElementById('div1').addEventListener('click', clickToIncrement);
document.getElementById('div2').addEventListener('click', clickToIncrement);
<div id="div1">
<span>0</span>
</div>
<div id="div2">
<span>0</span>
</div>
Related
I have array of objects
toDo.js
export const examples = [
{
question: "xx",
answer: "yy",
},
{
question: "xx",
answer: "yy",
},....]
Then i have following logic and then i import it to index.js. by this piece of code <div id="examples-container"></div> and then also i import it <script src="logic.js" type="module"></script>
I have following problem: Toggle (function) is declared but its value is never read ts(6133). I'm gettin this error when i click on buttons.
logic.js
import { examples } from "./Předměty/Matematika.js";
function toggle(i) {
const div = document.querySelector(`#result_${i}`);
if (div.style.display !== "none") {
div.style.display = "none";
} else {
div.style.display = "block";
}
}
let html = "";
examples.forEach((ex, i) => {
const example = `
<div class="card">
<div class="example">
${ex.question}
</div>
<button type="button" class="toggle" onclick="toggle(${i})">
Toggle
</button>
<div id="result_${i}" style="display:none" class="result">${ex.answer}</div>
</div>`;
html += example;
});
const container = document.querySelector("#examples-container");
container.innerHTML = html;
When i consol.log(examples). I get whole array. Also i've tried create new variable and then insert the array. Whole app worked how i expected when it was in one file but now i try to split it to different files because.. U know it's standart have app devided to more then 1 big file and also i have some future plans so it will be necessary. ^^
Best
Vojtěch
The problem is that the html code cannot access the toggle function. To fix this, you can build the DOM tree manually. So, instead of creating a string with html code in it and appending it onto the container element, you can create each element individually.
const container = document.querySelector("#examples-container");
examples.forEach((ex, i) => {
// Make the div for the <div class="card">
const card = document.createElement("div");
// Add the "card" class to the div
card.classList.add("card");
// Create the div for the <div class="example">${ex.question}</div>
const example = document.createElement("div");
// Add the "example" class
example.classList.add("example");
// Set the HTML inside of it to ex.question
example.innerHTML = ex.question;
// Add it to the card element
card.appendChild(example);
// Create the button for the <button class="Toggle">Toggle</button>
const button = document.createElement("button");
// Add the "toggle" class to the button
button.classList.add("toggle");
// Set the text inside of it to say "Toggle"
button.innerHTML = "Toggle";
// Add the onclick event listener
button.addEventListener("click", () => toggle(i));
// Add the toggle button to the card
card.appendChild(button);
// Create the div for the <div id="result_${i}" class="result">${ex.answer}</div>
const result = document.createElement("div");
// Add the id for the element
result.id = "result_" + i;
// Hide the result element (display: none)
result.style.display = "none";
// Add the "result" class
result.classList.add("result");
// Set it's HTML to the answer
result.innerHTML = ex.answer;
// Add the result element to the card
card.appendChild(result);
// Add the card to the container
container.appendChild(card);
});
I need to clone a div that contains an input file, and within the clone, there is a button to delete the created clone.
My problem is that once the clone is created I cannot add the function on the button to delete the clone.
The function does not work. Where am I wrong?
if (document.querySelector('.clona-input-file') !== null) {
var clonaInputFile = document.querySelector('.clona-input-file');
clonaInputFile.addEventListener('click', function(e) {
e.preventDefault();
var RowDaClonare = document.querySelector('#row-da-clonare');
var clone = RowDaClonare.cloneNode(true);
clone.children[0].lastElementChild.value = '';
clone.id = 'row-da-clonare-' + Date.now();
RowDaClonare.after(clone);
var _buttonDel = document.createElement("button");
_buttonDel.id = 'cancellaInputClone';
_buttonDel.type = 'button';
_buttonDel.setAttribute("data-id-da-eliminare", clone.id);
_buttonDel.classList.add("btn");
_buttonDel.classList.add("btn-danger");
_buttonDel.classList.add("cancellaInputClone");
_buttonDel.innerHTML = '<i class="bi bi-trash-fill"></i>';
clone.appendChild(_buttonDel);
});
}
var cloneSet = document.querySelectorAll(".cancellaInputClone");
for (var i = 0; i < cloneSet.length; i++) {
cloneSet[i].addEventListener('click', fx_button);
}
function fx_button() {
console.log(this)
}
The issue is because you're attempting to bind event handlers to elements which don't yet exist in the DOM. This can be addressed by delegating your event handlers to parent elements which do exist when the DOM loads, and interrogating the events to see if they were raised by the elements you created.
In addition there's some other issues in your code to address:
Firstly, don't use id attributes in dynamic content. It makes your logic more complex than it needs to be. Use classes instead, and relate elements to each other using DOM traversal methods, such as closest().
Secondly, use querySelector() to find the child element, not children/index accessors. It's more robust.
Lastly, you can provide multiple separate class names to classList.add() to save you having to call it repeatedly.
With that said, try this working example:
let cloneButton = document.querySelector('.clona-input-file');
if (cloneButton) {
cloneButton.addEventListener('click', function(e) {
e.preventDefault();
let rowDaClonare = document.querySelector('.row-da-clonare'); // querySelector will return first match only
let clone = rowDaClonare.cloneNode(true);
clone.querySelector('input').value = '';
rowDaClonare.after(clone);
let buttonDel = document.createElement("button");
buttonDel.type = 'button';
buttonDel.classList.add("btn", "btn-danger", "cancellaInputClone");
clone.appendChild(buttonDel);
let icon = document.createElement('i');
icon.classList.add('bi', 'bi-trash-fill');
buttonDel.appendChild(icon);
});
}
// icon click handler delegated to the .container element
document.querySelector('.container').addEventListener('click', e => {
let el = e.target;
if (!el.classList.contains('cancellaInputClone') && !el.closest('button')?.classList.contains('cancellaInputClone'))
return;
el.closest('.row-da-clonare').remove();
});
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons#1.3.0/font/bootstrap-icons.css">
<div class="container">
<button class="clona-input-file">Clone</button>
<div class="row-da-clonare">
<div>
Lorem ipsum dolor sit.
<input type="text" class="foo" />
</div>
</div>
</div>
According to your code, it looks like the button is created only when the clonaInputFile is clicked, but the querySelectorAll and loop and addEventListener executes right after you registered the callback for the clonaInputFil. So now there is no button you are querying, and the cloneSet should be empty, if you haven't created before.
Try log out the length of cloneSet. If it is 0, I recommend you to put the addEventListener('click', fx_button); right after the creation of that button.
I'm trying to update the number of participants by increasing it by one every time the submit button gets clicked, and by doing so I added an add() inside my script tag and increased the participant number every time it gets clicked. But the number of participants doesn't get changed for some reason.
Btw I'm new to DOM and JS
let Agenda_style = document.querySelector('.agenda'); //to add style you must acess the class name/ID using querySelector
Agenda_style.style.color = "red "; // set color to red to change the color of the class agenda
let NewElement = document.createElement("li "); //create new element of type <li>
NewElement.innerText = "Here "; // add a text inside the <li> element
Agenda_style.append(NewElement); // append a tet to the agendaa class
let participant = 0;
function add() {
participant++;
document.getElementById("submit").innerText = participant;
};
<h5>Number of participant:<span id="submit">0</span></h5>
<button type="button" onclick="add()">Submit</button>
</div>
It fails for me, as I have now .agenda element.. do you have that in your HTML?
If I put a null check around that section of the script, the remaining piece works.
<h5>Number of participants: <span id="submit">0</span></h5>
<button type="button" onclick="add()">Submit</button>
</div>
<script>
let Agenda_style = document.querySelector('.agenda'); // to add style you must access the class name/ID using querySelector
if(Agenda_style != null) { // only proceed if Agenda_style exists
Agenda_style.style.color = "red "; // set color to red to change the color of the class agenda
let NewElement = document.createElement("li "); // create new element of type <li>
NewElement.innerText = "Here "; // add a text inside the <li> element
Agenda_style.append(NewElement); // append a tet to the agendaa class
}
let participant = 0;
function add() {
participant++;
document.getElementById("submit").innerText = participant;
}
</script>
These days we tend to not use inline JavaScript, so it would be best to grab your button with querySelector, and then use addEventListener to call your function when it's clicked. This way there's a "separation of concerns" between your mark-up, your CSS, and your code.
const number = document.querySelector('#number');
const button = document.querySelector('button');
button.addEventListener('click', add, false);
let participant = 0;
function add() {
number.textContent = ++participant;
}
<h5>Number of participant:
<span id="number">0</span>
</h5>
<button type="button">Submit</button>
This should be working
function add() {
let val = document.querySelector('#submit').innerText;
val = parseInt(val)+1;
}
What i want to do is....when the counter button is clicked....increment a span to see how many times its been clicked but also create a new counter with its own incrementor
I have it somewhat working but when i create a new button....the counter doesnt work
anyone help me out with what im missing?
this is also done in vanilla js and html
template:
<div class="container">
<button type="button" id="increase" onClick="increaseCounter()">click</button>
<span id="amount"></span>
</div>
JS:
var counter = 0
function increaseCounter() {
let button = document.getElementById("increase");
let span = document.getElementById("amount");
counter += 1;
span.innerHTML = counter;
createNewButton();
}
function createNewButton() {
let container = document.getElementsByClassName("container")[0];
let btn = document.createElement("button");
btn.innerHTML = "new button";
btn.id = "increase";
container.appendChild(btn);
let span = document.createElement("span");
span.innerHTML = 0;
span.id = "amount";
container.appendChild(span);
}
LINK: https://codepen.io/zomdar/pen/ZEGPxNP?editors=1011
Here is a few things you should look for:
1- The event listener is only affected to the first button you assign as it is an inline event listener. So new buttons won't have the event listener.
2- If you want to have a new span containing the amount everyone, you must have a different ID for each span (or set the innerHTML directly as I did) or it will be the same span changing.
Note - The ID attribute should be unique in the page, every time you create a button the same ID is attributed. You should use the class attribute (non-unique value) or a different ID every time (e.g. "amount"+counter)
function increaseCounter() {
let button = document.getElementById("increase");
let span = document.getElementById("amount");
counter += 1;
// span.innerHTML = counter;
createNewButton();
}
function createNewButton() {
let container = document.getElementsByClassName("container")[0];
let btn = document.createElement("button");
btn.innerHTML = "new button";
btn.id = "increase"; // UNIQUE ID !!!
btn.onclick = increaseCounter;
container.appendChild(btn);
let span = document.createElement("span");
span.innerHTML = counter;
span.id = "amount";
container.appendChild(span);
}
You can pass along the element that was clicked as a parameter of the increaseCounter function and then get the nextElementSibling of that (the span associated with it), grab the innerHTML (current count) of it, then increment it by 1. This will prevent you from having to fetch the elements by id (as mentioned in the answer by clota974, you want your id's to be unique). You were also missing adding the onclick function for the buttons you were creating as well.
References:
https://www.w3schools.com/jsref/prop_element_nextelementsibling.asp
https://www.w3schools.com/jsref/event_onclick.asp
Here's the example code:
function increaseCounter(el) {
/* increment the span associated with this button (nextElementSibling is the span) */
el.nextElementSibling.innerHTML = parseInt(el.nextElementSibling.innerHTML) + 1;
createNewButton();
}
function createNewButton() {
let container = document.getElementsByClassName("container")[0];
let btn = document.createElement("button");
btn.innerHTML = "new button";
btn.onclick = function() {
increaseCounter(this);
};
container.appendChild(btn);
let span = document.createElement("span");
span.innerHTML = 0;
container.appendChild(span);
}
<div class="container">
<button type="button" onClick="increaseCounter(this)">click</button>
<span>0</span>
</div>
I have a form that adds and subtracts a smaller form when user clicks on add or subtract users button.
what I want to do is add a +1 every time the user hits the add button to a ID tag inside the div tag.. confusing yes I know
say this is the original tag:
<div id="formwrap" class="test" name="test">im a form in this formwrap</div>
now say the user clicks on a add button and a new form will pop up below the current form and using a print or echo version of javascript this would be the new code created for the div using .append(html) or something along those lines too this:
<div id="formwrap" class="test" name="test">im a form in this formwrap</div>
<div id="formwrap" class="test2" name="test2">im a form in this formwrap</div>
and so on as u can see next the 2 would change to a 3 if someone where to click the add button
<div id="formwrap" class="test(+1 code would go here)" name="test(+1 code would go here)">im a form in this formwrap</div>
So before I code is this even possible? I want to be able to style the smaller form differently and I want the phpmailer to be able get retrieve all the data depending on how many clicks they do. and adding a +1 too each of the tags would do both goals.
It is possible using data attributes.
See: https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes
You would do something like this
var count = 0
var formwrap = document.getElementById('formwrap');
formwrap.dataset.name = "aName" + count
// increment count on click
You can use jquery for this when user click on add button you have to increment the value of count like this ...
var count = 0;
$("button").on("click", function () {
var html = '<div id="formwrap'+count+'" class="test'+count+'"
name="test'+count+'">im a form in this formwrap</div>';
count++;
$(div).append(html);
});
this way you can do this.
//Create a private counter var
const counter = (function() {
var formCount = 0;
return {
value: () => formCount,
increment: () => ++formCount
}
})();
//A function that will generate a "form" element
const ID_PREFIX = 'formwrap_';
const generateFormEl = () => {
let el = document.createElement('div');
el.setAttribute('id', `${ID_PREFIX}${counter.increment()}`);
el.setAttribute('class', 'test');
el.setAttribute('name', 'test');
el.appendChild(document.createTextNode(`ID: ${el.getAttribute('id')}`));
return el;
};
//Store your DOM elements in some vars
const forms = document.querySelector('#forms');
const btn = document.querySelector('[type="button"]');
//Add a form element to the DOM on click
btn.addEventListener('click', () => {
const form = generateFormEl();
forms.appendChild(form);
});
<div id="container">
<input type="button" value="Add a form" />
<div id="forms"></div>
</div>