Pourquoi utiliser onClick () en HTML est-il une mauvaise pratique?

133

J'ai souvent entendu dire que l'utilisation d'événements JavaScript, tels que onClick(), en HTML, est une mauvaise pratique, car ce n'est pas bon pour la sémantique. Je voudrais savoir quels sont les inconvénients et comment corriger le code suivant?

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
NiLL
la source
4
Il n'y a rien de mal avec onlickc, mais en faisant le href #, quiconque choisit de désactiver javascript sera bloqué et incapable de faire quoi que ce soit. Pour certains sites, c'est une bonne chose, mais pour simplement ouvrir une fenêtre, il est tout simplement stupide de ne pas fournir un "vrai" lien.
Marc B
2
Lisez certainement ce lien. Cela a très peu à voir avec la sémantique, et plus à voir avec ... tout ce qui se trouve sur cette page :-)
morewry
Essayez d'ouvrir votre lien dans un nouvel onglet, et vous verrez un exemple de pourquoi c'est faux ...
Sebastien C.
2
Cela devrait être un <button>, car les liens sont censés pointer vers une ressource réelle. C'est plus sémantique de cette façon et plus clair pour les utilisateurs de lecteurs d'écran.
programmer5000

Réponses:

171

Vous parlez probablement de Javascript discret , qui ressemblerait à ceci:

<a href="#" id="someLink">link</a>

avec la logique dans un fichier javascript central ressemblant à ceci:

$('#someLink').click(function(){
    popup('/map/', 300, 300, 'map'); 
    return false;
});

Les avantages sont

  • le comportement (Javascript) est séparé de la présentation (HTML)
  • pas de mélange de langues
  • vous utilisez un framework javascript tel que jQuery qui peut gérer la plupart des problèmes multi-navigateurs
  • Vous pouvez ajouter un comportement à de nombreux éléments HTML à la fois sans duplication de code
Michael Borgwardt
la source
30
Vous avez énuméré de nombreux avantages, mais qu'en est-il des inconvénients? Déboguer la fumée bleue?
abelito
2
@abelito: Je ne suis pas sûr que le débogage soit un inconvénient. Si quoi que ce soit, c'est un avantage pour le débogage car vous pouvez parcourir votre JavaScript dans n'importe quel outil de débogage de navigateur. Je ne suis pas sûr que vous puissiez faire cela aussi facilement si le code est en ligne un onClick. Vous pouvez également écrire des tests unitaires si vous le souhaitez par rapport à vos scripts, ce qui est également très difficile à supposer si le code est à l'intérieur d'un onClicket qu'aucune logique n'est séparée. Personnellement, je n'ai aucun problème à déboguer un JavaScript discret et les avantages de la gestion et du test du code JavaScript sont bien trop grands pour ne pas l'utiliser.
Non
45
@ François Wahl: le principal inconvénient est la découvrabilité: regarder le HTML ne vous dit pas quels callbacks y sont attachés, pas même s'il y en a.
Michael Borgwardt
1
@MichaelBorgwardt: Je suis tout à fait d'accord avec vous pour dire que c'est un inconvénient. Si le code est bien structuré et organisé, je ne le vois pas comme un gros problème lors du travail sur un projet ou du débogage de la base de code de quelqu'un d'autre. En général, bien que je sois d'accord avec vous, je ne vois pas la découvrabilité comme une bonne raison d'écrire un script en ligne, des avantages sacralisés tels que la testabilité du code et la possibilité de séparer votre code de fonction / comportement du DOM. Je ne dis pas que le code en ligne est mauvais et ne fonctionnera pas. Cela fonctionne et est absolument parfait, selon si vous êtes intéressé par des choses comme la testabilité ou non.
Non
4
Un autre point de cette discussion, avec les novices, onclickpeut cartographier plus explicitement les relations causales entre l'interaction de l'élément et l'effet (appel de fonction) ainsi que réduire la charge cognitive. De nombreuses leçons plongent les apprenants dans ce addEventListenerqui contient beaucoup plus d'idées dans une syntaxe plus difficile. De plus, les cadres récents basés sur des composants tels que Riot et React utilisent l' onclickattribut et changent la notion de ce que devrait être la séparation. Le transfert du HTML onclickvers un composant qui prend en charge cela peut être un processus d'apprentissage plus efficace.
jmk2142
41

