Perché usare onClick () in HTML è una cattiva pratica?


133

Ho sentito molte volte che usare eventi JavaScript, come onClick()HTML, è una cattiva pratica, perché non è buono per la semantica. Vorrei sapere quali sono gli svantaggi e come risolvere il codice seguente?

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


4
Non c'è niente di sbagliato in onlickc, ma facendo l'href #, chiunque scelga di disabilitare javascript rimarrà bloccato e incapace di fare nulla. Per alcuni siti è una buona cosa, ma per aprire semplicemente una finestra, è assolutamente stupido non fornire un link "reale".
Marc B,

2
Sicuramente leggi quel link. Ha molto poco a che fare con la semantica, e altro ancora con ... tutte le cose su quella pagina :-)
morewry

Prova ad aprire il tuo link in una nuova scheda e vedrai un esempio del perché è sbagliato ...
Sebastien C.

2
Questo dovrebbe essere un <button>, perché i collegamenti dovrebbero puntare a una risorsa reale. È più semantico in questo modo ed è più chiaro per gli utenti di screen reader.
programmatore

Risposte:


171

Probabilmente stai parlando di Javascript discreto , che sarebbe simile a questo:

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

con la logica in un file JavaScript centrale simile a questo:

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

I vantaggi sono:

  • il comportamento (Javascript) è separato dalla presentazione (HTML)
  • nessuna miscelazione di lingue
  • stai utilizzando un framework javascript come jQuery in grado di gestire la maggior parte dei problemi tra browser
  • È possibile aggiungere il comportamento a molti elementi HTML contemporaneamente senza duplicazione del codice

30
Hai elencato alcuni vantaggi, ma per quanto riguarda gli svantaggi? Debug del fumo blu?
abelito,

2
@abelito: non è sicuro che il debug sia uno svantaggio. Semmai è un vantaggio per il debug in quanto è possibile scorrere il JavaScript all'interno di qualsiasi strumento di debug del browser. Non sono sicuro di poterlo fare facilmente se il codice è in linea e onClick. Puoi anche scrivere unit test se desideri contro i tuoi script, il che è anche molto difficile, suppongo che se il codice è all'interno di una onClicklogica e nessuna logica è separata. Personalmente non ho alcun problema con il debug di JavaScript discreto e i vantaggi nella gestione e nel test del codice JavaScript sono di gran lunga grandi per non usarlo.
No, il

45
@ François Wahl: il principale svantaggio è la rilevabilità: guardare l'HTML non ti dice quali callback sono collegati ad esso, nemmeno se ce ne sono.
Michael Borgwardt,

1
@MichaelBorgwardt: concordo pienamente con te sul fatto che sia uno svantaggio. Se il codice è ben strutturato e organizzato, non lo vedo come un grosso problema quando si lavora su un progetto o si esegue il debug di qualcun altro codice base. In generale, mentre sono d'accordo con te, non vedo che la rilevabilità sia una buona ragione per scrivere script in linea, vantaggi di sacrificio come la testabilità del codice e la capacità di separare il codice di funzione / comportamento dal DOM. Non sto dicendo che il codice in linea è male e non funzionerà. Lo fa ed è assolutamente perfetto, a seconda che tu sia interessato a cose come la testabilità o meno.
No, il

4
Un altro punto di questa discussione, con i novizi, è in onclickgrado di mappare più esplicitamente le relazioni causali tra l'interazione dell'elemento e l'effetto (chiamata di funzione) e ridurre il carico cognitivo. Molte lezioni gettano gli studenti in ciò addEventListenerche racchiude molte più idee in una sintassi più difficile. Inoltre, quadri recenti basati su componenti come Riot e React usano l' onclickattributo e stanno cambiando l'idea di quale separazione dovrebbe essere. Il trasferimento da HTML onclicka un componente che supporta questo potrebbe essere un processo "passo" più efficace per l'apprendimento.
jmk2142

41

Se stai usando jQuery, allora:

HTML:

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

JS:

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

Questo ha il vantaggio di continuare a lavorare senza JS o se l'utente fa clic sul collegamento al centro.

Significa anche che potrei gestire i popup generici riscrivendoli di nuovo a:

