Técnicas de Manipulação de Eventos com JavaScript para Navegadores Diversos

Ao desenvolver para a web, é comum encontrar a necessidade de lidar com eventos do DOM de forma que funcione em diferentes navegadores e versões. A API de eventos do JavaScript possui algumas particularidades entre os ambientes, especialmente entre navegadores baseados em WebKit/Gecko e o Internet Explorer mais antigo.

Implementando Funções de Associação de Eventos Robustas

Uma função utilitária para adicionar eventos de forma compatível pode verificar a existência de métodos específicos do navegador.

Abordagem Direta


function adicionarOuvinte(elemento, tipo, acao) {
   if (elemento.addEventListener) {
       // Para navegadores modernos (Chrome, Firefox, Safari, etc.)
       elemento.addEventListener(tipo, acao, false);
   } else if (elemento.attachEvent) {
       // Para versões mais antigas do Internet Explorer
       elemento.attachEvent('on' + tipo, acao);
   }
}

window.onload = function() {
   var listaPai = document.getElementById('listaItens');
   adicionarOuvinte(listaPai, 'click', function(evento) {
       var eventoReal = evento || window.event;
       var alvo = eventoReal.target || eventoReal.srcElement;

       if (alvo.tagName.toLowerCase() === 'li') {
           alert(alvo.id);
       }
   });
};
   

Utilizando Padrão de IIFE (Immediately Invoked Function Expression)

Essa técnica permite retornar a função de associação de evento correta uma única vez, otimizando a chamada.


var associarOuvinte = (function() {
   if (document.addEventListener) {
       return function(elemento, tipo, acao) {
           elemento.addEventListener(tipo, acao, false);
       };
   } else {
       return function(elemento, tipo, acao) {
           elemento.attachEvent('on' + tipo, acao);
       };
   }
})();

window.onload = function() {
   associarOuvinte(document.getElementById('listaItens'), 'click', function(evento) {
       var eventoReal = evento || window.event;
       var alvo = eventoReal.target || eventoReal.srcElement;

       if (alvo.tagName.toLowerCase() === 'li') {
           alert(alvo.id);
       }
   });
};
   

Implmeentando Funções de Remoção de Eventos Compatíveis

A remoção de eventos é igualmente importante. Funções como removeEventListener e detachEvent são as contrapartes para addEventListener e attachEvent, respectivamente.

Observação: Para que a remoção funcione corretamente, especialmente com detachEvent, a função de callback deve ser uma referência nomeada (declarada externamente) e não uma função anônima inline.


var associarOuvinte = (function() {
   if (document.addEventListener) {
       return function(elemento, tipo, acao) {
           elemento.addEventListener(tipo, acao, false);
       };
   } else {
       return function(elemento, tipo, acao) {
           elemento.attachEvent('on' + tipo, acao);
       };
   }
})();

function removerOuvinte(elemento, tipo, acao) {
   if (elemento.removeEventListener) {
       elemento.removeEventListener(tipo, acao, false);
   } else if (elemento.detachEvent) {
       elemento.detachEvent('on' + tipo, acao);
   }
}

window.onload = function() {
   // É necessário usar uma função nomeada para poder removê-la depois
   var meuCallback = function(evento) {
       var eventoReal = evento || window.event;
       var alvo = eventoReal.target || eventoReal.srcElement;
       if (alvo.tagName.toLowerCase() === 'li') {
           alert(alvo.id);
       }
   };

   associarOuvinte(document.getElementById('listaItens'), 'click', meuCallback);
   // Exemplo de remoção do listener
   // removerOuvinte(document.getElementById('listaItens'), 'click', meuCallback);
};
   

Obtendo o Evento e o Elemento de Origem de Forma Universal

Para acessar informações consistentes sobre o evento e o elemento que o disparou, funções auxiliares são úteis.


function obterEvento(evento) {
   return evento || window.event;
}

function obterAlvo(evento) {
   var eventoReal = obterEvento(evento);
   return eventoReal.target || eventoReal.srcElement;
}
   

Controlando o Fluxo de Eventos: Prevenção de Comportamentos

Duas operações comuns são interromper a propagação de um evento e prevenir sua ação padrão.

Interromper Propagação (Stop Propagation)

Impede que o evento suba na árvore DOM.


function pararPropagacao(evento) {
    var eventoReal = obterEvento(evento);
    if (eventoReal.stopPropagation) {
        eventoReal.stopPropagation();
    } else {
        eventoReal.cancelBubble = true; // Compatibilidade com IE
    }
}
   

