0

I have an array called 'filteredTasks', for each task I want to create a button in a Div and add an onclick function to that button. I created some code (which you can see below, first block) to do that. Which didnt work, the buttons get created properly but the onclick function only gets added to the last button. When I use the exact same code, but splitted up into 2 different forEach callbacks (which you can also view below, second part), it does work. In short, when trying to create the buttons and adding a onclick function to each when created, only the last button properly receives an onclick function. But when I create all the buttons first, then add an onclick function to each, it does work. I fail to figure out why this is. Any better developer here that can point out to me why? Thanks alot!

        filteredTasks.forEach(function(task) {
            var buttonId = task.taskName.replace(/\s/g, '') + 'Button'
            var taskHTML = `<button class="taskButton" id="${buttonId}">${task.taskName}</button>`;
            taskDiv.innerHTML += taskHTML;
            var button = document.querySelector(`#${buttonId}`);
            button.onclick = function() {performTask(task)};
        });
    };





        filteredTasks.forEach(function(task) {
            var buttonId = task.taskName.replace(/\s/g, '') + 'Button'
            var taskHTML = `<button class="taskButton" id="${buttonId}">${task.taskName}</button>`;
            taskDiv.innerHTML += taskHTML;
        });

        filteredTasks.forEach(function(task) {
            var buttonId = task.taskName.replace(/\s/g, '') + 'Button'
            var button = document.querySelector(`#${buttonId}`);
            button.onclick = function() {performTask(task)};
        });
    };
  • try to create the buttons using createElement instead of concatenating string – Mario Jan 16 '19 at 13:20
  • Can you show an example of the content of filterTasks? In order to produce a more adjusted example to your case – Mario Jan 16 '19 at 13:22

1 Answers1

1

I suspect this is to do with the way that you're handling the DOM updates. Your code is only adding the event to the last button. This is a known issue with for/loops). Using a forEach instead of a for/loop would normally fix this, but apparently not in this case. (I appreciate this is a bit hand-wavy. Another SO member might have a better detailed technical reason why it doesn't work in this case.)

That said, as you've discovered, you're much better off doing this in stages, but you should go a couple of steps further. It will avoid getting into the situation you're in now, and will be more performant.

At the moment you're trying to achieve everything in one loop: the creation of the task html, adding it to the DOM, and adding the event listener to new button. What you should do is create all the HTML, then add that to the DOM, then pick up all the buttons at once using their class and add listeners to them.

const filteredTasks = [{ taskName: 'bob' },{ taskName: 'dave' }];
const tasks = document.querySelector('#tasks');

// Use `map` to create all the buttons
// I've used a data attribute here instead of an actual id
const html = filteredTasks.map(task => {
  const buttonId = `${task.taskName.replace(/\s/g, '')}Button`;
  return `<button class="taskButton" data-id="${buttonId}">${task.taskName}</button>`;
}).join('');

// Add that HTML to the DOM with one update
tasks.innerHTML = html;

// Grab all the taskButton buttons using their class,
// and add click listeners to them
const buttons = document.querySelectorAll('.taskButton');
[...buttons].forEach(button => button.addEventListener('click', performTask, false));

// `performTask` destructs the event target (clicked button)
// and logs the id 
function performTask(e) {
  const { dataset: { id } } = e.target;
  console.log(id);
}
<div id="tasks"></div>
Andy
  • 61,948
  • 13
  • 68
  • 95