Por que usar onClick () no HTML é uma prática ruim?

133

Ouvi muitas vezes que o uso de eventos JavaScript, como onClick()HTML, é uma prática ruim, porque não é bom para semântica. Gostaria de saber quais são as desvantagens e como corrigir o código a seguir?

<a href="#" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
NiLL
fonte
4
Não há nada errado com o onlickc, mas, ao fazer o href #, qualquer um que optar por desativar o javascript ficará preso e não poderá fazer nada. Para alguns sites, isso é uma coisa boa, mas, para simplesmente abrir uma janela, é completamente estúpido não fornecer um link "real".
Marc B
2
Definitivamente leia esse link. Ele tem muito pouco a ver com a semântica, e mais a ver com ... todas as coisas nessa página :-)
morewry
Tente abrir o link em uma nova aba, e você verá um exemplo de por que é errado ...
Sebastien C.
2
Isso deve ser um <button>, porque os links devem apontar para um recurso real. É mais semântico e é mais claro para os usuários de leitores de tela.
programmer5000

Respostas:

171

Você provavelmente está falando de Javascript discreto , que seria assim:

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

com a lógica em um arquivo javascript central parecido com isto:

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

As vantagens são

  • comportamento (Javascript) é separado da apresentação (HTML)
  • sem mistura de idiomas
  • você está usando uma estrutura javascript como o jQuery que pode lidar com a maioria dos problemas entre navegadores para você
  • Você pode adicionar comportamento a vários elementos HTML de uma só vez sem duplicação de código
Michael Borgwardt
fonte
30
Você listou algumas vantagens, mas e as desvantagens? Depurando fumaça azul?
Abelito 21/05
2
@abelito: Não tenho certeza se a depuração é uma desvantagem. Se alguma coisa é uma vantagem para a depuração, você pode percorrer o JavaScript em qualquer ferramenta de depuração do navegador. Não tenho certeza se você pode fazer isso tão facilmente se o código estiver em linha an onClick. Você também pode escrever testes de unidade, se desejar, contra seus scripts, o que também é muito difícil, eu diria que se o código estiver dentro de onClicke nenhuma lógica for separada. Pessoalmente, não tenho problema em depurar JavaScript discreto e os benefícios de gerenciar e testar o código JavaScript são muito grandes para não usá-lo.
Nope
45
@ François Wahl: a principal desvantagem é a descoberta: olhar para o HTML não informa quais retornos de chamada estão associados a ele, nem mesmo o que existe.
precisa
1
@ MichaelBorgwardt: Concordo plenamente com você que é uma desvantagem. Se o código é bem estruturado e organizado, não o vejo como um grande problema ao trabalhar em um projeto ou depurar outra base de código. Em geral, embora eu concorde com você, não considero a descoberta um bom motivo para escrever scripts em linha, oferecendo benefícios como a testabilidade do código e a capacidade de separar seu código de função / comportamento do DOM. Não estou dizendo que o código in-line é ruim e não funciona. Ele funciona e é absolutamente bom, dependendo se você está interessado em coisas como testabilidade ou não.
Nope
4
Outro ponto desta discussão, com os novatos - é o onclickmapeamento mais explícito dos relacionamentos causais entre a interação do elemento e o efeito (chamada de função), além de reduzir a carga cognitiva. Muitas lições colocam os alunos em uma série de addEventListeneridéias, dentro de uma sintaxe mais difícil. Além disso, estruturas recentes baseadas em componentes como Riot e React usam o onclickatributo e estão mudando a noção do que deve ser a separação. Transferir do HTML onclickpara um componente que suporte isso pode ser um processo de "etapa" mais eficaz para o aprendizado.
precisa saber é o seguinte
41

Se você estiver usando jQuery, então:

HTML:

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

JS:

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

Isso tem o benefício de continuar trabalhando sem JS ou se o usuário clicar no meio do link.

Isso também significa que eu poderia lidar com pop-ups genéricos reescrevendo novamente para:

HTML:

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

