web-dev-qa-db-fra.com

addEventListener vs onclick

Quelle est la différence entre addEventListener et onclick?

var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);

Le code ci-dessus réside dans un fichier .js séparé et ils fonctionnent parfaitement.

629
William Sham

Les deux sont corrects, mais aucun d'entre eux n'est "meilleur" en soi, et il peut y avoir une raison pour laquelle le développeur a choisi d'utiliser les deux approches.

Écouteurs d'événement (addEventListener et l'attachement d'IE)

Les versions précédentes d'Internet Explorer implémentent le javascript différemment de presque tous les autres navigateurs. Avec les versions inférieures à 9, vous utilisez la méthode attachEvent [ doc ], comme ceci:

element.attachEvent('onclick', function() { /* do stuff here*/ });

Dans la plupart des autres navigateurs (y compris IE 9 et versions ultérieures), vous utilisez addEventListener [ doc ], comme suit:

element.addEventListener('click', function() { /* do stuff here*/ }, false);

En utilisant cette approche ( événements DOM niveau 2 ), vous pouvez attacher un nombre d’événements théoriquement illimité à un seul élément. La seule limite pratique concerne la mémoire côté client et d'autres problèmes de performances, différents pour chaque navigateur.

Les exemples ci-dessus représentent l'utilisation d'une fonction anonyme [ doc ]. Vous pouvez également ajouter un écouteur d'événement à l'aide d'une référence de fonction [ doc ] ou d'une fermeture [ doc ]:

var myFunctionReference = function() { /* do stuff here*/ }

element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);

Une autre caractéristique importante de addEventListener est le dernier paramètre, qui contrôle la réaction de l'écouteur aux événements de propagation [ doc ]. J'ai passé faux dans les exemples, ce qui est la norme pour probablement 95% des cas d'utilisation. Il n'y a pas d'argument équivalent pour attachEvent, ou lors de l'utilisation d'événements en ligne.

Evénements en ligne (HTML onclick = "" propriété et element.onclick)

Dans tous les navigateurs prenant en charge javascript, vous pouvez insérer un écouteur d'événements en ligne, c'est-à-dire directement dans le code HTML. Vous avez probablement vu ceci:

<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>

La plupart des développeurs expérimentés évitent cette méthode, mais le travail est fait; c'est simple et direct. Vous ne pouvez pas utiliser de fermetures ou de fonctions anonymes ici (bien que le gestionnaire lui-même soit une fonction anonyme), et votre contrôle de la portée est limité.

L'autre méthode que vous mentionnez:

element.onclick = function () { /*do stuff here */ };

... équivaut au javascript en ligne, sauf que vous maîtrisez mieux la portée (puisque vous écrivez un script plutôt que HTML) et que vous pouvez utiliser des fonctions anonymes, des références de fonctions et/ou des fermetures.

L'inconvénient majeur des événements en ligne est que, contrairement aux écouteurs d'événements décrits ci-dessus, un seul événement en ligne peut être attribué. Les événements en ligne sont stockés en tant qu'attribut/propriété de l'élément [ doc ], ce qui signifie qu'il peut être remplacé.

En utilisant l'exemple <a> du code HTML ci-dessus:

var element = document.getElementById('testing');
element.onclick = function () { alert('did stuff #1'); };
element.onclick = function () { alert('did stuff #2'); };

... lorsque vous avez cliqué sur l'élément, vous seulement voyez "Did stuff # 2" - vous avez écrasé le premier assigné de la onclickpropriété avec la deuxième valeur et vous avez écrasé la propriété inline HTML onclick originale également. Découvrez-le ici: http://jsfiddle.net/jpgah/ .

De manière générale, n'utilise pas d'événements en ligne . Il peut y avoir des cas d'utilisation spécifiques, mais si vous n'êtes pas sûr à 100% que vous avez ce cas d'utilisation, vous ne devez pas et ne devriez pas utiliser d'événements en ligne.

