Warum ist die Verwendung von onClick () in HTML eine schlechte Praxis?

133

Ich habe oft gehört, dass die Verwendung von JavaScript-Ereignissen, beispielsweise onClick()in HTML, eine schlechte Praxis ist, da sie für die Semantik nicht gut ist. Ich möchte wissen, was die Nachteile sind und wie man den folgenden Code behebt.

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
NiLL
quelle
4
Es ist nichts falsch mit dem Onlickc, aber wenn Sie das href machen #, wird jeder, der Javascript deaktiviert hat, stecken bleiben und nichts tun können. Für einige Websites ist das eine gute Sache, aber zum einfachen Öffnen eines Fensters ist es absolut dumm, keinen "echten" Link bereitzustellen.
Marc B
2
Lesen Sie auf jeden Fall diesen Link. Es hat sehr wenig mit Semantik zu tun und mehr mit ... all dem Zeug auf dieser Seite :-)
Morewry
Versuchen Sie, Ihren Link in einem neuen Tab zu öffnen, und Sie werden ein Beispiel dafür sehen, warum es falsch ist ...
Sebastien C.
2
Das sollte ein sein <button>, da Links auf eine tatsächliche Ressource verweisen sollen. Auf diese Weise ist es semantischer und für Benutzer von Bildschirmleseprogrammen klarer.
Programmierer5000

Antworten:

171

Sie sprechen wahrscheinlich von unauffälligem Javascript , das so aussehen würde:

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

mit der Logik in einer zentralen Javascript-Datei, die ungefähr so ​​aussieht:

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

Die Vorteile sind

  • Verhalten (Javascript) ist von Präsentation (HTML) getrennt
  • kein Mischen von Sprachen
  • Sie verwenden ein Javascript-Framework wie jQuery, das die meisten browserübergreifenden Probleme für Sie lösen kann
  • Sie können vielen HTML-Elementen gleichzeitig Verhalten hinzufügen, ohne dass Code dupliziert wird
Michael Borgwardt
quelle
30
Sie haben einige Vorteile aufgelistet, aber was ist mit den Nachteilen? Blauen Rauch debuggen?
Abelito
2
@abelito: Nicht sicher, ob das Debuggen ein Nachteil ist. Wenn überhaupt, ist das Debuggen von Vorteil, da Sie Ihr JavaScript in jedem Browser-Debugging-Tool schrittweise durchlaufen können. Ich bin mir nicht sicher, ob Sie das so einfach machen können, wenn der Code inline ist onClick. Sie können auch Unit-Tests schreiben, wenn Sie dies gegen Ihre Skripte tun möchten, was ebenfalls sehr schwierig ist. Ich würde annehmen, wenn sich der Code in einem befindet onClickund keine Logik getrennt ist. Ich persönlich habe kein Problem damit, unauffälliges JavaScript zu debuggen, und die Vorteile beim Verwalten und Testen von JavaScript-Code sind viel zu groß, um ihn nicht zu verwenden.
Nein,
45
@ François Wahl: Der Hauptnachteil ist die Auffindbarkeit: Wenn Sie sich den HTML-Code ansehen, sehen Sie nicht, welche Rückrufe damit verbunden sind, auch nicht, wenn es welche gibt.
Michael Borgwardt
1
@ MichaelBorgwardt: Ich stimme Ihnen voll und ganz zu, dass dies ein Nachteil ist. Wenn Code gut strukturiert und organisiert ist, sehe ich es nicht als großes Problem an, wenn ich an einem Projekt arbeite oder eine andere Codebasis debugge. Obwohl ich Ihnen zustimme, sehe ich die Auffindbarkeit im Allgemeinen nicht als guten Grund, Inline-Skripte zu schreiben, was Vorteile wie die Testbarkeit von Code und die Möglichkeit, Ihren Funktions- / Verhaltenscode vom DOM zu trennen, beeinträchtigt. Ich sage nicht, dass Inline-Code schlecht ist und nicht funktioniert. Es funktioniert und ist absolut in Ordnung, je nachdem, ob Sie an Dingen wie Testbarkeit interessiert sind oder nicht.
Nein,
4
Ein weiterer Punkt dieser Diskussion mit Anfängern: Sie onclickkönnen kausale Zusammenhänge zwischen der Elementinteraktion und dem Effekt (Funktionsaufruf) expliziter abbilden und die kognitive Belastung verringern. Viele Lektionen werfen die Lernenden in das, addEventListenerwas viel mehr Ideen in eine schwierigere Syntax packt. Darüber hinaus verwenden neuere komponentenbasierte Frameworks wie Riot und React das onclickAttribut und ändern die Vorstellung davon, was Trennung sein sollte. Das Übertragen von HTML onclickauf eine Komponente, die dies unterstützt, ist möglicherweise ein effektiverer "Schritt" -Prozess zum Lernen.
jmk2142
41