JS:

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

Isso permitiria adicionar um pop-up a qualquer link, apenas fornecendo a classe pop-up.

Essa idéia pode ser estendida ainda mais assim:

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

Agora posso usar o mesmo código para muitos pop-ups em todo o site, sem ter que escrever um monte de coisas onclick! Yay para reutilização!

Isso também significa que, se mais tarde eu decidir que os pop-ups são uma má prática (o que são!) E que eu quero substituí-los por uma janela modal no estilo lightbox, eu posso mudar:

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

para

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

e todos os meus pop-ups em todo o site agora estão funcionando de maneira totalmente diferente. Eu poderia até fazer a detecção de recursos para decidir o que fazer em um pop-up ou armazenar a preferência dos usuários para permitir ou não. Com o on-click on-line, isso exige um grande esforço de copiar e colar.

Rich Bradshaw
fonte
4
Funciona sem JavaScript ?? É jQuery. Ele precisa do Javascript habilitado no navegador para funcionar, certo?
Thomas Shields
2
Sim, mas o link pode redirecionar para uma página que executa a ação sem JavaScript neste caso.
ThiefMaster
12
O link funciona sem JavaScript. Veja como o link tem um atributo href normal. Se o usuário não tiver JavaScript, o link ainda será um link e o usuário ainda poderá acessar o conteúdo.
morewry
3
@Thomas Shields: Não, ele quer dizer que se você não tem o Javascript, o link ainda vai levá-lo para / map / ...
Robert
2
Ah, rico! Todos nós amamos você por esta solução mais bacana!
Nirmal
21

Com aplicativos JavaScript muito grandes, os programadores estão usando mais encapsulamento de código para evitar poluir o escopo global. E para disponibilizar uma função para a ação onClick em um elemento HTML, ela deve estar no escopo global.

Você pode ter visto arquivos JS parecidos com este ...

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

Essas são Expressões de Função Invocadas Imediatamente (IIFEs) e qualquer função declarada nelas só existirá dentro de seu escopo interno.

Se você declarar function doSomething(){}dentro de um IIFE, em seguida, faça doSomething()a ação onClick de um elemento em sua página HTML, você receberá um erro.

Se, por outro lado, você criar um eventListener para esse elemento dentro do IIFE e chamar doSomething()quando o ouvinte detectar um evento de clique, você será bom porque o ouvinte e doSomething()compartilhará o escopo do IIFE.

Para pequenos aplicativos da Web com uma quantidade mínima de código, isso não importa. Mas se você deseja escrever grandes bases de códigos que podem ser mantidas, onclick=""é um hábito que você deve evitar.

LetMyPeopleCode
fonte
1
se você aspirar a escrever grande, codebases sustentáveis usar estruturas JS como Angular, Vue, Reagir ... que recomendo para manipuladores de eventos ligam dentro de seus modelos HTML
Kamil Kiełczewski
20

Não é bom por vários motivos:

  • mistura código e marcação
  • código escrito desta maneira passa eval
  • e é executado no escopo global

A coisa mais simples seria adicionar um nameatributo ao seu <a>elemento, então você poderia fazer:

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

embora a melhor prática moderna seja usar idum nome em vez de um nome e usá-lo em addEventListener()vez de usar, onclickpois isso permite vincular várias funções a um único evento.

Alnitak
fonte
Mesmo sugerindo dessa maneira, o usuário pode ter muitos problemas com colisões de manipuladores de eventos.
pré
3
@ ação exatamente como um manipulador de eventos em linha, portanto, por que minha resposta diz que é melhor usar addEventListener()e por quê.
Alnitak
2
Alguém pode explicar isso mais? "código escrito dessa maneira passa por eval" ... não encontro nada na web sobre isso. Você está dizendo que há alguma desvantagem de desempenho ou segurança no uso de Javascript embutido?
MarsAndBack
12