Si vous utilisez jQuery, alors:

HTML:

 <a id="openMap" href="/map/">link</a>

JS:

$(document).ready(function() {
    $("#openMap").click(function(){
        popup('/map/', 300, 300, 'map');
        return false;
    });
});

Cela a l'avantage de fonctionner toujours sans JS, ou si l'utilisateur clique du milieu sur le lien.

Cela signifie également que je pourrais gérer les popups génériques en réécrivant à nouveau dans:

HTML:

 <a class="popup" href="/map/">link</a>

JS:

$(document).ready(function() {
    $(".popup").click(function(){
        popup($(this).attr("href"), 300, 300, 'map');
        return false;
    });
});

Cela vous permettrait d'ajouter un popup à n'importe quel lien en lui donnant simplement la classe popup.

Cette idée pourrait être étendue encore plus loin comme ceci:

HTML:

 <a class="popup" data-width="300" data-height="300" href="/map/">link</a>

JS:

$(document).ready(function() {
    $(".popup").click(function(){
        popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');
        return false;
    });
});

Je peux maintenant utiliser le même morceau de code pour beaucoup de popups sur tout mon site sans avoir à écrire des tas de trucs onclick! Yay pour la réutilisabilité!

Cela signifie également que si plus tard je décide que les popups sont une mauvaise pratique, (ce qu'ils sont!) Et que je veux les remplacer par une fenêtre modale de style lightbox, je peux changer:

popup($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');

à

myAmazingModalWindow($(this).attr("href"), $(this).data('width'), $(this).data('height'), 'map');

et tous mes popups sur tout mon site fonctionnent maintenant de manière totalement différente. Je pourrais même faire une détection de fonctionnalités pour décider quoi faire sur une fenêtre contextuelle, ou stocker une préférence d'utilisateurs pour les autoriser ou non. Avec le onclick en ligne, cela nécessite un énorme effort de copie et de collage.

Rich Bradshaw
la source
4
Fonctionne sans JavaScript ?? C'est jQuery. Il faut que Javascript soit activé sur le navigateur pour fonctionner, non?
Thomas Shields
2
Oui, mais le lien peut rediriger vers une page effectuant l'action sans JavaScript dans ce cas.
ThiefMaster
12
Le lien fonctionne sans JavaScript. Voyez comment le lien a un attribut href normal. Si l'utilisateur n'a pas JavaScript, le lien sera toujours un lien et l'utilisateur peut toujours accéder au contenu.
morewry
3
@Thomas Shields: Non, il veut dire que si vous n'avez pas Javascript, le lien vous mènera tout de même à / map / ...
Robert
2
Ah riche! Nous vous aimons tous pour cette solution la plus astucieuse!
Nirmal
21

Avec de très grandes applications JavaScript, les programmeurs utilisent plus d'encapsulation de code pour éviter de polluer la portée globale. Et pour rendre une fonction disponible à l'action onClick dans un élément HTML, elle doit être dans la portée globale.

Vous avez peut-être vu des fichiers JS qui ressemblent à ceci ...

(function(){
    ...[some code]
}());

Il s'agit d'expressions de fonction immédiatement appelées (IIFE) et toute fonction déclarée à l'intérieur n'existera que dans leur portée interne.

Si vous déclarez function doSomething(){}dans un IIFE, puis effectuez doSomething()l'action onClick d'un élément dans votre page HTML, vous obtiendrez une erreur.

Si, d'un autre côté, vous créez un eventListener pour cet élément dans cet IIFE et appelez doSomething()lorsque l'écouteur détecte un événement de clic, vous êtes bon car l'auditeur et doSomething()partage la portée de l'IIFE.

Pour les petites applications Web avec une quantité minimale de code, cela n'a pas d'importance. Mais si vous aspirez à écrire des bases de code volumineuses et maintenables, onclick=""c'est une habitude que vous devriez éviter.

LetMyPeopleCode
la source
1
si vous aspirez à écrire des bases de code volumineuses et maintenables, utilisez des frameworks JS comme Angular, Vue, React ... qui recommandent de lier les gestionnaires d'événements à l'intérieur de leurs modèles HTML
Kamil Kiełczewski
20

Ce n'est pas bon pour plusieurs raisons:

  • il mélange code et balisage
  • le code écrit de cette façon passe par eval
  • et fonctionne dans la portée mondiale

Le plus simple serait d'ajouter un nameattribut à votre <a>élément, alors vous pourriez faire:

document.myelement.onclick = function() {
    window.popup('/map/', 300, 300, 'map');
    return false;
};

bien que la meilleure pratique moderne consiste à utiliser un idau lieu d'un nom, et à utiliser addEventListener()au lieu d'utiliser onclickpuisque cela vous permet de lier plusieurs fonctions à un seul événement.

Alnitak
la source
Même en suggérant cette méthode, l'utilisateur est confronté à un tas de problèmes de collisions de gestionnaires d'événements.
préaction le
3
@preaction comme le fait un gestionnaire d'événements en ligne, d'où la raison pour laquelle ma réponse dit qu'il vaut mieux l'utiliser addEventListener(), et pourquoi.
Alnitak
2
Quelqu'un peut-il expliquer cela davantage? "le code écrit de cette façon passe par eval" ... Je ne trouve rien sur le web à ce sujet. Êtes-vous en train de dire que l'utilisation de Javascript en ligne présente un inconvénient en termes de performances ou de sécurité?
MarsAndBack
12

Il y a quelques raisons:

  1. Je trouve que cela facilite la maintenance pour séparer le balisage, c'est-à-dire les scripts HTML et côté client. Par exemple, jQuery facilite l'ajout de gestionnaires d'événements par programme.

  2. L'exemple que vous donnez serait interrompu dans tout agent utilisateur qui ne prend pas en charge le javascript ou qui a désactivé javascript. Le concept d' amélioration progressive encouragerait un simple lien hypertexte vers /map/pour les agents utilisateurs sans javascript, puis l'ajout d'un gestionnaire de clics prgramatiquement pour les agents utilisateurs prenant en charge le javascript.

Par exemple:

Balisage:

<a id="example" href="/map/">link</a>

Javascript:

$(document).ready(function(){

    $("#example").click(function(){
        popup('/map/', 300, 300, 'map');
        return false;
    });

})
Graham
la source
2
Merci de me rappeler l'amélioration progressive et cela correspond donc toujours à ce paradigme:<a href="https://stackoverflow.com/map/" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Daniel Sokolowski
10

Révision

L' approche JavaScript discrète était bonne dans le PASSÉ - en particulier la liaison du gestionnaire d'événements en HTML était considérée comme une mauvaise pratique (principalement parce onclick events run in the global scope and may cause unexpected errorque ce qui était mentionné par YiddishNinja )

Toutefois...

Actuellement, il semble que cette approche soit un peu dépassée et nécessite une mise à jour. Si quelqu'un veut être un développeur frontend professionnel et écrire des applications volumineuses et compliquées, il doit utiliser des frameworks tels que Angular, Vue.js, etc ... Cependant, ces frameworks utilisent généralement (ou permettent d'utiliser) des modèles HTML où les gestionnaires d'événements sont liés dans le code de modèle html directement et c'est très pratique, clair et efficace - par exemple, dans un modèle angulaire, les gens écrivent généralement:

<button (click)="someAction()">Click Me</button> 

En js / html brut, l'équivalent de ceci sera

<button onclick="someAction()">Click Me</button>

La différence est que l' onclickévénement js brut est exécuté dans la portée globale - mais les frameworks fournissent une encapsulation.

Alors, où est le problème?

Le problème est quand un programmeur novice qui a toujours entendu dire que html-onclick est mauvais et qui utilise toujours btn.addEventListener("onclick", ... )veut utiliser un cadre avec des modèles (a addEventListenerégalement des inconvénients - si nous mettons à jour le DOM de manière dynamique en utilisant innerHTML=(ce qui est assez rapide ) - alors nous perdons des événements les gestionnaires se lient de cette manière). Ensuite, il fera face à quelque chose comme de mauvaises habitudes ou une mauvaise approche de l'utilisation du framework - et il utilisera le framework d'une très mauvaise manière - car il se concentrera principalement sur la partie js et non sur la partie modèle (et produira des éléments peu clairs et difficiles à maintenir. code). Pour changer ces habitudes, il perdra beaucoup de temps (et il aura probablement besoin d'un peu de chance et d'enseignant).

Donc, à mon avis, sur la base de l'expérience avec mes étudiants, mieux serait pour eux s'ils utilisent html-handlers-bind au début. Comme je l'ai dit, il est vrai que les gestionnaires sont appelés dans une portée globale, mais à ce stade, les étudiants créent généralement de petites applications faciles à contrôler. Pour écrire des applications plus volumineuses, ils choisissent des frameworks.

Alors que faire?

Nous pouvons METTRE À JOUR l'approche JavaScript discrète et autoriser les gestionnaires d'événements de liaison (éventuellement avec des paramètres simples) en html (mais uniquement le gestionnaire de liaison - pas de logique dans onclick comme dans OP quesiton). Donc, à mon avis, en js / html brut, cela devrait être autorisé

<button onclick="someAction(3)">Click Me</button>

ou

Mais les exemples ci-dessous ne doivent PAS être autorisés

<button onclick="console.log('xx'); someAction(); return true">Click Me</button>

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>

La réalité change, notre point de vue devrait aussi

Kamil Kiełczewski
la source
Salut Kamil, je suis un débutant en JavaScript et j'ai également été confronté à une confusion sur l'utilisation d'onclick, c'est-à-dire, pouvez-vous expliquer les inconvénients d'onclick comme suit: 1. Vous ne pouvez avoir qu'un seul événement en ligne attribué. Les événements en ligne sont stockés en tant que propriété de l'élément DOM et, comme toutes les propriétés d'objet, ils peuvent être écrasés. Cet événement continuera à se déclencher même si vous supprimez la propriété onclick.
mirzhal
1
Ad 1. Oui, un seul, mais mon expérience (avec angulaire) montre que cela suffit dans la plupart des situations - mais sinon, utilisez simplement addEventListener. Ad 2. Oui, ils peuvent être écrasés (mais généralement personne ne le fait) - où est le problème? Ad 3. Handler ne sera pas renvoyé après suppression onclick attrib - preuve ici
Kamil Kiełczewski
8

C'est un nouveau paradigme appelé " JavaScript discret ". Le "standard Web" actuel dit de séparer la fonctionnalité et la présentation.

Ce n'est pas vraiment une "mauvaise pratique", c'est juste que la plupart des nouvelles normes veulent que vous utilisiez des écouteurs d'événements au lieu de JavaScript intégré.

En outre, cela peut être juste une chose personnelle, mais je pense que c'est beaucoup plus facile à lire lorsque vous utilisez des écouteurs d'événements, surtout si vous avez plus d'une instruction JavaScript que vous souhaitez exécuter.

Fusée Hazmat
la source
2
La page Wikipédia sur le sujet a plus de 4 ans. Ce n'est plus un nouveau paradigme. :)
Quentin
@David: Ce n'est peut-être pas "nouveau", mais il y a des gens qui ne l'utilisent toujours pas, donc c'est "nouveau" pour eux.
Rocket Hazmat
6

Votre question déclenchera une discussion, je suppose. L'idée générale est qu'il est bon de séparer le comportement et la structure. De plus, afaik, un gestionnaire de clics en ligne doit être evalamené à «devenir» une véritable fonction javascript. Et c'est assez démodé, même si c'est un argument assez fragile. Ah, eh bien, lisez-en quelques-uns à ce sujet @ quirksmode.org

KooiInc
la source
Je ne veux pas créer une fois de plus la guerre sainte, j'essaye de trouver vrai: \
NiLL
2
  • Les événements onclick s'exécutent dans la portée globale et peuvent provoquer une erreur inattendue.
  • L'ajout d'événements onclick à de nombreux éléments DOM ralentira les
    performances et l'efficacité.
Minghuan
la source
0

Deux autres raisons de ne pas utiliser de gestionnaires en ligne:

Ils peuvent nécessiter un devis fastidieux pour échapper à des problèmes

Étant donné une chaîne arbitraire , si vous voulez être en mesure de construire un gestionnaire en ligne qui appelle une fonction avec cette chaîne, pour la solution générale, vous devrez échapper les délimiteurs d' attribut (avec l'entité HTML associée), et vous doivent échapper au délimiteur utilisé pour la chaîne à l'intérieur de l'attribut, comme suit:

