How to make several elements participate in Event Listener?

Asked

Viewed 81 times

1

I created a card page and added a Fontawesome icon to each one. However, when I try to make this action repeat on the other cards, it doesn’t work. When I click on any other card, only the first one obeys the function.

var favBtn =  document.getElementById('button')

function clique() { 
  if (favBtn.classList.contains("far")) {
    favBtn.classList.remove("far")
    favBtn.classList.add("fas")
  } else {
    favBtn.classList.remove("fas")
    favBtn.classList.add("far")
  }
}
<i onclick="clique()" id="button" class="far fa-star"></i>

1 answer

2

This happens because, although each button has a attribute Event Listener, they all perform the same function (in this case, the function clique). However, note that the function clique operates only with the result of document.getElementById('button'). That is, it will only work with the first button with ID equal to button.

In addition, using multiple elements with the same ID is not a good practice in HTML. If you need to use a selector common to multiple elements, prefer to use a class.

In this case, you should allow some dynamism there, so as to perform the function according to the element that activated the event. For this, you can pass the this as an argument to the function, in the attribute onclick. Thus:

<!--               ↓↓↓↓                                    -->
<i onclick="clique(this)" id="button" class="far fa-star"></i>

As I explained in more detail in this other answer, when an event attribute is executed, the value of this refers to the element that activated the event (in which case it will always be the element that owns the event itself attribute Event Listener). Thus, you can access this argument as the first parameter of clique:

function clique(favBtn) { 
  if (favBtn.classList.contains("far")) {
    favBtn.classList.remove("far");
    favBtn.classList.add("fas");
  } else {
    favBtn.classList.remove("fas");
    favBtn.classList.add("far");
  }
}

Note that now, instead of operating with a single button, the function operates with the parameter you will receive (in this case, it will always be the button itself - that activated the event).

A functional example:

function clique(favBtn) { 
  if (favBtn.classList.contains("far")) {
    favBtn.classList.remove("far");
    favBtn.classList.add("fas");
  } else {
    favBtn.classList.remove("fas");
    favBtn.classList.add("far");
  }
}
/* Ignore. Está aqui somente para diferenciar ao clique. */
.far { color: red; }
.fas { color: blue; }
<i onclick="clique(this)" class="far fa-star">1</i>
<i onclick="clique(this)" class="far fa-star">2</i>
<i onclick="clique(this)" class="far fa-star">3</i>

Just as a suggestion, this code can be improved. You can manipulate it all using Javascript itself (using the addEventListener). You can use the method toggle, implemented by clasList to avoid the if. Behold:

// Selecionamos todos os botões (utilizamos a classe `rating-btn`):
const btns = document.getElementsByClassName('rating-btn');

for (const btn of btns) {
  // Para cada botão (`btn`), adicionamos um listener de eventos:
  btn.addEventListener('click', function() {
    // O `this` aqui corresponde ao botão que foi clicado.
    
    this.classList.toggle('fas');
    this.classList.toggle('far');
  });
}
/* Ignore. Está aqui somente para diferenciar ao clique. */
.far { color: red; }
.fas { color: blue; }
<i class="rating-btn far fa-star">1</i>
<i class="rating-btn far fa-star">2</i>
<i class="rating-btn far fa-star">3</i>


Note also that I added a semicolon at the end of each statement. I know it sounds like freshness, but a semicolon can help you avoid all sorts of weird behaviors, like this. Learn more here.

  • To the person who left the negative: please explain. I cannot know where to improve without a direction. As far as I can see, the answer is good, although open to suggestions. :-)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.