Há algumas razões:

  1. Acho que ajuda na manutenção da separação da marcação, ou seja, os scripts HTML e do lado do cliente. Por exemplo, o jQuery facilita a adição de manipuladores de eventos programaticamente.

  2. O exemplo que você deu seria quebrado em qualquer agente de usuário que não suporte javascript ou que tenha o javascript desativado. O conceito de aprimoramento progressivo incentivaria um hiperlink simples /map/para agentes de usuários sem javascript e, em seguida, adicionaria um manipulador de cliques de forma significativa para agentes de usuários que suportam javascript.

Por exemplo:

Marcação:

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

Javascript:

$(document).ready(function(){

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

})
Graham
fonte
2
Obrigado por me lembrar do aprimoramento progressivo e, portanto, isso ainda se encaixa nesse paradigma:<a href="https://stackoverflow.com/map/" onclick="popup('/map/', 300, 300, 'map'); return false;">link</a>
Daniel Sokolowski
10

Revisão

A abordagem discreta do JavaScript foi boa no PAST - especialmente a vinculação do manipulador de eventos no HTML foi considerada uma má prática (principalmente porque o onclick events run in the global scope and may cause unexpected errorque foi mencionado pelo YiddishNinja )

Contudo...

Atualmente, parece que essa abordagem está um pouco desatualizada e precisa de alguma atualização. Se alguém quiser ser desenvolvedor profissional de front-end e escrever aplicativos grandes e complicados, precisará usar estruturas como Angular, Vue.js, etc ... No entanto, essas estruturas geralmente usam (ou permitem usar) modelos HTML em que manipuladores de eventos são vinculados no código html-template diretamente e isso é muito útil, claro e eficaz - por exemplo, no modelo angular geralmente as pessoas escrevem:

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

Em js / html bruto, o equivalente a isso será

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

A diferença é que, no onclickevento js bruto , é executado no escopo global - mas as estruturas fornecem encapsulamento.

Então onde está o problema?

O problema é quando o programador iniciante que sempre ouviu falar que o html-onclick é ruim e que sempre usa btn.addEventListener("onclick", ... )quer usar alguma estrutura com modelos ( addEventListenertambém tem desvantagens - se atualizarmos o DOM de maneira dinâmica usando innerHTML=(o que é bastante rápido )), perderemos eventos manipuladores se ligam dessa maneira). Então, ele enfrentará algo como maus hábitos ou abordagem incorreta do uso da estrutura - e ele usará a estrutura de maneira muito ruim - porque ele se concentrará principalmente na parte js e não na parte do modelo (e produzirá incertos e difíceis de manter código). Para mudar esses hábitos, ele perderá muito tempo (e provavelmente precisará de um pouco de sorte e professor).

Então, na minha opinião, com base na experiência com meus alunos, seria melhor para eles se eles usassem html-handlers-bind no início. Como eu digo, é verdade que os manipuladores são chamados no escopo global, mas nesse estágio os alunos geralmente criam aplicativos pequenos e fáceis de controlar. Para escrever aplicativos maiores, eles escolhem algumas estruturas.

Então o que fazer?

Podemos ATUALIZAR a abordagem JavaScript Discreta e permitir manipuladores de eventos de ligação (eventualmente com parâmetros simples) em html (mas apenas manipulador de ligação - não colocar lógica no onclick como no quesiton OP). Portanto, na minha opinião, em js / html bruto, isso deve ser permitido

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

ou

Mas exemplos abaixo NÃO devem ser permitidos

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

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

A realidade muda, nosso ponto de vista também deve

Kamil Kiełczewski
fonte
oi Kamil, sou iniciante em JavaScript e também enfrentei confusão sobre o uso do onclick, ou seja, por favor, você pode explicar as desvantagens do onclick da seguinte forma: 1. Você pode ter apenas um evento em linha atribuído. 2.Os eventos em linha são armazenados como uma propriedade do elemento DOM e, como todas as propriedades do objeto, podem ser substituídos. 3.Este evento continuará sendo acionado, mesmo se você excluir a propriedade onclick.
Mirzhal 19/05/19
1
Anúncio 1. Sim, apenas um, no entanto, minha experiência (com angular) mostra que isso é suficiente na maioria das situações - no entanto, se não, basta usar addEventListener. Anúncio 2. Sim, eles podem ser substituídos (no entanto, geralmente ninguém o faz) - onde está o problema? Anúncio 3. O manipulador não será acionado após excluir o
atributo
8