const str = prompt('What string to display on click?', 'foo\'"bar');
const escapedStr = str
  // since the attribute value is going to be using " delimiters,
  // replace "s with their corresponding HTML entity:
  .replace(/"/g, '&quot;')
  // since the string literal inside the attribute is going to delimited with 's,
  // escape 's:
  .replace(/'/g, "\\'");
  
document.body.insertAdjacentHTML(
  'beforeend',
  '<button onclick="alert(\'' + escapedStr + '\')">click</button>'
);

C'est incroyablement moche. À partir de l'exemple ci-dessus, si vous ne remplacez pas le 's, une SyntaxError en résultera, car la alert('foo'"bar')syntaxe n'est pas valide. Si vous ne remplacez pas le "s, le navigateur l'interpréterait comme une fin de l' onclickattribut (délimité par "s ci-dessus), ce qui serait également incorrect.

Si l'on utilise habituellement des gestionnaires en ligne, il faudrait s'assurer de se rappeler de faire quelque chose de similaire à ce qui précède (et de le faire correctement ) à chaque fois , ce qui est fastidieux et difficile à comprendre en un coup d'œil. Mieux vaut éviter complètement les gestionnaires en ligne pour que la chaîne arbitraire puisse être utilisée dans une simple fermeture:

const str = prompt('What string to display on click?', 'foo\'"bar');
const button = document.body.appendChild(document.createElement('button'));
button.textContent = 'click';
button.onclick = () => alert(str);

N'est-ce pas tellement plus agréable?


La chaîne de portée d'un gestionnaire en ligne est extrêmement particulière

Que pensez-vous que le code suivant enregistrera?

let disabled = true;
<form>
  <button onclick="console.log(disabled);">click</button>
</form>

Essayez-le, exécutez l'extrait. Ce n'est probablement pas ce à quoi vous vous attendiez. Pourquoi produit-il ce qu'il fait? Parce que les gestionnaires en ligne s'exécutent à l'intérieur de withblocs. Le code ci-dessus se trouve dans trois with blocs: un pour le document, un pour le <form>et un pour le <button>:

entrez la description de l'image ici

Puisqu'il disableds'agit d'une propriété du bouton, le référencement disabledà l'intérieur du gestionnaire en ligne fait référence à la propriété du bouton et non à la disabledvariable externe . C'est assez contre-intuitif. witha de nombreux problèmes: il peut être la source de bogues déroutants et ralentit considérablement le code. Ce n'est même pas du tout autorisé en mode strict. Mais avec les gestionnaires en ligne, vous êtes obligé d'exécuter le code via withs - et pas seulement via un with, mais via plusieurs withs imbriqués . C'est fou.

withne doit jamais être utilisé dans le code. Étant donné que les gestionnaires en ligne exigent implicitement withavec tout son comportement déroutant, les gestionnaires en ligne doivent également être évités.

CertainPerformance
la source