DEV Community

Cover image for Event delegation e múltiplos seletores com vanilla JS
doug-source
doug-source

Posted on

Event delegation e múltiplos seletores com vanilla JS

Nota: apenas traduzi o texto abaixo e postei aqui.

Hoje, veremos abordagens para usar event delegation com múltiplos seletores.

Como isso funciona

Se você não está familiarizado com a abordagem, veja como funciona. Em vez de attaching events a elementos específicos, você "listen" esse event em um parent element (geralmente o document ou window).

Qualquer event desse tipo que aconteça dentro do parent element aparece e você pode filtrar aqueles que não match ao element que você está procurando.

document.addEventListener('click', function (event) {
    // Verifica a classe .click-me
    // Se o elemento clicado não possui ela, não faça nada
    if (!event.target.matches('.click-me')) return;

    // O código que você quer executar vai aqui
});
Enter fullscreen mode Exit fullscreen mode

Se você estiver "listen" events em mais de um element com o mesmo seletor, essa abordagem é realmente melhor para desempenho porque usa menos memória no navegador.

Múltiplos seletores

Quando compartilho essa abordagem, muitas vezes os alunos me perguntam como implementá-la quando você precisa "listen" o mesmo tipo de event para vários seletores diferentes e fazer coisas diferentes com base no seletor que o elemento possui.

Por exemplo, digamos que você tenha botões mostrar/ocultar conteúdo que possuem uma classe .show-me e outro conjunto de botões com a classe .save que salva o conteúdo de um form para reutilização posterior.

Como você analisa quais ações serão executadas com base no seletor?

Opção 1: separe event listeners

A abordagem mais óbvia é usar dois event listeners separados, um para cada seletor.

document.addEventListener('click', function (event) {
    if (!event.target.matches('.show-me')) return;
    // Revele o conteúdo escondido...
});

document.addEventListener('click', function (event) {
    if (!event.target.matches('.save')) return;
    // Salve o conteúdo do form...
});
Enter fullscreen mode Exit fullscreen mode

Essa abordagem funciona bem se seus scripts forem mantidos em arquivos modulares separados.

Embora você esteja usando dois event listeners em vez de um, isso ainda é muito melhor para o desempenho do que "attaching" um listener a cada botão correspondente.

Opção 2: if...else

Se os dois trechos de código estiverem no mesmo arquivo, você poderá combiná-los em um único event listener e usar uma instrução if...else if.

document.addEventListener('click', function (event) {
    if (event.target.matches('.show-me')) {
        // Revele o conteúdo escondido...
    } else if (event.target.matches('.save')) {
        // Salve o conteúdo do form...
    }
});
Enter fullscreen mode Exit fullscreen mode

Isso proporciona um pequeno aumento de desempenho.

Opção 3: if e return

Muitas pessoas acham as instruções if...else if difíceis de manejar, especialmente se você tiver mais do que apenas algumas verificações. Eles podem crescer rapidamente e sair fora de controle.

Por esse motivo, prefiro usar instruções if sequenciais, com "returns" entre chaves para encerrar a function quando ocorrer um match.

document.addEventListener('click', function (event) {
    if (event.target.matches('.show-me')) {
        // Revele o conteúdo escondido...
        return;
    }
    if (event.target.matches('.save')) {
        // Salve o conteúdo do form...
        return;
    }
});
Enter fullscreen mode Exit fullscreen mode

Essa abordagem é, para mim, mais fácil de ler. Isso também significa que posso movimentar minhas verificações sem me preocupar em quebrar nada.

Opção 4: handler functions

Se você gosta de programação funcional, essa abordagem fornece uma estrutura interessante.

Dentro do event listener callback, você chama handler functions separadas para cada seletor e passa o event object. Em cada handler function, você executa a verificação do seletor e executa o código de acordo.

var showMe = function (event) {
    if (!event.target.matches('.show-me')) return;
    // Revele o conteúdo escondido...
};

var saveMe = function (event) {
    if (!event.target.matches('.save')) return;
    // Salve o conteúdo do form...
};

document.addEventListener('click', function (event) {
    showMe(event);
    saveMe(event);
});
Enter fullscreen mode Exit fullscreen mode

Essa abordagem fornece um bom equilíbrio entre desempenho e capacidade de manutenção.

O desempenho é um pouco menor do que as opções 2 e 3, porque a function saveMe() será executada mesmo se showMe() encontrar um match (as outras duas abordagens param assim que um match é encontrado). Mas ainda é melhor do que ter event listeners separados e fornece uma estrutura de código limpa e legível.

Qual abordagem você deve usar?

Em última análise, depende do seu projeto.

Eu uso muito a opção 1, porque costumo manter meus scripts em arquivos modulares. Mas quando o código está todo em um arquivo, a opção 4 é minha favorita.

Sugestão (atualizado):

Através da execução de Array.prototype.some da lista das handler functions, você pode executar somente até a primeira que retorne true, desde de que todas elas retornem isso (ou undefined no caso negativo) durante as verificações.

var showMe = function (event) {
    if (!event.target.matches('.show-me')) return;
    // Revele o conteúdo escondido...
    return true;
};

var saveMe = function (event) {
    if (!event.target.matches('.save')) return;
    // Salve o conteúdo do form...
    return true;
};

var otherLogic1 = function (event) {
    // a verificação negativa
    // a lógica
    return true;
}

var otherLogic2 = function (event) {
    // a verificação negativa
    // a lógica
    return true;
}

document.addEventListener('click', function (event) {
    [showMe, saveMe, otherLogic1, otherLogic2].some(
        handler => Boolean(handler(event))
    );
});
Enter fullscreen mode Exit fullscreen mode

Array.prototype.some interrompe sua iteração interna quando a primeira execução da sua callback function retornar true.

Fonte

Newsletter de Go Make Things

Top comments (0)