É um novo paradigma chamado " JavaScript Discreto ". O "padrão da web" atual diz para separar funcionalidade e apresentação.

Não é realmente uma "má prática", mas a maioria dos novos padrões deseja que você use ouvintes de eventos em vez de incorporar o JavaScript.

Além disso, isso pode ser apenas uma coisa pessoal, mas acho que é muito mais fácil ler quando você usa ouvintes de eventos, especialmente se você tiver mais de uma instrução JavaScript que deseja executar.

Foguete Hazmat
fonte
2
A página da Wikipedia sobre o assunto tem mais de 4 anos. Não é mais um novo paradigma. :)
Quentin
@ David: Pode não ser "novo", mas ainda há pessoas que não o usam, por isso é "novo" para eles.
Rocket Hazmat
6

Sua pergunta irá desencadear uma discussão, suponho. A idéia geral é que é bom separar comportamento e estrutura. Além disso, um manipulador de cliques inline deve ser evallevado a 'tornar-se' uma função javascript real. E é bem antiquado, apesar de ser um argumento bastante instável. Ah, bem, leia um pouco sobre isso @ quirksmode.org

KooiInc
fonte
Não quero criar mais uma guerra santa, estou tentando descobrir a verdade: \
NiLL
2
  • Os eventos onclick são executados no escopo global e podem causar erros inesperados.
  • A adição de eventos onclick a muitos elementos DOM diminuirá o
    desempenho e a eficiência.
Minghuan
fonte
0

Mais dois motivos para não usar manipuladores embutidos:

Eles podem exigir problemas de escape de cotações tediosas

Dada uma sequência arbitrária , se você quiser construir um manipulador em linha que chame uma função com essa sequência, para a solução geral, será necessário escapar dos delimitadores de atributo (com a entidade HTML associada) e você precisa escapar do delimitador usado para a sequência dentro do atributo, como o seguinte:

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

Isso é incrivelmente feio. No exemplo acima, se você não substituísse os ', resultaria em um SyntaxError, porque alert('foo'"bar')não é uma sintaxe válida. Se você não substituísse "s, o navegador o interpretaria como um fim para o onclickatributo (delimitado com "s acima), que também estaria incorreto.

Se alguém usa habitualmente manipuladores em linha, um teria que ter certeza de lembrar fazer algo semelhante ao anterior (e fazê-lo direito ) de cada vez , o que é tedioso e difícil de entender à primeira vista. Melhor evitar manipuladores inline inteiramente para que a cadeia arbitrária possa ser usada em um fechamento simples:

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

Isso não é muito melhor?


A cadeia de escopo de um manipulador em linha é extremamente peculiar

O que você acha que o código a seguir registrará?

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

Experimente, execute o snippet. Provavelmente não é o que você estava esperando. Por que produz o que faz? Porque os manipuladores em linha executam dentro de withblocos. O código acima está dentro de três with blocos: um para o document, um para o <form>e um para o <button>:

insira a descrição da imagem aqui

Como disabledé uma propriedade do botão, a referência disableddentro do manipulador embutido refere-se à propriedade do botão, não à disabledvariável externa . Isso é bastante contra-intuitivo. withtem muitos problemas: pode ser a fonte de erros confusos e diminui significativamente o código. Nem sequer é permitido em modo estrito. Mas com manipuladores inline, você é forçado a executar o código por meio de withs - e não apenas por um with, mas por vários withs aninhados . É louco.

withnunca deve ser usado no código. Como os manipuladores inline exigem implicitamente, withjuntamente com todo o seu comportamento confuso, os manipuladores inline também devem ser evitados.

CertainPerformance
fonte