Como o Javascript embutido (em HTML) funciona?

129

Eu sei que isso é uma má prática. Não escreva códigos como este, se possível.

Obviamente, sempre nos encontraremos em situações em que um snippet inteligente de Javascript embutido pode solucionar um problema rapidamente.

Estou buscando esta consulta no interesse de entender completamente o que acontece (e as possíveis armadilhas) quando algo assim é escrito:

<a href="#" onclick="alert('Hi')">Click Me</a>

Até onde eu sei, isso é funcionalmente o mesmo que

<script type="text/javascript">
   $(function(){ // I use jQuery in this example
       document.getElementById('click_me').onclick = 
           function () { alert('Hi'); };
   });
</script>
<a href="#" id="click_me">Click Me</a>

Extrapolando disso, parece que a string atribuída ao atributo onclick é inserida em uma função anônima que é atribuída ao manipulador de cliques do elemento. Este é realmente o caso?

Porque estou começando a fazer coisas assim:

<a href="#" onclick="$(this).next().fadeIn(); return false;">Display my next sibling</a> <!-- Return false in handler so as not to scroll to top of page! --> 

O que funciona. Mas eu não sei o quanto isso é um hack. Parece suspeito porque não há nenhuma função aparente que está sendo retornada!

Você pode perguntar, por que está fazendo isso, Steve? JS embutido é uma prática ruim!

Bem, para ser sincero, estou cansado de editar três seções diferentes de código apenas para modificar uma seção de uma página, especialmente quando estou apenas criando um protótipo de algo para ver se funcionará. É muito mais fácil e, às vezes, até faz sentido que o código especificamente relacionado a esse elemento HTML seja definido dentro do elemento: Quando eu decido, 2 minutos depois, que essa foi uma ideia terrível, terrível, eu posso destruir a div inteira (ou qualquer outra coisa) ) e não tenho um monte de mistérios misteriosos de JS e CSS no resto da página, diminuindo a renderização. Isso é semelhante ao conceito de localidade de referência, mas, em vez de erros de cache, estamos observando bugs e inchaço do código.

Steven Lu
fonte
3
Você está correto, é uma função anônima.
22412 bhamlin
1
Você precisará conectar a consulta #click_meno evento DOMready ou colocar o script após o nó.
Bergi 15/05/12
1
Em um console, faça document.getElementById("click_me").onclick;. Ou alerte. Você verá que está em uma função.
D. Strout 15/05/12

Respostas:

96

Você está quase correto, mas não contabilizou o thisvalor fornecido ao código embutido.

<a href="#" onclick="alert(this)">Click Me</a>

é realmente mais perto de:

<a href="#" id="click_me">Click Me</a>
<script type="text/javascript">
document.getElementById('click_me').addEventListener("click", function(event) {
    (function(event) {
        alert(this);
    }).call(document.getElementById('click_me'), event);
});
</script>

Manipuladores de eventos embutidos definidos como thisiguais ao destino do evento. Você também pode usar a função anônima no script embutido

<a href="#" onclick="(function(){alert(this);})()">Click Me</a>
apsillers
fonte
7
O script deve vir após a tag, caso contrário, a tag não existe quando é executada - sugiro alterar sua resposta para refletir isso.
indefinido
como propagar o eventcom um inline onclick='myFunction(event)'?
oldboy
9

O que o navegador faz quando você tem

<a onclick="alert('Hi');" ... >

é definir o valor real de "onclick" para algo efetivamente como:

new Function("event", "alert('Hi');");

Ou seja, ele cria uma função que espera um parâmetro "evento". (Bem, o IE não; é mais uma função anônima simples).

Pontudo
fonte
2
Ah Para que eu possa realmente usar a variável eventpara recuperar o evento (e o elemento de origem a partir dele event.target), do meu snippet JS embutido! Legal.
Steven Lu
1
O IE realmente não passa o eventparâmetro, mas se você usar eventessa função anônima, o IE o entenderá como o objeto de evento real. Como a função anônima é definida como uma propriedade do windowobjeto no IE, ela será vista como window.event.
Rdleal # 15/12
8

Parece haver muitas práticas ruins sendo lançadas em torno dos Atributos do Manipulador de Eventos. A prática recomendada é não conhecer e usar os recursos disponíveis onde for mais apropriado. Os atributos do evento são padrões totalmente documentados pelo W3C e não há nada de ruim nas práticas. Não é diferente de colocar estilos em linha, que também é documentado pelo W3C e pode ser útil em alguns momentos. Se você o colocar em tags de script ou não, será interpretado da mesma maneira.