Javascript moderne (angulaire, etc.)

Depuis que cette réponse a été publiée, les frameworks javascript tels que Angular sont devenus beaucoup plus populaires. Vous verrez un code comme celui-ci dans un modèle Angular:

<button (click)="doSomething()">Do Something</button>

Cela ressemble à un événement en ligne, mais ce n'est pas. Ce type de modèle sera transpilé en un code plus complexe qui utilise des écouteurs d'événements en coulisse. Tout ce que j'ai écrit sur les événements ici s'applique toujours, mais vous êtes éliminé du fond des choses au moins une couche. Vous devez comprendre les rouages ​​de base, mais si vos meilleures pratiques en matière de framework JS moderne impliquent l'écriture de ce type de code dans un modèle, n'ayez pas l'impression d'utiliser un événement en ligne - vous ne l'êtes pas.

Quel est le meilleur?

La question est une question de compatibilité et de nécessité du navigateur. Avez-vous actuellement besoin d'attacher plus d'un événement à un élément? Veux-tu dans le futur? Les chances sont, vous allez. attachEvent et addEventListener sont nécessaires. Si ce n'est pas le cas, un événement en ligne peut sembler être la solution, mais vous êtes bien mieux préparé à un avenir qui, bien que cela puisse paraître improbable, est au moins prévisible. Il est possible que vous deviez passer aux écouteurs d'événements basés sur JS. Vous pouvez donc également commencer par là. Ne pas utiliser les événements en ligne.

jQuery et d'autres frameworks javascript encapsulent les différentes implémentations de navigateur des événements DOM de niveau 2 dans les modèles génériques afin que vous puissiez écrire du code compatible entre navigateurs sans vous soucier de l'historique d'IE en tant que rebelle. Même code avec jQuery, tous navigateurs croisés et prêt à basculer:

$(element).on('click', function () { /* do stuff */ });

Ne vous précipitez pas et obtenez un cadre pour cette seule chose, cependant. Vous pouvez facilement lancer votre propre petit utilitaire pour prendre en charge les anciens navigateurs:

function addEvent(element, evnt, funct){
  if (element.attachEvent)
   return element.attachEvent('on'+evnt, funct);
  else
   return element.addEventListener(evnt, funct, false);
}

// example
addEvent(
    document.getElementById('myElement'),
    'click',
    function () { alert('hi!'); }
);

Essayez-le: http://jsfiddle.net/bmArj/

Compte tenu de tout cela, à moins que le script que vous êtes en train de regarder prenne en compte les différences de navigateur d'une autre manière (en code non indiqué dans votre question), la partie utilisant addEventListener ne fonctionnerait pas dans IE versions inférieures à 9.

Documentation et lectures associées

869
Chris Baker

La différence que vous pourriez voir si vous aviez deux autres fonctions:

var h = document.getElementById('a');
h.onclick = doThing_1;
h.onclick = doThing_2;

h.addEventListener('click', doThing_3);
h.addEventListener('click', doThing_4);

Les fonctions 2, 3 et 4 fonctionnent, mais 1 ne fonctionne pas. En effet, addEventListener n'écrase pas les gestionnaires d'événements existants, alors que onclick remplace tous les gestionnaires d'événements onclick = fn existants.

L’autre différence significative, bien sûr, est que onclick fonctionnera toujours, alors que addEventListener ne fonctionnera pas dans Internet Explorer avant la version 9. Vous pouvez utiliser l’analogue attachEvent (qui - légèrement syntaxe différente) dans IE <9.

164
lonesomeday

Dans cette réponse, je décrirai les trois méthodes de définition des gestionnaires d’événements DOM.

element.addEventListener()

Exemple de code:

const element = document.querySelector('a');
element.addEventListener('click', event => event.preventDefault(), true);
<a href="//google.com">Try clicking this link.</a>

