Reputation: 1390
I have a HTML file with some JS logic where I want to create buttons based on an array.
for (var i = 0; i < arrayButtons.length; i++) {
console.log("console! ", arrayButtons[i].title); // works!
let buttonTitle = arrayButtons[i].title;
document.getElementById("buttons").innerHTML +=
"<div><button class='changeButton' " +
"onclick= document.getElementById(\"changeByButtonClick\").innerHTML = buttonTitle'>"
+ "<i class=\"icon\"></i>"+ buttonTitle + "</button></div>";}
In this line: "onclick= document.getElementById(\"changeByButtonClick\").innerHTML = buttonTitle'>"
, it throws an error that buttonTitle is not defined:
Uncaught ReferenceError: buttonTitle is not defined at HTMLButtonElement.onclick
I absolutely don't understand why. As you can see, I defined buttonTitle inside the method and just want to access it. And on console.log()
, it works. Why is it undefined in onClick()
?
Unfortunately, this and this solutions don't work for me.
Help will be much appreciated!
Upvotes: 0
Views: 2092
Reputation: 1074495
Your first use of buttonTitle
is inside the string that you're assigning to the innerHTML
of the id="buttons"
element, inside the onclick
attribute you're using there. The code in the onclick
string is evaluated at global scope,¹ where you don't have a buttonTitle
variable.
I strongly recommend not building code strings like that, not least for this reason. Instead, use a function:
for (var i = 0; i < arrayButtons.length; i++) {
console.log("console! ", arrayButtons[i].title); // works!
let buttonTitle = arrayButtons[i].title;
const buttons = document.getElementById("buttons")
buttons.insertAdjacentHTML(
"beforeend",
"<div><button class='changeButton'>" +
"<i class=\"icon\"></i>"+ buttonTitle + "</button></div>"
);
// Get the button we just added (the last one)
const buttonList = buttons.querySelectorAll("button");
const newButton = buttonList[buttonList.length - 1];
// Add the handler to it
newButton.addEventListener("click", function() {
document.getElementById("changeByButtonClick").innerHTML = buttonTitle;
});
}
Live Example:
const arrayButtons = [
{title: "first"},
{title: "second"},
{title: "third"},
{title: "fourth"},
];
for (var i = 0; i < arrayButtons.length; i++) {
console.log("console! ", arrayButtons[i].title); // works!
let buttonTitle = arrayButtons[i].title;
const buttons = document.getElementById("buttons")
buttons.insertAdjacentHTML(
"beforeend",
"<div><button class='changeButton'>" +
"<i class=\"icon\"></i>"+ buttonTitle + "</button></div>"
);
// Get the button we just added (the last one)
const buttonList = buttons.querySelectorAll("button");
const newButton = buttonList[buttonList.length - 1];
// Add the handler to it
newButton.addEventListener("click", function() {
document.getElementById("changeByButtonClick").innerHTML = buttonTitle;
});
}
#changeByButtonClick {
min-height: 1em;
}
<div id="changeByButtonClick"></div>
<div id="buttons">
This is the initial content that we don't remove.
</div>
Also note that I used insertAdjacentHTML
instead of using +=
on innerHTML
. (Thanks to Niet the Dark Absol for pointing it out, I missed it was a +=
.) Never use +=
on innerHTML
, when you do that, the browser has to do this:
In the process it loses event handlers and other non-HTML information. In contrast, with insertAdjacentHTML
, all it has to do is parse the string and insert the new nodes.
¹ "...at global scope..." Actually, it's a bit more complicated than that, it's a nested scope using (effectively) with
blocks. But for the purposes of the question, the details there aren't important; it's nearly at global scope.
Upvotes: 2