Wenn Sie jQuery verwenden, dann:

HTML:

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

JS:

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

Dies hat den Vorteil, dass Sie weiterhin ohne JS arbeiten oder wenn die Benutzermitte auf den Link klickt.

Es bedeutet auch, dass ich generische Popups verarbeiten könnte, indem ich sie erneut umschreibe in:

HTML:

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

JS:

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

Auf diese Weise können Sie jedem Link ein Popup hinzufügen, indem Sie ihm einfach die Popup-Klasse geben.

Diese Idee könnte noch weiter erweitert werden:

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;
    });
});

Ich kann jetzt das gleiche Stück Code für viele Popups auf meiner gesamten Site verwenden, ohne viele Onclick-Inhalte schreiben zu müssen! Ja für die Wiederverwendbarkeit!

Es bedeutet auch, dass ich Folgendes ändern kann, wenn ich später feststelle, dass Popups eine schlechte Praxis sind (was sie sind!) Und dass ich sie durch ein modales Fenster im Lightbox-Stil ersetzen möchte:

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

zu

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

und alle meine Popups auf meiner gesamten Website funktionieren jetzt völlig anders. Ich könnte sogar eine Feature-Erkennung durchführen, um zu entscheiden, was in einem Popup zu tun ist, oder die Präferenz eines Benutzers speichern, um sie zuzulassen oder nicht. Mit dem Inline-Onclick erfordert dies einen großen Aufwand beim Kopieren und Einfügen.

Rich Bradshaw
quelle
4
Funktioniert ohne JavaScript? Es ist jQuery. Javascript muss im Browser aktiviert sein, damit es funktioniert, oder?
Thomas Shields
2
Ja, aber der Link kann in diesem Fall zu einer Seite umleiten, auf der die Aktion ohne JavaScript ausgeführt wird.
ThiefMaster
12
Der Link funktioniert ohne JavaScript. Sehen Sie, wie der Link ein normales href-Attribut hat. Wenn der Benutzer kein JavaScript hat, ist der Link weiterhin ein Link und der Benutzer kann weiterhin auf den Inhalt zugreifen.
Morgen
3
@ Thomas Shields: Nein, er meint, wenn Sie kein Javascript haben, führt Sie der Link trotzdem zu / map / ...
Robert
2
Ah Rich! Wir alle lieben Sie für diese raffinierteste Lösung!
Nirmal
21

Bei sehr großen JavaScript-Anwendungen verwenden Programmierer eine stärkere Kapselung von Code, um eine Verschmutzung des globalen Bereichs zu vermeiden. Und um eine Funktion für die onClick-Aktion in einem HTML-Element verfügbar zu machen, muss sie sich im globalen Bereich befinden.

Möglicherweise haben Sie JS-Dateien gesehen, die so aussehen ...

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

Hierbei handelt es sich um sofort aufgerufene Funktionsausdrücke (IIFEs), und alle darin deklarierten Funktionen sind nur in ihrem internen Bereich vorhanden.

Wenn Sie function doSomething(){}innerhalb eines IIFE deklarieren und dann doSomething()die onClick-Aktion eines Elements in Ihrer HTML-Seite ausführen, wird eine Fehlermeldung angezeigt.

