Como adicionar ouvintes de eventos individuais usando jquery .each ()?

Jan 08 2021

Tenho uma configuração simples: dois botões com valores diferentes. Quero usar a função .each () de jquery para adicionar um ouvinte de evento de clique a cada um dos botões que simplesmente registra os valores de cada botão. Mas de alguma forma, ao usar .each (), jquery sobrescreve o manipulador de cliques dos botões anteriores. Isso me deixa com duas perguntas:

  1. Por que é que?
  2. Como adicionar ouvintes de eventos individuais a uma coleção de objetos jquery?

Veja o código abaixo

  <html lang="en">
    
    <head>
        <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
        <script>
            $(() => { $(`button[toggle]`).each((_, button) => {
                    let $toggle = $(button);
                    type = $toggle.attr("type"); value = $toggle.attr("value");
                    $toggle.on("click", () => {
                        console.log(type, value);
                    });
                });
            });
        </script>
    
    </head>
    
    <body>
        <div>
            <button toggle type="one" value="true">One</button>
            <button toggle type="two" value="false">Two</button>
        </div>
    </body>
    
    </html>

Respostas

1 RoryMcCrossan Jan 08 2021 at 20:14

O problema em seu código é porque você declarou implicitamente as variáveis typee value, portanto, elas estão no windowescopo. Assim, cada iteração seguinte sobrescreve os valores para que apenas os valores finais sejam vistos após o término do loop.

Para corrigir isso, defina as variáveis dentro do loop:

$(() => { $(`button[toggle]`).each((_, button) => {
    let $toggle = $(button);
    let type = $toggle.attr("type"); let value = $toggle.attr("value");
    $toggle.on("click", () => {
      console.log(type, value);
    });
  });
});
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<div>
  <button toggle type="one" value="true">One</button>
  <button toggle type="two" value="false">Two</button>
</div>

No entanto , você não precisa doeach()loop de forma alguma. Você pode corrigir o problema e melhorar a qualidade do código vinculando o mesmo manipulador de eventos a todos osbuttonelementos e usando atargetpropriedade do evento que é gerada para acessar seus atributos.

Além disso, observe que adicionar seus próprios atributos não padrão, toggleneste caso, pode causar problemas na IU e no JS. Uma abordagem melhor é usar uma classe para agrupar os elementos.

Finalmente, onee twonão são valores válidos para o typeatributo. Em datavez disso, use um atributo para anexar seus próprios metadados personalizados a um elemento.

Com tudo isso dito, tente isto:

jQuery($ => {
  $('.toggle').on('click', e => { let $toggle = $(e.target); let type = $toggle.data("type"); 
    let value = $toggle.prop("value");
    console.log(type, value);
  });
});
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<div>
  <button class="toggle" data-type="one" value="true">One</button>
  <button class="toggle" data-type="two" value="false">Two</button>
</div>

1 prasanth Jan 08 2021 at 20:10

use $(this)dentro do click callback

$(() => {
  $(`button[toggle]`).each((_, button) => { $(button).on("click", function(){
       type   = $(this).attr("type"); value = $(this).attr("value");
      console.log(type, value);
    });
  });
});
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>

<button toggle type="one" value="true">One</button>
<button toggle type="two" value="false">Two</button>

E também não é preciso mencionar dentro de cada callback. Você poderia ligar da maneira normal como abaixo

$(() => { $(`button[toggle]`).on("click", function(){
       type   = $(this).attr("type"); value = $(this).attr("value");
      console.log(type, value);
    });
});
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>

<button toggle type="one" value="true">One</button>
<button toggle type="two" value="false">Two</button>

Kostas Jan 08 2021 at 20:11
  1. Porque o ouvinte se liga ao último valor de $ toggle.
  2. Criando e executando uma nova função conforme mostrado no snippet.

Explicado melhor nesta questão .

<html lang="en">
    
    <head>
        <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
        <script>
            $(() => {
                $(`button[toggle]`).each((_, button) => { let $toggle = $(button); type = $toggle.attr("type");
                    value = $toggle.attr("value"); $toggle.on("click", ((type, value) => {
                      return () => {
                        console.log(type, value);
                      };
                    })(type, value));
                });
            });
        </script>
    
    </head>
    
    <body>
        <div>
            <button toggle type="one" value="true">One</button>
            <button toggle type="two" value="false">Two</button>
        </div>
    </body>
    
    </html>