JavaScript onclick: Change Text on Button Click by Index
Learn how to update text in JavaScript based on clicked button using forEach index, data attributes, or event delegation. Fix arr[element] errors with clean onclick handlers, code examples for dynamic text swaps without classes.
How can I display different text based on which button the user clicked in JavaScript?
I have five buttons and want to update the h2 and p inside .mainInfo using JavaScript (I don’t want to toggle visibility classes). Here’s my current code:
const choices = document.getElementsByClassName("choices");
const title = document.querySelector(".mainInfo h2");
const paragraph = document.querySelector(".mainInfo p");
class aboutUs{
constructor(title, paragraph){
this.title = title;
this.paragraph = paragraph;
}
}
let history = new aboutUs("Our History", "As a company we thr");
let goals = new aboutUs("Our Goals", "Our goals are as follows");
let team = new aboutUs("Our Team", "Our Team consists of");
let values = new aboutUs("Our Values", "The values we protect here at 45 Degrees are");
let why = new aboutUs("Why", "The Why is one of our favorite things to talk about");
let arr = [history, goals, team, values, why]
Array.from(choices).forEach(element =>{
element.onclick = function(){
console.log(element)
// title.textContent = arr[element].title;
// paragraph.textContent = arr[element].paragraph;
}
})
I know arr[element] doesn’t work because element is a DOM element, not an index. What is the best way to map the clicked button to the correct array item and update the title/paragraph? Should I use data-index attributes (dataset), Array.from(choices).indexOf(element), the index parameter in forEach, event delegation, or another pattern? Please show a concise example of the recommended approach.
Use the forEach index parameter in JavaScript to capture each button’s position in your choices collection, then set an onclick handler that updates the .mainInfo h2 and p text from your arr array. This maps the clicked button directly to the right aboutUs object without needing data-index attributes or complex lookups—perfect for your five static buttons. Here’s the fixed code: Array.from(choices).forEach((element, index) => { element.onclick = () => { title.textContent = arr[index].title; paragraph.textContent = arr[index].paragraph; }; });.
Contents
- The Problem with Your Current Code
- Recommended Fix: forEach with Index
- Alternative: Data Attributes for Explicit Mapping
- Scalable Option: Event Delegation
- Best Practices and Performance
- Sources
- Conclusion
The Problem with Your Current Code
You’ve got five buttons in a choices collection, an array of aboutUs objects, and you want a clean JavaScript onclick swap for the title and paragraph in .mainInfo. No toggling classes—just pure text updates. But arr[element] fails because element is a DOM node, not a number. Frustrating, right?
The loop runs fine, but inside the handler, element refers to the specific button clicked (thanks to closure), yet you can’t index an array with a <button> element. Console.log proves it: you’re seeing the node, not 0 through 4. Common snag for anyone diving into onclick event handling.
Your aboutUs class is solid—nice touch with the constructor. We just need to link buttons to array indices reliably. Three solid patterns fix this: a simple forEach tweak (best for small static sets like yours), data attributes for semantic clarity, or event delegation if buttons might grow dynamically.
Recommended Fix: forEach with Index
For your setup—five fixed buttons—this is the cleanest. Leverage forEach’s second parameter, index, to “bake in” the array position during loop time. The onclick closure captures it perfectly.
Replace your loop with this:
Array.from(choices).forEach((element, index) => {
element.onclick = () => {
title.textContent = arr[index].title;
paragraph.textContent = arr[index].paragraph;
};
});
Click the first button? Boom—history text appears. Second? goals. No indexOf lookups at runtime, which keeps it snappy. This pattern shines in MDN’s Array.forEach docs, where the callback gets (element, index, array)—index is free real estate.
Why not element.indexOf or similar? Those recompute position every click, slower for bigger lists. Here, it’s captured once. Test it: buttons respond instantly, console forgotten.
And yeah, textContent beats innerHTML unless you need HTML parsing—safer against injections.
Alternative: Data Attributes for Explicit Mapping
Prefer buttons to scream their purpose? Slap a data-index on each in HTML. Semantic, self-documenting, and works even if you shuffle the array later.
Update your HTML like:
<button class="choices" data-index="0">History</button>
<button class="choices" data-index="1">Goals</button>
<!-- etc. up to data-index="4" -->
Then the handler:
Array.from(choices).forEach(element => {
element.onclick = function() {
const idx = parseInt(this.dataset.index, 10);
title.textContent = arr[idx].title;
paragraph.textContent = arr[idx].paragraph;
};
});
dataset.index grabs it effortlessly—camelCase magic from HTML5 data attributes. No loop math needed. Great for teams or when buttons have labels like “History” that don’t match array order.
Downside? Manual HTML edits. But for readability? Chef’s kiss.
Scalable Option: Event Delegation
Got dozens of buttons? Or they load dynamically? Ditch per-button listeners. Attach one to a parent (say, .choices-container), let events bubble up, and check event.target.
Assuming a wrapper:
document.querySelector('.choices-container').addEventListener('click', (e) => {
if (e.target.matches('.choices')) {
const index = Array.from(choices).indexOf(e.target);
title.textContent = arr[index].title;
paragraph.textContent = arr[index].paragraph;
}
});
matches('.choices') filters noise, indexOf(e.target) finds position. Scales forever—new buttons “just work.” JavaScript.info nails this: one handler, bubbling via event.target.
Tradeoff: slight runtime cost for indexOf. Fine for hundreds, overkill for five. But future-proof.
Combine with data attrs? Even better: const idx = parseInt(e.target.dataset.index);—no Array.from needed.
Best Practices and Performance
textContent for plain text—always. innerHTML tempts XSS if user data sneaks in, as GeeksforGeeks warns.
Accessibility? Add aria-pressed="false" to buttons, toggle on click. Keyboard users thank you.
Performance: forEach index wins for static small sets (Stack Overflow consensus). Delegation for 50+ or dynamic (freeCodeCamp example).
Edge cases: Buttons added post-load? Delegation. Resizing arrays? Data attrs. Debug tip: console.log(index) inside handlers.
Modern twist: addEventListener over onclick for flexibility (remove listeners easily). Your code’s close—arrow functions capture index perfectly.
What about let vs var in loops? ES6 forEach sidesteps old closure pitfalls.
Sources
- Stack Overflow - Changing button text onclick
- GeeksforGeeks - How to Change the Button Label when Clicked using JavaScript
- BobbyHadz - Change button text on Click using JavaScript
- Permadi - JavaScript: Changing Button’s Text
- SheCodes - How to Change Text When Clicking in JavaScript
- Stack Overflow - Get index of clicked element using pure JavaScript
- Stack Overflow - How to index inside onclick in JavaScript
- Stack Overflow - Javascript - find the index of clicked button
- Stack Overflow - Get index of clicked element
- Stack Overflow - Getting data-* attribute for onclick event
- Stack Overflow - onclick for all buttons with a data attribute
- MDN - Array.prototype.forEach()
- JavaScript.info - Event delegation
- freeCodeCamp - Event Delegation in JavaScript
- MDN - Event bubbling
- JavaScript Tutorial - Event Delegation
- Envato Tuts+ - HTML5 Data Attributes in JavaScript
- Dofactory - HTML button data-*
- Salesforce StackExchange - Get value from attribute after onclick
Conclusion
Stick with the forEach index fix for your five buttons—it’s dead simple, performant, and matches your code style. Scale up? Swap to data attributes or delegation. Either way, your JavaScript onclick text swaps will feel buttery smooth, no classes or hacks needed. Drop that in, refresh, click around—problem solved.