Prevenir Ação Padrão (Prevent Default)

Impede que o navegador execute a ação padrão associada ao evento (ex: seguir um link, subemter um formulário).


function prevenirPadrao(evento) {
    var eventoReal = obterEvento(evento);
    if (eventoReal.preventDefault) {
        eventoReal.preventDefault();
    } else {
        eventoReal.returnValue = false; // Compatibilidade com IE
    }
}
   

Um Objeto Utilitário para Eventos

Consolidar todas essas funcionalidades em um objeto pode simplificar o gerenciamento de eventos.


var GerenciadorEventos = {
   adicionar: function(elemento, tipo, acao) {
       if (elemento.addEventListener) {
           elemento.addEventListener(tipo, acao, false);
       } else if (elemento.attachEvent) {
           elemento.attachEvent('on' + tipo, acao);
       }
   },
   remover: function(elemento, tipo, acao) {
       if (elemento.removeEventListener) {
           elemento.removeEventListener(tipo, acao, false);
       } else {
           elemento.detachEvent('on' + tipo, acao);
       }
   },
   obterEvento: function(evento) {
       return evento || window.event;
   },
   obterAlvo: function(evento) {
       var eventoReal = this.obterEvento(evento);
       return eventoReal.target || eventoReal.srcElement;
   },
   pararPropagacao: function(evento) {
       var eventoReal = this.obterEvento(evento);
       if (eventoReal.stopPropagation) {
           eventoReal.stopPropagation();
       } else {
           eventoReal.cancelBubble = true;
       }
   },
   prevenirPadrao: function(evento) {
       var eventoReal = this.obterEvento(evento);
       if (eventoReal.preventDefault) {
           eventoReal.preventDefault();
       } else {
           eventoReal.returnValue = false;
       }
   }
};
   

Criando uma Função de Associação Genérica (Sem Suporte a IE)

Para cenários onde a compatibilidade com versões antigas do IE não é um requisito, podemos simplificar a lógica.

Este exemplo implementa um bindEvent que suporta tanto a associação direta quanto a delegação de eventos, utilizando a API moderna.


/**
* Função genérica para associar eventos.
* @param {Element} elemento - O elemento DOM ao qual o evento será associado.
* @param {string} tipo - O tipo de evento (ex: 'click', 'mouseover').
* @param {string|Function} seletorOuFuncao - Um seletor CSS para delegação de eventos, ou a função de callback se não houver delegação.
* @param {Function} [funcaoCallback] - A função a ser executada quando o evento ocorrer (usada apenas com delegação).
*/
function bindEvent(elemento, tipo, seletorOuFuncao, funcaoCallback) {
   let callbackFn = funcaoCallback;
   let selector = null;

   // Verifica se é delegação de evento ou associação direta
   if (typeof seletorOuFuncao === 'function') {
       // Associação direta: seletorOuFuncao é o callback
       callbackFn = seletorOuFuncao;
   } else {
       // Delegação de evento: seletorOuFuncao é o seletor
       selector = seletorOuFuncao;
   }

   elemento.addEventListener(tipo, function(event) {
       const target = event.target;

       if (selector) {
           // Delegação: verifica se o elemento alvo corresponde ao seletor
           if (target.matches(selector)) {
               // Executa o callback com o contexto (this) definido para o elemento alvo
               callbackFn.call(target, event);
           }
       } else {
           // Associação direta: executa o callback
           callbackFn.call(target, event);
       }
   });
}
   

Exemplos de Uso

Associação direta de evento em um botão:


const meuBotao = document.getElementById('botaoExemplo');
bindEvent(meuBotao, 'click', function(event) {
   console.log('Botão clicado!', event);
   event.preventDefault(); // Exemplo: previne ação padrão
   event.stopPropagation(); // Exemplo: para propagação
});
   

Delegação de evento em uma lista:


const minhaLista = document.getElementById('listaItens');
// Associa um ouvinte de clique na lista, que executará a função apenas se o alvo for um item com a classe 'item-especifico'
bindEvent(minhaLista, 'click', '.item-especifico', function(event) {
   console.log('Item específico da lista clicado:', event);
   event.preventDefault();
   event.stopPropagation();
});
   
  • Item 1
  • Item 2 (Específico)
  • Item 3
  • Item 4 (Específico)

Clique Aqui

Tags: javascript Eventos DOM compatibilidade addEventListener

Publicado em 6-30 20:12