HTML:

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

JS:

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

Ciò ti consentirebbe di aggiungere un popup a qualsiasi link semplicemente assegnandogli la classe popup.

Questa idea potrebbe essere ulteriormente estesa in questo modo:

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

Ora posso usare lo stesso codice per molti popup su tutto il mio sito senza dover scrivere un sacco di cose onclick! Yay per riusabilità!

Significa anche che se in seguito decido che i popup sono cattive pratiche (che sono!) E che voglio sostituirli con una finestra modale in stile lightbox, posso cambiare:

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

per

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

e tutti i miei popup sull'intero sito ora funzionano in modo totalmente diverso. Potrei anche fare il rilevamento delle funzionalità per decidere cosa fare in un popup o memorizzare le preferenze degli utenti per consentirli o meno. Con il clic on-line, ciò richiede un enorme sforzo di copia e incolla.


4
Funziona senza JavaScript ?? È jQuery. Per funzionare, è necessario che Javascript sia abilitato sul browser, giusto?
Thomas Shields,

2
Sì, ma il link può reindirizzare a una pagina che esegue l'azione senza JavaScript in questo caso.
ThiefMaster,

12
Il collegamento funziona senza JavaScript. Scopri come il collegamento ha un normale attributo href. Se l'utente non ha JavaScript, il collegamento rimarrà comunque un collegamento e l'utente potrà comunque accedere al contenuto.
altro

3
@Thomas Shields: No, significa che se non hai Javascript, il link ti porterà comunque a / map / ...
Robert,

2
Ah ricco! Ti amiamo tutti per questa soluzione più intelligente!
Nirmal,

21

Con applicazioni JavaScript di dimensioni molto grandi, i programmatori utilizzano una maggiore incapsulamento del codice per evitare di inquinare l'ambito globale. E per rendere una funzione disponibile per l'azione onClick in un elemento HTML, deve essere nell'ambito globale.

Potresti aver visto i file JS che assomigliano a questo ...

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

Si tratta di espressioni di funzione invocate immediatamente (IIFE) e qualsiasi funzione dichiarata al loro interno esiste solo nell'ambito di applicazione interno.

Se dichiari function doSomething(){}all'interno di un IIFE, quindi esegui doSomething()un'azione onClick di un elemento nella tua pagina HTML, otterrai un errore.

Se, d'altra parte, crei un eventListener per quell'elemento all'interno di quell'IIFE e chiami doSomething()quando l'ascoltatore rileva un evento click, sei bravo perché ascoltatore e doSomething()condividi l'ambito di IIFE.

Per le piccole app Web con una quantità minima di codice, non importa. Ma se aspiri a scrivere basi di codice grandi e mantenibili, onclick=""è un'abitudine che dovresti lavorare per evitare.


1
se aspiri a scrivere basi di codice grandi e gestibili, usa framework JS come Angular, Vue, React ... che raccomandano di associare i gestori di eventi all'interno dei loro template HTML
Kamil Kiełczewski

20

Non va bene per diversi motivi:

  • mescola codice e markup
  • il codice scritto in questo modo passa attraverso eval
  • e funziona nell'ambito globale

La cosa più semplice sarebbe aggiungere un nameattributo al tuo <a>elemento, quindi potresti fare:

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

sebbene la migliore pratica moderna sarebbe quella di usare un idinvece di un nome, e usare addEventListener()invece di usare onclickpoiché ciò consente di associare più funzioni a un singolo evento.


Anche suggerire questo modo apre l'utente a una serie di problemi con le collisioni del gestore di eventi.
preazione

3
@preaction proprio come fa un gestore di eventi inline, quindi perché la mia risposta dice che è meglio usare addEventListener()e perché.
Alnitak,

2
Qualcuno può spiegarlo di più? "il codice scritto in questo modo passa per eval" ... Non trovo nulla sul web al riguardo. Stai dicendo che c'è qualche svantaggio in termini di prestazioni o sicurezza nell'utilizzo di Javascript in linea?
MarsAndBack

12

