11

I'd like to perform the logic in the "onClick" through the event listener in jS but it only seems to run once? I have the class in all four but I can't figure out why it seems to only work for the first?

HTML:

<button id='btn-1' type="button" name="first" class="breakdown main-text" onclick="enableButton('btn-2');disableButton('btn-1');show('btn-1')"> Breakdown Start </button>
<button id='btn-2' type="button" name="second" class="breakdown main-text" onclick="enableButton('btn-3');disableButton('btn-2');show('btn-2')" disabled> Repair Start </button>
<button id='btn-3' type="button" name="third" class="breakdown main-text" onclick="enableButton('btn-4');disableButton('btn-3');show('btn-3')" disabled> Repair End </button>
<button id='btn-4' type="button" name="fourth" class="breakdown main-text" onclick="show('btn-4')" disabled> Breakdown Ended </button>

JS:

let button1 = document.querySelector('#btn-1')
let button2 = document.querySelector('#btn-2');
let button3 = document.querySelector('#btn-3');
let button4 = document.querySelector('#btn-4');


const breakdownButton = document.querySelector('.breakdown');
breakdownButton.addEventListener('click', function() {
console.log(this.innerHTML);
});
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Nakul Malhotra
  • 111
  • 1
  • 2
  • 6
  • The other buttons are disabled. Event listener for them won't be triggered apart from the first button – kboul Jul 28 '18 at 17:04
  • How else would I dynamically disable and re enable the buttons? – Nakul Malhotra Jul 28 '18 at 17:26
  • Use [event delegation](//developer.mozilla.org/docs/Learn/JavaScript/Building_blocks/Events#Event_delegation) instead of assigning multiple event listeners — it’s more maintainable. E.g., use an [event argument](//developer.mozilla.org/docs/Web/API/EventTarget/addEventListener#The_event_listener_callback)’s [`target`](//developer.mozilla.org/docs/Web/API/Event/target). See [the tag info](/tags/event-delegation/info) and [What is DOM Event delegation?](/q/1687296/4642212). Here: `addEventListener("click", ({ target }) => { if(target.closest(".breakdown")){ console.log(target.innerHTML); } });`. – Sebastian Simon Aug 31 '21 at 16:04
  • Related (maybe duplicate): [addEventListener on NodeList](/q/12362256/4642212). – Sebastian Simon Mar 18 '22 at 00:22

3 Answers3

15

You need to use querySelectorAll which will return a collection.Now use spread operator (three dots) to convert it to array and use forEach .Inside forEach callback add the event listener to it

[...document.querySelectorAll('.breakdown')].forEach(function(item) {
  item.addEventListener('click', function() {
    console.log(item.innerHTML);
  });
   });
<button id='btn-1' type="button" name="first" class="breakdown main-text"> Breakdown Start </button>
<button id='btn-2' type="button" name="second" class="breakdown main-text" disabled> Repair Start </button>
<button id='btn-3' type="button" name="third" class="breakdown main-text" disabled> Repair End </button>
<button id='btn-4' type="button" name="fourth" class="breakdown main-text" disabled> Breakdown Ended </button>

In your snippet you have also attached inline event handler,that may not be necessary.

If the objective is to enable the next button then a function to enable it can be called from the callback function of the event handler

brk
  • 48,835
  • 10
  • 56
  • 78
  • 1
    Is the spread into an array really necessary? `forEach` can be called on the `querySelectorAll` result in Chrome console it would seem. – benmccallum Jun 13 '19 at 09:08
  • 1
    It cannot however be called directly on the result of `getElementsByClassName` unfortunately... – benmccallum Jun 13 '19 at 09:20
  • anyone knows why this works with `querySelectorAll` and does not work with `document.getElementsByClassName` ? – S.. Dec 28 '21 at 20:03
  • @S.. Because one returns a [`NodeList`](//developer.mozilla.org/en/docs/Web/API/NodeList), the other an [`HTMLCollection`](//developer.mozilla.org/en/docs/Web/API/HTMLCollection). Only one of those has the `forEach` method. – Sebastian Simon Mar 18 '22 at 00:20
3

Need to use querySelectorAll instead of querySelector. And iterate over the list like this.

const breakdownButton = document.querySelectorAll('.breakdown');

// It add event listeners for the first button element. 
// you can use forloop or map function to iterate over the list elements
// and here i used breakdownButton[0] as an example.

breakdownButton[0].addEventListener('click', function() {
  console.log(this.innerHTML);
});

Use iterate functions like forEach or map. I used forEach

const breakdownButton = document.querySelectorAll('.breakdown');
breakdownButton.forEach(function(btn) {
  btn.addEventListener('click', function() {
    console.log();
  });
});
htoniv
  • 1,658
  • 21
  • 40
2

Look at the documentation for querySelector:

querySelector() returns the first Element within the document that matches the specified selector, or group of selectors.

If you want to match more than one element, you'll need to use querySelectorAll and, because it doesn't return a single element loop over the result.

Alternatively, you could use event delegation.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335