element.addEventListener() présente de nombreux avantages:

  • Vous permet d’enregistrer des gestionnaires d’événements illimités et de les supprimer avec element.removeEventListener().
  • Possède le paramètre useCapture, qui indique si vous souhaitez gérer un événement dans sa phase de capture ou de formation de bulles . Voir: Incapable de comprendre l'attribut useCapture dans addEventListener .
  • Se préoccupe de la sémantique . En gros, cela rend plus explicite l’enregistrement des gestionnaires d’événements. Pour un débutant, un appel de fonction indique clairement que quelque chose se passe, alors que l’attribution d’un événement à une propriété de l’élément DOM n’est au moins pas intuitive.
  • Vous permet de séparer la structure du document (HTML) et la logique (JavaScript) . Dans les petites applications Web, cela peut sembler ne pas avoir d’importance, mais c’est importe importe pour tout projet plus important. Il est beaucoup plus facile de gérer un projet qui sépare structure et logique qu'un projet qui ne le fait pas.
  • Élimine la confusion avec les noms d'événements corrects. En raison de l'utilisation d'écouteurs d'événement inline ou de l'affectation d'écouteurs d'événement aux propriétés .onevent des éléments DOM, de nombreux programmeurs JavaScript inexpérimentés pensent que le nom de l'événement est par exemple onclick ou onload. on est not ​​une partie du nom de l'événement . Les noms d'événements corrects sont click et load, et c'est ainsi que les noms d'événements sont passés à .addEventListener().
  • Fonctionne dans presque tous les navigateurs . Si vous devez toujours prendre en charge IE <= 8, vous pouvez utiliser n polyfill de MDN .

element.onevent = function() {} (par exemple onclick, onload)

Exemple de code:

const element = document.querySelector('a');
element.onclick = event => event.preventDefault();
<a href="//google.com">Try clicking this link.</a>

C'était une façon d'enregistrer les gestionnaires d'événements dans DOM 0. C'est maintenant déconseillé, parce que:

  • Vous permet d'enregistrer un seul gestionnaire d'événements . Supprimer également le gestionnaire attribué n'est pas intuitif, car pour supprimer le gestionnaire d'événements affecté à l'aide de cette méthode, vous devez rétablir la propriété onevent à son état initial (c'est-à-dire null).
  • ne répond pas correctement aux erreurs . Par exemple, si vous attribuez par erreur une chaîne à window.onload, par exemple: window.onload = "test";, aucune erreur ne sera générée. Votre code ne fonctionnerait pas et il serait très difficile de savoir pourquoi. .addEventListener() cependant, émettrait une erreur (au moins dans Firefox): TypeError: l'argument 2 de EventTarget.addEventListener n'est pas un objet.
  • Ne permet pas de choisir si vous souhaitez gérer un événement dans sa phase de capture ou de propagation.

Gestionnaires d'événements en ligne (attribut onevent HTML)

Exemple de code:

<a href="//google.com" onclick="event.preventDefault();">Try clicking this link.</a>

De la même manière que element.onevent, il est maintenant déconseillé. Outre les problèmes que element.onevent a, il:

  • C’est un problème de sécurité potentiel , car il rend XSS bien plus dangereux. De nos jours, les sites Web doivent envoyer le bon en-tête HTTP Content-Security-Policy pour bloquer les scripts en ligne et autoriser les scripts externes uniquement à partir de domaines approuvés. Voir Comment fonctionne la politique de sécurité du contenu?
  • ne sépare pas la structure de document et la logique .
  • Si vous générez votre page avec un script côté serveur et, par exemple, vous générez cent liens, chacun avec le même gestionnaire d'événements en ligne, votre code serait beaucoup plus long que si le gestionnaire d'événements n'avait été défini qu'une seule fois. Cela signifie que le client devrait télécharger plus de contenu, ce qui ralentirait votre site Web.

Voir également

59