Ci sono alcuni motivi:

  1. Trovo che aiuti la manutenzione per separare il markup, vale a dire l'HTML e gli script lato client. Ad esempio, jQuery semplifica l'aggiunta programmativa di gestori di eventi.

  2. L'esempio fornito verrà interrotto in qualsiasi agente utente che non supporta javascript o che JavaScript è disattivato. Il concetto di miglioramento progressivo incoraggerebbe un semplice collegamento ipertestuale /map/per gli user agent senza javascript, quindi l'aggiunta di un gestore di clic in modo prgrammatico per gli user agent che supportano javascript.

Per esempio:

markup:

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

Javascript:

$(document).ready(function(){

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

})

2
Grazie per avermi ricordato il miglioramento progressivo e quindi questo si adatta anche a quel paradigma:<a href="https://stackoverflow.com/map/" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Daniel Sokolowski

10

Revisione

L' approccio JavaScript non invadente è stato positivo nel PAST, in particolare il legame del gestore di eventi in HTML è stato considerato una cattiva pratica (principalmente perché onclick events run in the global scope and may cause unexpected errorciò che è stato menzionato da YiddishNinja )

Però...

Attualmente sembra che questo approccio sia un po 'datato e necessiti di un aggiornamento. Se qualcuno vuole essere uno sviluppatore di frontend professionale e scrivere app di grandi dimensioni e complicate, allora deve usare framework come Angular, Vue.js, ecc ... Comunque quei framework usano solitamente (o permettono di usare) template HTML dove i gestori di eventi sono vincolati nel codice del modello html direttamente e questo è molto utile, chiaro ed efficace - ad esempio nel modello angolare di solito le persone scrivono:

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

In js / html non elaborato sarà l'equivalente di questo

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

La differenza è che nel raw js l' onclickevento viene eseguito nell'ambito globale - ma i framework forniscono l'incapsulamento.

Quindi dov'è il problema?

Il problema è quando il programmatore principiante che ha sempre sentito che html-onclick è male e che usa sempre btn.addEventListener("onclick", ... )vuole usare un framework con i template (ha addEventListeneranche degli svantaggi - se aggiorniamo DOM in modo dinamico usando innerHTML=(che è piuttosto veloce ) - perdiamo gli eventi i gestori si legano in quel modo). Quindi dovrà affrontare qualcosa come le cattive abitudini o l'approccio sbagliato all'utilizzo del framework - e userà il framework in modo molto cattivo - perché si concentrerà principalmente su js-part e no su template-part (e produrrà poco chiaro e difficile da mantenere codice). Per cambiare questa abitudine perderà molto tempo (e probabilmente avrà bisogno di fortuna e di insegnante).

Quindi secondo me, in base all'esperienza con i miei studenti, sarebbe meglio per loro se all'inizio usassero html-handlers-bind. Come ho detto, è vero che i gestori sono chiamati in ambito globale, ma in questa fase gli studenti di solito creano piccole applicazioni che sono facili da controllare. Per scrivere applicazioni più grandi scelgono alcuni framework.

Quindi che si fa?

Possiamo AGGIORNARE l'approccio JavaScript discreto e consentire i gestori di eventi bind (eventualmente con parametri semplici) in html (ma solo il gestore bind - non mettere la logica in onclick come in OP quesiton). Quindi secondo me in js / html non elaborati questo dovrebbe essere permesso

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

o

Ma sotto gli esempi NON dovrebbero essere ammessi

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

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

La realtà cambia, anche il nostro punto di vista dovrebbe


ciao Kamil, sono un principiante di JavaScript e ho anche incontrato confusione sull'uso di onclick, vale a dire, per favore, puoi spiegare gli svantaggi di onclick come segue: 1. Potresti avere solo un evento in linea assegnato. 2.Gli eventi incorporati vengono archiviati come proprietà dell'elemento DOM e, come tutte le proprietà dell'oggetto, possono essere sovrascritti. 3.Questo evento continuerà a essere attivato anche se si elimina la proprietà onclick.
mirzhal