Wenn Sie andererseits einen eventListener für dieses Element in diesem IIFE erstellen und aufrufen, doSomething()wenn der Listener ein Klickereignis erkennt, sind Sie gut, weil der Listener doSomething()den Bereich des IIFE teilt.

Für kleine Web-Apps mit einer minimalen Menge an Code spielt dies keine Rolle. Wenn Sie jedoch große, wartbare Codebasen schreiben möchten, onclick=""sollten Sie daran arbeiten, dies zu vermeiden.

LetMyPeopleCode
quelle
1
Wenn Sie große, wartbare Codebasen schreiben möchten, verwenden Sie JS-Frameworks wie Angular, Vue, React ..., die empfehlen, Ereignishandler in ihre HTML-Vorlagen
einzubinden
20

Es ist aus mehreren Gründen nicht gut:

  • Es mischt Code und Markup
  • Code, der auf diese Weise geschrieben wurde, geht durch eval
  • und läuft im globalen Bereich

Am einfachsten wäre es, nameIhrem <a>Element ein Attribut hinzuzufügen , dann könnten Sie Folgendes tun:

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

Obwohl die moderne Best Practice darin besteht, idanstelle eines Namens einen zu verwenden und addEventListener()statt zu verwenden onclick, können Sie damit mehrere Funktionen an ein einzelnes Ereignis binden.

Alnitak
quelle
Selbst wenn Sie diesen Weg vorschlagen, kann der Benutzer eine Reihe von Problemen mit Event-Handler-Kollisionen lösen.
Predction
3
@preaction genau wie ein Inline-Event-Handler, daher sagt meine Antwort, dass es besser ist, es zu verwenden addEventListener(), und warum.
Alnitak
2
Kann jemand das mehr erklären? "Code, der auf diese Weise geschrieben wurde, geht durch eval" ... Ich finde nichts im Web darüber. Wollen Sie damit sagen, dass die Verwendung von Inline-Javascript Leistungs- oder Sicherheitsnachteile mit sich bringt?
MarsAndBack
12

Es gibt einige Gründe:

  1. Ich finde, es hilft bei der Wartung, Markups zu trennen, dh die HTML- und clientseitigen Skripte. Mit jQuery ist es beispielsweise einfach, Ereignishandler programmgesteuert hinzuzufügen.

  2. Das von Ihnen angegebene Beispiel ist in jedem Benutzeragenten fehlerhaft, der kein Javascript unterstützt oder dessen Javascript deaktiviert ist. Das Konzept der progressiven Verbesserung würde einen einfachen Hyperlink /map/für Benutzeragenten ohne Javascript fördern und dann programmgesteuert einen Klick-Handler für Benutzeragenten hinzufügen, die Javascript unterstützen.

Beispielsweise:

Markup:

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

Javascript:

$(document).ready(function(){

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

})
Graham
quelle
2
Vielen Dank, dass Sie mich an die fortschreitende Verbesserung erinnert haben, und dies passt auch noch zu diesem Paradigma:<a href="https://stackoverflow.com/map/" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Daniel Sokolowski
10

Revision

Der unauffällige JavaScript- Ansatz war in der Vergangenheit gut - insbesondere die Bindung von Ereignishandlern in HTML wurde als schlechte Praxis angesehen (hauptsächlich, weil das, onclick events run in the global scope and may cause unexpected errorwas von YiddishNinja erwähnt wurde ).

Jedoch...

Derzeit scheint dieser Ansatz etwas veraltet zu sein und muss aktualisiert werden. Wenn jemand ein professioneller Frontend-Entwickler sein und große und komplizierte Apps schreiben möchte, muss er Frameworks wie Angular, Vue.js usw. verwenden. Diese Frameworks verwenden jedoch normalerweise HTML-Vorlagen (oder erlauben die Verwendung), in denen Event-Handler gebunden sind direkt in HTML-Vorlagencode und dies ist sehr praktisch, klar und effektiv - zB in eckigen Vorlagen schreiben die Leute normalerweise:

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