Alors que onclick fonctionne dans tous les navigateurs, addEventListener ne fonctionne pas dans les versions antérieures d'Internet Explorer, qui utilise attachEvent à la place.

L'inconvénient de onclick est qu'il ne peut y avoir qu'un seul gestionnaire d'événements, tandis que les deux autres déclenchent tous les rappels enregistrés.

21
Magnar

Autant que je sache, l'événement "load" du DOM ne fonctionne toujours que de manière très limitée. Cela signifie qu'il ne sera déclenché que pour les éléments window object, images et <script>, par exemple. Il en va de même pour l'affectation directe onload. Il n'y a pas de différence technique entre ces deux. Probablement .onload = a une meilleure disponibilité entre navigateurs.

Cependant, vous ne pouvez pas affecter un load event à un élément <div> ou <span> ou quoi encore.

12
jAndy

Sommaire:

  1. addEventListener peut ajouter plusieurs événements, alors qu'avec onclick cela ne peut pas être fait.
  2. onclick peut être ajouté en tant qu'attribut HTML, alors qu'un addEventListener ne peut être ajouté qu'à l'intérieur de <script> éléments.
  3. addEventListener peut prendre un troisième argument qui peut arrêter la propagation de l'événement.

Les deux peuvent être utilisés pour gérer des événements. Cependant, addEventListener devrait être le choix préféré, car il peut tout faire onclick fait et plus encore. N'utilisez pas onclick inline en tant qu'attributs HTML, car cela mélange le javascript et le HTML, ce qui est une mauvaise pratique. Cela rend le code moins facile à gérer.

5
Willem van der Veen

Selon MDN, la différence est la suivante:

addEventListener:

La méthode EventTarget.addEventListener () ajoute l'objet compatible EventListener spécifié à la liste des écouteurs d'événement pour le type d'événement spécifié sur le EventTarget sur lequel il est appelé. La cible d'événement peut être un élément d'un document, le document lui-même, une fenêtre ou tout autre objet prenant en charge des événements (tel que XMLHttpRequest).

onclick:

La propriété onclick renvoie le code du gestionnaire d'événements click sur l'élément actuel. Lorsque vous utilisez l'événement click pour déclencher une action, envisagez également d'ajouter cette même action à l'événement keydown, afin de permettre à cette même action d'être utilisée par des personnes qui n'utilisent pas de souris ou d'écran tactile. Syntaxe element.onclick = functionRef; où functionRef est une fonction - souvent le nom d'une fonction déclarée ailleurs ou une expression de fonction. Voir "Guide JavaScript: Fonctions" pour plus de détails.

Il existe également une différence de syntaxe d'utilisation, comme vous le voyez dans les codes ci-dessous:

addEventListener:

// Function to change the content of t2
function modifyText() {
  var t2 = document.getElementById("t2");
  if (t2.firstChild.nodeValue == "three") {
    t2.firstChild.nodeValue = "two";
  } else {
    t2.firstChild.nodeValue = "three";
  }
}

// add event listener to table
var el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);

onclick:

function initElement() {
    var p = document.getElementById("foo");
    // NOTE: showAlert(); or showAlert(param); will NOT work here.
    // Must be a reference to a function name, not a function call.
    p.onclick = showAlert;
};

function showAlert(event) {
    alert("onclick Event detected!");
}
2
Alireza

Un détail n'a pas encore été noté: les navigateurs de bureau modernes considèrent différentes pressions sur les boutons comme des "clics" pour AddEventListener('click' et onclick par défaut.

  • Sur Chrome 42 et IE11, onclick et AddEventListener vous permettent de cliquer sur les boutons Clic gauche et moyen.
  • Sur Firefox 38, onclick déclenche niquement lors d'un clic gauche, mais AddEventListener un clic se déclenche lors d'un clic gauche, au centre et.