https://www.w3.org/TR/html5/webappapis.html#event-handler-idl-attributes

Daniel B
fonte
2
Sim, estou inclinado a concordar e até forneci "razões" razoáveis ​​pelas quais esse tipo específico de uso de código embutido pode ser justificado em determinadas situações. Mas a realidade é que, quando um aplicativo (aplicativo da Web ou de outra forma) passa a ter uma certa complexidade (e isso nem sempre leva muito tempo ou trabalho para ser alcançado), com pequenos trechos de código espalhados sobre o layout HTML provavelmente não ser arquiteturalmente do ponto de vista da manutenção. Mesmo começando a codificar JS diretamente dentro de um arquivo HTML, por exemplo, alguém está tentando a ladeira escorregadia da spaghettification.
Steven Lu
1
E esse é um argumento válido e válido contra o qual eu concordo. Normalmente, não adiciono scripts embutidos nem estilo para essa matéria. Mas as pessoas têm gostos diferentes. Muitos, por exemplo, gostam de adicionar tags de script e estilo na seção body, eu não aguento mais. Mas é válido e algumas pessoas gostam. Como costuras bad practice/spaghettificationpara alguns é good practice/structurepara outros.
Daniel B
Eu estive pesquisando github.com/styled-components/styled-components e, se você combinar isso com o react , por exemplo, agora temos tudo associado a um componente do seu aplicativo que realmente vive junto (espero que seja pacificamente) em um só lugar. E na verdade também não parece terrível. Parece progresso. Sob essa luz, o bloqueio de estilos e códigos inline no HTML não é o caminho certo (até agora parece o bloqueio de estilos e HTML no código JS).
Steven Lu
5

A melhor maneira de responder sua pergunta é vê- la em ação.

<a id="test" onclick="alert('test')"> test </a> 

Nos js

var test = document.getElementById('test');
console.log( test.onclick ); 

Como você vê no console, se você estiver usando o Chrome, ele imprime uma função anônima com o objeto de evento passado, embora seja um pouco diferente no IE.

function onclick(event) {
   alert('test')
}

Concordo com alguns de seus pontos de vista sobre manipuladores de eventos em linha. Sim, eles são fáceis de escrever, mas não concordo com o seu ponto de precisar alterar o código em vários locais; se você estruturar bem o seu código, não precisará fazer isso.

aziz punjani
fonte
Eu acho que algo como <button onclick="login()"> test login </button>é perfeitamente adequado para prototipagem. Você deseja testar algo que exija interação do usuário no momento e só o excluirá mais tarde. Por que escrever código extra em todo o lugar?
Dagg Nabbit
Não é um código extra, apenas uma função anônima extra. E não está em todo lugar, na verdade, tudo em um só lugar, nas tags de script.
Aziz punjani
Não tenho certeza se compartilhamos a mesma idéia de "estruturar bem seu código". Se você vincular a interface do usuário às suas "funções principais" no local em que suas funções principais realmente vivem, isso me parece um arranjo de acoplamento pior do que apenas vinculá-las na interface do usuário. Eu costumo manter toda a interface do usuário de ligação coisas separar o material do núcleo, e eu tenho a sensação de que o OP pode também ...
Dagg Nabbit
3

Parece suspeito porque não há nenhuma função aparente que está sendo retornada!

É uma função anônima que foi anexada ao evento click do objeto.

por que você está fazendo isso, Steve?

Por que diabos você está doi ... Ah, deixa pra lá, como você mencionou, é realmente uma prática ruim amplamente adotada :)

mattytommo
fonte
3

Tente isso no console:

var div = document.createElement('div');

div.setAttribute('onclick', 'alert(event)');

div.onclick

No Chrome, mostra o seguinte:

function onclick(event) {
  alert(event)
}

... e a namepropriedade não padrão de div.onclické"onclick" .

Portanto, se isso é ou não anônimo, depende da sua definição de "anônimo". Compare com algo como var foo = new Function(), onde foo.nameé uma string vazia, e foo.toString()produzirá algo como

function anonymous() {

}
Dagg Nabbit
fonte
Isso começou como um comentário na resposta de Pointy, mas realmente não funcionou como um comentário. A dele é realmente a melhor resposta aqui, eu acho.
Dagg Nabbit