Como passar um evento como argumento para um manipulador de eventos embutido em JavaScript?

103
// this e works
document.getElementById("p").oncontextmenu = function(e) {
    e = e || window.event;
    var target = e.target || e.srcElement;
    console.log(target);
};

// this e is undefined
function doSomething(e) {
    e = e || window.event;
    var target = e.target || e.srcElement;
    console.log(target);
}
<p id="p" onclick="doSomething(e)">
    <a href="#">foo</a>
    <span>bar</span>
</p>

Algumas perguntas semelhantes foram feitas.

Mas em meu código, estou tentando obter elementos filhos que foram clicados, como aouspan .

Então, qual é a maneira correta de passar eventcomo um argumento para o manipulador de eventos ou como obter o evento dentro do manipulador sem passar um argumento?

editar

Estou ciente addEventListenere jQueryforneça uma solução para passar o evento ao inlinemanipulador de eventos.

user1643156
fonte
5
Existe um bom motivo para usar manipuladores de eventos embutidos em vez de mudar para addEventListener? en.wikipedia.org/wiki/Unobtrusive_JavaScript
Xotic750
2
@ Xotic750 Para mim, sim, não preciso me preocupar em vinculá-los manualmente sempre que carregar ou recarregar html dinamicamente por meio de ajax, por exemplo.
Wadih M.

Respostas:

166

para passar o eventobjeto:

<p id="p" onclick="doSomething(event)">

para obter o filho clicado element(deve ser usado com o eventparâmetro:

function doSomething(e) {
    e = e || window.event;
    var target = e.target || e.srcElement;
    console.log(target);
}

para passar o elementpróprio (DOMElement):

<p id="p" onclick="doThing(this)">

veja o exemplo ao vivo no jsFiddle

Akram Berkawy
fonte
11
(E para quem está se perguntando: Sim, isso funciona no Chrome, Firefox, etc., embora alguns [Firefox, por exemplo] não tenham um eventobjeto global . É porque o contexto no qual o manipulador DOM0 é chamado tem um eventobjeto , mesmo que [em alguns navegadores] não seja global.)
TJ Crowder
thisrefere-se a p, mas estou tentando obter aouspan
user1643156
3
Passar em ´evento´ é a única maneira que conheço, desde que seja compatível. Analisar ´isto´ é inútil nesta situação
Xotic750
5
@ user1643156 Isso é muito importante. o parâmetro deve ser nomeado exatamente como "evento"
The-null-Pointer-
1
A resposta de Wadih M. é muito melhor do que isso. Este é enganoso (como outros semelhantes a ele), fazendo as pessoas acreditarem que o parâmetro "(evento)" deve ser colocado na chamada de função, quando, na verdade, JavaScript já tem o parâmetro "evento" e está prontamente disponível dentro da função chamada, então não há necessidade de declará-lo (demorei dias para perceber que não fazia diferença mudar o nome da variável, porque ela não estava realmente sendo passada). Além disso, a explicação dada ali descarta qualquer dúvida sobre por que JS funciona assim (talvez não devesse, mas não cabe a mim julgar ...)
Cyberknight
15

Como os eventos inline são executados como funções, você pode simplesmente usar argumentos .

<p id="p" onclick="doSomething.apply(this, arguments)">

e

function doSomething(e) {
  if (!e) e = window.event;
  // 'e' is the event.
  // 'this' is the P element
}

O 'evento' mencionado na resposta aceita é, na verdade, o nome do argumento passado para a função. Não tem nada a ver com o evento global.

Semra
fonte
Boa dica. Quando as pessoas começarão a adotar componentes da web call()e apply()se provarão essenciais na emulação de recursos de vinculação de dados disponíveis em estruturas js convencionais. Um truque extra é fazer algo semelhante a Object.assign(this.querySelector('my-btn'), this)dentro de um componente da web e voila, vinculação de dados. Você pode acessar qualquer método da classe do componente da web pai a partir dos eventos embutidos onclick="toggleBtnActive.call(this, true)".
Adrian Moisa
8

Você não precisa passar this, já existe o eventobjeto passado por padrão automaticamente, que contém event.targeto objeto de onde está vindo. Você pode aliviar sua sintaxe:

Este:

<p onclick="doSomething()">

Irá funcionar com isto:

function doSomething(){
  console.log(event);
  console.log(event.target);
}

Você não precisa instanciar o eventobjeto, ele já está lá. Experimente. E event.targetconterá todo o objeto que o chama, ao qual você se referia como "this" antes.

Agora, se você acionar doSomething () dinamicamente de algum lugar em seu código, notará que eventé indefinido. Isso ocorre porque ele não foi acionado a partir de um evento de clique. Portanto, se você ainda deseja acionar artificialmente o evento, basta usar dispatchEvent:

document.getElementById('element').dispatchEvent(new CustomEvent("click", {'bubbles': true}));

Então doSomething()vai ver evente event.targetcomo de costume!

Não há necessidade de passar por thistodos os lugares, e você pode manter suas assinaturas de funções livres de informações de fiação e simplificar as coisas.

Wadih M.
fonte
Pelo menos no IE11, isso não é verdade. Não há argumentos padrão.
cskwg
1
@cskwg Também funciona no IE11. Funciona em todos os principais navegadores, pois isso é necessário para compatibilidade com versões anteriores. Como mencionei na resposta, somente se a função javascript for disparada de um evento, que é o único cenário em que existe um evento, haverá um objeto já chamado de "evento" que você pode acessar prontamente dentro de sua função sem precisar passe fiação extra para seu protótipo de função. Acabei de fazer um teste na minha máquina com o IE11 para um evento onclick e a variável 'event' continha um "objeto PointerEvent".
Wadih M.