De plus, le comportement du clic du milieu est très incohérent entre les navigateurs lorsque des curseurs de défilement sont impliqués:

  • Sur Firefox, les événements de clic du milieu sont toujours déclenchés.
  • Sous Chrome, ils ne se déclenchent pas si le clic intermédiaire ouvre ou ferme un curseur de défilement.
  • Sur IE, ils se déclenchent lorsque le curseur de défilement se ferme, mais pas quand il s'ouvre.

Il est également intéressant de noter que les événements de "clic" pour tout élément HTML sélectionnable au clavier, tel que input, se déclenchent également dans l'espace ou entrent lorsque l'élément est sélectionné.

2
a guest

Si le support du navigateur ne vous inquiète pas trop, il existe un moyen de relier la référence "this" dans la fonction appelée par l'événement. Il indiquera normalement l'élément qui a généré l'événement lorsque la fonction est exécutée, ce qui n'est pas toujours ce que vous voulez. La partie délicate consiste à pouvoir en même temps supprimer le même écouteur d'événement, comme indiqué dans cet exemple: http://jsfiddle.net/roenbaeck/vBYu3/

/*
    Testing that the function returned from bind is rereferenceable, 
    such that it can be added and removed as an event listener.
*/
function MyImportantCalloutToYou(message, otherMessage) {
    // the following is necessary as calling bind again does 
    // not return the same function, so instead we replace the 
    // original function with the one bound to this instance
    this.swap = this.swap.bind(this); 
    this.element = document.createElement('div');
    this.element.addEventListener('click', this.swap, false);
    document.body.appendChild(this.element);
}
MyImportantCalloutToYou.prototype = {
    element: null,
    swap: function() {
        // now this function can be properly removed 
        this.element.removeEventListener('click', this.swap, false);           
    }
}

Le code ci-dessus fonctionne bien dans Chrome, et il y a probablement quelques difficultés à rendre "bind" compatible avec les autres navigateurs.

1
Lars Rönnbäck