In rohem js / html ist das Äquivalent dazu

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

Der Unterschied besteht darin, dass in raw js das onclickEreignis im globalen Bereich ausgeführt wird - die Frameworks bieten jedoch eine Kapselung.

Wo liegt also das Problem?

Das Problem ist, wenn unerfahrene Programmierer, die immer gehört haben, dass HTML-Onclick schlecht ist und die immer btn.addEventListener("onclick", ... )ein Framework mit Vorlagen verwenden möchten ( addEventListenerauch Nachteile haben - wenn wir DOM auf dynamische Weise mit aktualisieren innerHTML=(was ziemlich schnell ist ) -, verlieren wir Ereignisse Handler binden auf diese Weise). Dann wird er mit schlechten Gewohnheiten oder einer falschen Herangehensweise an die Framework-Nutzung konfrontiert sein - und er wird das Framework auf sehr schlechte Weise verwenden -, weil er sich hauptsächlich auf den js-Teil und nicht auf den Template-Teil konzentriert (und unklare und schwer zu wartende Produkte produziert) Code). Um diese Gewohnheiten zu ändern, wird er viel Zeit verlieren (und wahrscheinlich wird er etwas Glück und Lehrer brauchen).

Meiner Meinung nach, basierend auf den Erfahrungen mit meinen Schülern, wäre es für sie besser, wenn sie zu Beginn HTML-Handler-Bind verwenden. Wie ich bereits sagte, sind Handler zwar global aufgerufen, aber in dieser Phase erstellen die Schüler normalerweise kleine Anwendungen, die leicht zu steuern sind. Um größere Anwendungen zu schreiben, wählen sie einige Frameworks.

Was tun?

Wir können den unauffälligen JavaScript-Ansatz AKTUALISIEREN und Bindungsereignishandler (eventuell mit einfachen Parametern) in HTML zulassen (aber nur Bindungshandler - keine Logik wie bei einer OP-Abfrage in einen Klick setzen). Meiner Meinung nach sollte dies in rohem js / html erlaubt sein

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

oder

Die folgenden Beispiele sollten jedoch NICHT zulässig sein

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

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

Die Realität ändert sich, sollte auch unser Standpunkt

Kamil Kiełczewski
quelle
Hallo Kamil, ich bin Anfänger in JavaScript und habe auch Verwirrung hinsichtlich der Verwendung von onclick erfahren. Das heißt, können Sie die Nachteile von onclick wie folgt erklären: 1. Möglicherweise ist nur ein Inline-Ereignis zugewiesen. 2. Inline-Ereignisse werden als Eigenschaft des DOM-Elements gespeichert und können wie alle Objekteigenschaften überschrieben werden. 3.Dieses Ereignis wird auch dann weiter ausgelöst, wenn Sie die Eigenschaft onclick löschen.
Mirzhal
1
Anzeige 1. Ja, nur eine, aber meine Erfahrung (mit Winkel) zeigt, dass dies in den meisten Situationen ausreicht - wenn nicht, dann nur verwenden addEventListener. Anzeige 2. Ja, sie können überschrieben werden (aber normalerweise tut es niemand) - wo liegt das Problem? Anzeige 3. Handler wird nicht nach dem Löschen auf Klick Attribut hier
Kamil Kiełczewski
8

Es ist ein neues Paradigma namens " Unauffälliges JavaScript ". Der aktuelle "Webstandard" sieht vor, Funktionalität und Präsentation zu trennen.

Es ist nicht wirklich eine "schlechte Praxis", es ist nur so, dass die meisten neuen Standards möchten, dass Sie Ereignis-Listener verwenden, anstatt JavaScript einzubinden.

Dies mag auch nur eine persönliche Sache sein, aber ich denke, es ist viel einfacher zu lesen, wenn Sie Ereignis-Listener verwenden, insbesondere wenn Sie mehr als eine JavaScript-Anweisung haben, die Sie ausführen möchten.