1
Annuncio 1. Sì, solo uno, tuttavia la mia esperienza (con angolare) mostra che questo è sufficiente nella maggior parte delle situazioni - tuttavia, se non, basta usare addEventListener. Annuncio 2. Sì, possono essere sovrascritti (tuttavia di solito nessuno lo fa) - dov'è il problema? Annuncio 3. Il gestore non verrà licenziato dopo l'eliminazione
dell'attributo

8

È un nuovo paradigma chiamato " JavaScript discreto ". L'attuale "standard web" dice di separare funzionalità e presentazione.

Non è proprio una "cattiva pratica", è solo che la maggior parte dei nuovi standard vuole che tu usi gli ascoltatori di eventi invece di incorporare JavaScript.

Inoltre, questa potrebbe essere solo una cosa personale, ma penso che sia molto più facile da leggere quando usi i listener di eventi, specialmente se hai più di una dichiarazione JavaScript che vuoi eseguire.


2
La pagina di Wikipedia sull'argomento ha più di 4 anni. Non è più un nuovo paradigma. :)
Quentin,

@David: potrebbe non essere "nuovo", ma ci sono ancora persone che non lo usano, quindi è "nuovo" per loro.
Rocket Hazmat,

6

La tua domanda scatenerà la discussione, suppongo. L'idea generale è che è bene separare comportamento e struttura. Inoltre, un gestore di clic inline deve essere evalportato a "diventare" una vera funzione javascript. Ed è piuttosto vecchio stile, anche se questa è una discussione piuttosto traballante. Ah, beh, leggi qualcosa a riguardo @ quirksmode.org


Non voglio creare ancora una volta una guerra santa, sto cercando di trovare la verità: \
NiLL

2
  • gli eventi onclick vengono eseguiti nell'ambito globale e possono causare errori imprevisti.
  • L'aggiunta di eventi onclick a molti elementi DOM rallenterà le
    prestazioni e l'efficienza.

0

Altre due ragioni per non usare i gestori in linea:

Possono richiedere noiose citazioni che sfuggono alle quotazioni

Dato un arbitrario stringa, se si vuole essere in grado di costruire un gestore di linea che chiama una funzione con quella stringa, per la soluzione generale, si dovrà sfuggire alle attributi delimitatori (con l'entità HTML associato), e sarete deve uscire dal delimitatore usato per la stringa all'interno dell'attributo, come il seguente:

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

È incredibilmente brutto. Dall'esempio precedente, se non si sostituisce 's, si otterrebbe un errore di alert('foo'"bar')sintassi , poiché non è una sintassi valida. Se non sostituissi i messaggi di posta "elettronica, il browser lo interpreterebbe come una fine onclickdell'attributo (delimitato con "i messaggi precedenti), che sarebbe anche errato.

Se si usano abitualmente gestori in linea, si dovrebbe assicurarsi di ricordare di fare qualcosa di simile a quanto sopra (e farlo bene ) ogni volta , che è noioso e difficile da capire a colpo d'occhio. Meglio evitare completamente i gestori inline in modo che la stringa arbitraria possa essere utilizzata in una semplice chiusura:

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

Non è molto più bello?


La catena di portata di un gestore in linea è estremamente peculiare

Cosa pensi che registrerà il seguente codice?

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

Provalo, esegui lo snippet. Probabilmente non è quello che ti aspettavi. Perché produce quello che fa? Perché i gestori inline corrono all'interno di withblocchi. Il codice sopra è all'interno di tre with blocchi: uno per il document, uno per il <form>e uno per il <button>:

inserisci qui la descrizione dell'immagine

Poiché disabledè una proprietà del pulsante, il riferimento disabledall'interno del gestore inline si riferisce alla proprietà del pulsante, non alla disabledvariabile esterna . Questo è abbastanza intuitivo. withha molti problemi: può essere la fonte di bug confusi e rallenta significativamente il codice. Non è nemmeno permesso affatto in modalità rigorosa. Ma con i gestori in linea, lo sei costretto a eseguire il codice attraverso withs - e non solo attraverso uno with, ma attraverso più withs nidificati . È pazzesco.

withnon dovrebbe mai essere usato nel codice. Poiché i gestori inline implicitamente richiedono withinsieme a tutto il suo comportamento confuso, anche i gestori inline dovrebbero essere evitati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.