Il devrait également être possible d'étendre l'auditeur en le prototypant (si nous avons une référence à cette fonction et que ce n'est pas une fonction anonyme) ou de faire l'appel 'onclick' à une bibliothèque de fonctions (une fonction appelant d'autres fonctions).

comme

    Elm.onclick = myFunctionList
    function myFunctionList(){
      myFunc1();
      myFunc2();
    }

cela signifie que nous n'avons jamais à modifier l'appel de onclick, mais simplement à modifier la fonction myFunctionList () pour faire ce que nous voulons, mais cela nous laisse sans contrôle des phases de barbotage/capture et doit donc être évité pour les nouveaux navigateurs.

juste au cas où quelqu'un trouverait ce fil dans le futur ...

1
patrik

L'utilisation de gestionnaires en ligne est incompatible avec stratégie de sécurité du conten , de sorte que l'approche addEventListener est plus sécurisée de ce point de vue. Bien sûr, vous pouvez activer les gestionnaires en ligne avec unsafe-inline mais, comme son nom l’indique, ce n’est pas sans danger, car cela ramène toutes les exploitations JavaScript que CSP empêche.

1
kravietz

Le contexte référencé par le mot clé 'this' dans JavasSript est différent.

regardez le code suivant:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>

</head>
<body>
    <input id="btnSubmit" type="button" value="Submit" />
    <script>
        function disable() {
            this.disabled = true;
        }
        var btnSubmit = document.getElementById('btnSubmit');
        btnSubmit.onclick = disable();
        //btnSubmit.addEventListener('click', disable, false);
    </script>
</body>
</html>

Ce qu'il fait est vraiment simple. lorsque vous cliquez sur le bouton, le bouton sera automatiquement désactivé.

D'abord, lorsque vous essayez de connecter les événements de cette façon, button.onclick = function(), l'événement onclick sera déclenché en cliquant sur le bouton. Toutefois, le bouton ne sera pas désactivé car il n'y a pas de liaison explicite entre button.onclick et le gestionnaire d'événements onclick. Si vous déboguez voir l'objet 'this', vous pouvez voir qu'il fait référence à l'objet 'window'.

Deuxièmement, si vous commentez btnSubmit.onclick = disable(); et décommentez //btnSubmit.addEventListener('click', disable, false);, vous pouvez voir que le bouton est désactivé car, de cette manière, il existe une liaison explicite entre les événements button.onclick et le gestionnaire d'événements onclick. Si vous déboguez dans la fonction de désactivation, vous pouvez voir que 'this' désigne le button control plutôt que le window.

C'est quelque chose que je n'aime pas dans JavaScript, qui est une incohérence. Au fait, si vous utilisez jQuery ($('#btnSubmit').on('click', disable);), il utilise une liaison explicite.

0
Larry

Javascript a tendance à tout mélanger dans des objets, ce qui peut prêter à confusion. Tout en un est la manière JavaScript.

Essentiellement onclick est un attribut HTML. Inversement, addEventListener est une méthode sur l'objet DOM représentant un élément HTML.

Dans les objets JavaScript, une méthode est simplement une propriété qui a une fonction en tant que valeur et qui est opposée à l'objet auquel elle est attachée (en utilisant ceci par exemple).

En JavaScript en tant qu'élément HTML représenté par DOM, ses attributs sont mappés sur ses propriétés.

C'est là que les gens sont déroutés, car JavaScript fusionne tout dans un conteneur ou un espace de noms unique, sans aucune couche d'indirection.

Dans une disposition normale OO (qui fusionne au moins l'espace de noms des propriétés/méthodes), vous pourriez avoir quelque chose comme:

domElement.addEventListener // Object(Method)
domElement.attributes.onload // Object(Property(Object(Property(String))))

Il existe des variantes, par exemple l'utilisation d'un getter/setter pour onload ou de HashMap pour les attributs, mais en fin de compte, il en serait de même. JavaScript a éliminé cette couche d'indirection au lieu de savoir quoi entre autres. Il a fusionné domElement et attributs.

Sauf compatibilité, vous devriez, comme meilleure pratique, utiliser addEventListener. Tandis que d’autres réponses parlent des différences à cet égard plutôt que des différences programmatiques fondamentales, je vais l’abandonner. Essentiellement, dans un monde idéal, vous ne devez utiliser que du * HTML, mais dans un monde encore plus idéal, vous ne devriez rien faire de semblable à partir de HTML.

Pourquoi est-il dominant aujourd'hui? C'est plus rapide à écrire, plus facile à apprendre et à travailler.

Le but principal de onload en HTML est de donner accès à la méthode ou à la fonctionnalité addEventListener en premier lieu. En l’utilisant dans JS, vous passez par HTML alors que vous pourriez l’appliquer directement.

Hypothétiquement, vous pouvez créer vos propres attributs:

$('[myclick]').each(function(i, v) {
     v.addEventListener('click', function() {
         eval(v.myclick); // eval($(v).attr('myclick'));
     });
});

Ce que JS fait avec est un peu différent de cela.

Vous pouvez l'associer à quelque chose comme (pour chaque élément créé):

element.addEventListener('click', function() {
    switch(typeof element.onclick) {
          case 'string':eval(element.onclick);break;
          case 'function':element.onclick();break;
     }
});

Les détails de la mise en œuvre réelle différeront probablement avec une gamme de variations subtiles rendant les deux légèrement différents dans certains cas, mais c'est l'essentiel de celui-ci.

C'est sans doute un problème de compatibilité que vous pouvez épingler une fonction à un attribut sur puisque par défaut, les attributs sont toutes des chaînes.

0
jgmjgm

addEventListener vous permet de définir plusieurs gestionnaires, mais n'est pas pris en charge dans IE8 ou une version inférieure.

IE a attachEvent, mais ce n'est pas exactement la même chose.

0
user113716