Rakete Hazmat
quelle
2
Die Wikipedia-Seite zu diesem Thema ist über 4 Jahre alt. Es ist kein neues Paradigma mehr. :)
Quentin
@ David: Es ist vielleicht nicht "neu", aber es gibt Leute, die es immer noch nicht benutzen, also ist es "neu" für sie.
Rocket Hazmat
6

Ihre Frage wird wohl eine Diskussion auslösen. Die allgemeine Idee ist, dass es gut ist, Verhalten und Struktur zu trennen. Außerdem muss ein Inline-Klick-Handler afaik evaldazu gebracht werden, eine echte Javascript-Funktion zu werden. Und es ist ziemlich altmodisch, obwohl das ein ziemlich wackeliges Argument ist. Ah, nun, lesen Sie etwas darüber @ quirksmode.org

KooiInc
quelle
Ich will keinen heiligen Krieg mehr führen, ich versuche wahr zu finden: \
NiLL
2
  • Onclick-Ereignisse werden im globalen Bereich ausgeführt und können unerwartete Fehler verursachen.
  • Das Hinzufügen von Onclick-Ereignissen zu vielen DOM-Elementen verlangsamt die
    Leistung und Effizienz.
Minghuan
quelle
0

Zwei weitere Gründe, keine Inline-Handler zu verwenden:

Sie können langwierige Probleme erfordern

Eine gegebene beliebige Zeichenfolge, wenn Sie einen Inline - Handler in der Lage sein wollen , zu konstruieren , die eine Funktion mit dieser Zeichenfolge aufruft, für die allgemeine Lösung, werden Sie die entkommen müssen Attribut Trennzeichen (mit dem zugehörigen HTML - Entität), und du wirst müssen das Trennzeichen, das für die Zeichenfolge innerhalb des Attributs verwendet wird, wie folgt umgehen:

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>'
);

Das ist unglaublich hässlich. Wenn Sie im obigen Beispiel das 's nicht ersetzen würden, würde sich ein SyntaxError ergeben, da alert('foo'"bar')die Syntax nicht gültig ist. Wenn Sie das "s nicht ersetzen würden, würde der Browser es als Ende des onclickAttributs (getrennt durch "s oben) interpretieren , was ebenfalls falsch wäre.

Wenn man gewöhnlich Inline-Handler verwendet, muss man sich daran erinnern, jedes Mal etwas Ähnliches wie das oben Gesagte zu tun (und es richtig zu machen ) , was auf einen Blick mühsam und schwer zu verstehen ist. Vermeiden Sie Inline-Handler besser vollständig, damit die beliebige Zeichenfolge in einem einfachen Abschluss verwendet werden kann:

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);

Ist das nicht so viel schöner?


Die Scope-Kette eines Inline-Handlers ist äußerst eigenartig

Was glaubst du, wird der folgende Code protokollieren?

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

Probieren Sie es aus, führen Sie das Snippet aus. Es ist wahrscheinlich nicht das, was Sie erwartet hatten. Warum produziert es, was es tut? Weil Inline-Handler in withBlöcken ausgeführt werden. Der obige Code befindet sich in drei with Blöcken: einer für den document, einer für den <form>und einer für den <button>:

Geben Sie hier die Bildbeschreibung ein

Da disabledes sich um eine Eigenschaft der Schaltfläche handelt, disabledbezieht sich die Referenzierung im Inline-Handler auf die Eigenschaft der Schaltfläche und nicht auf die äußere disabledVariable. Dies ist ziemlich kontraintuitiv. withhat viele Probleme: Es kann die Quelle verwirrender Fehler sein und den Code erheblich verlangsamen. Im strengen Modus ist das überhaupt nicht erlaubt. Aber mit Inline - Handler, sind Sie gezwungen , den Code durchlaufen withs - und das nicht nur durch eine with, sondern durch mehr verschachtelten withs. Es ist verrückt.

withsollte niemals im Code verwendet werden. Da Inline-Handler implizit withein verwirrendes Verhalten erfordern , sollten auch Inline-Handler vermieden werden.

Bestimmte Leistung
quelle