Aquí hay algunos ejemplos que pueden ayudar a las personas a comprender y solucionar mejor sus problemas.
TL; DR
on*
controladores de eventos (por ejemplo, onclick
atributo en un elemento de botón): devuelve falso para cancelar el evento
addEventListener
es una API diferente, los valores de retorno (p false
. ej. ) se ignoran: use event.preventDefault()
.
onclick="<somejs>"
tiene su propia confusión potencial porque <somejs>
está envuelto en el cuerpo de una función onclick.
- Utilice la
getEventListeners
API de devtools del navegador para ver cómo se ve su detector de eventos para solucionar problemas si su controlador de eventos no se comporta como se esperaba.
Ejemplo
Este ejemplo es específico del click
evento con un <a>
enlace ... pero se puede generalizar para la mayoría de los tipos de eventos.
Tenemos un ancla (enlace) con la clase js-some-link-hook
que queremos abrir un modal y evitar que suceda la navegación de la página.
Los siguientes ejemplos se ejecutaron en google chrome (71) en MacOS Mojave.
Un error importante es asumir que onclick=functionName
es el mismo comportamiento que usaraddEventListener
Digamos que tenemos una etiqueta de anclaje (enlace) que queremos manejar con javascript cuando javascript está habilitado. No queremos que el navegador siga el enlace cuando se hace clic en él (comportamiento "evitar predeterminado").
<a href="https://www.example.com/url/to/go/to/when/no/javascript"
class="js-some-link-hook">click me!</a>
atributo onclick
function eventHandler (event) {
alert('eventHandler ran');
return false;
}
function addEventListenerToElement () {
var link = document.querySelector('.js-some-link-hook');
link.setAttribute('onclick', eventHandler);
}
addEventListenerToElement();
Luego, ejecútelo en la consola de devtools del navegador:
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener);
... y ves:
function onclick(event) {
function eventHandler (event) {alert('eventHandler ran'); return false;}
}
Esto no funciona en absoluto. Cuando se utiliza onclick=
su función de controlador se envuelve en otra función.
Puede ver que la definición de mi función está incluida pero no se llama porque especifiqué la referencia de la función sin invocarla. es decir, que necesitamos onclick="functionName()"
no onclick="functionName"
para asegurarse de que functionName
se ejecuta cuando se hace clic en el elemento.
Además, puede ver que incluso si se llamó a mi función y mi función devolvió falso ... la onclick
función no devolvería ese valor falso ... que se requiere para 'cancelar' el evento.
Para solucionar esto, podemos establecer onclick
que sea lo return myHandlerFunc();
que asegura que onclick
devuelve el valor de retorno (falso) de myHandlerFunc
.
También puede eliminar el return false;
de myHandlerFunc
y cambiar el onclick
por ser, myHandlerFunc(); return false;
pero esto tiene menos sentido, ya que probablemente desee mantener la lógica junta en su función de controlador.
Tenga en cuenta que cuando configura onclick
con javascript , cuando configura onclick
directamente en html en lugar de hacerlo con javascript (como mis ejemplos), el onclick
valor del atributo es tipo cadena y todo funciona. Si está configurando onclick
usando javascript, debe prestar atención al tipo. Si dice element.setAttribute('onclick', myHandlerFunc())
myHandlerFunc
que se ejecutará ahora mismo y el resultado se almacenará en el atributo ... en lugar de ejecutarse en cada clic. En su lugar, debe asegurarse de que el valor del atributo esté establecido como una cadena. element.setAttribute('onclick', 'return myHandlerFunc();')
Ahora que vemos cómo funciona, podemos modificar el código para hacer lo que queramos. Ejemplo extraño con fines ilustrativos (no use este código):
function eventHandler (e) {
alert('eventHandler ran');
console.log(e);
return false;
}
function addEventListenerToElement () {
var link = document.querySelector('.js-some-link-hook');
link.setAttribute('onclick', 'return ('+eventHandler+')(event);');
}
addEventListenerToElement();
Verá que hemos envuelto nuestra definición de función eventHandler en una cadena. Específicamente: una función autoejecutable con una declaración de retorno al frente.
De nuevo en la consola de Chrome devtools:
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener);
... muestra:
function onclick(event) {
return (function eventHandler (e) {
alert('eventHandler ran');
console.log(e);
return false;
})(event);
}
... así que sí, eso debería funcionar. Efectivamente, si hacemos clic en el enlace, recibimos la alerta y, al descartar la alerta, la página no navega a ninguna parte ni se actualiza.
Una nota más sobre onclick
... Si desea recibir y utilizar elevent
parámetro en el momento del evento, debe tener en cuenta que se llama "evento". Puede acceder a esto dentro de su controlador usando el nombre event
(disponible a través del onclick
alcance de la función principal ). O puede construir su controlador para tomarlo event
como parámetro (mejor para pruebas) ... por ejemplo, onclick="return myEventHandler(event);"
o como ve en el ejemplo anterior.
addEventListener
function eventHandler (ev) {
alert('eventHandler ran');
console.log(ev);
return false;
}
function addEventListenerToElement () {
var link = document.querySelector('.js-some-link-hook');
link.addEventListener('click', eventHandler, false);
}
addEventListenerToElement();
devtools del navegador:
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener);
resultado:
function eventHandler (ev) {
alert('eventHandler ran');
console.log(ev);
return false;
}
Entonces ya puedes ver la diferencia. Con addEventListener
no estamos envueltos en una onclick
función. Nuestro manejador recibe elevent
parámetro directamente (y así podemos llamarlo como queramos). También nuestro return false
está en el "nivel superior" aquí y no tenemos que preocuparnos por agregar una declaración de retorno adicional como con onclick
.
Entonces parece que debería funcionar. Al hacer clic en el enlace obtenemos la alerta. Descarte la alerta y la página navega / se actualiza. es decir, el evento NO se canceló devolviendo falso.
Si buscamos la especificación (ver recursos en la parte inferior), vemos que nuestra función de devolución de llamada / controlador para addEventListener no admite un tipo de retorno. Podemos devolver lo que queramos, pero como no forma parte de la API / interfaz del navegador, no tiene ningún efecto.
Solución: usar en event.preventDefault()
lugar de return false;
...
function eventHandler (ev) {
ev.preventDefault();
alert('eventHandler ran');
}
function addEventListenerToElement () {
var link = document.querySelector('.js-some-link-hook');
link.addEventListener('click', eventHandler, false);
}
addEventListenerToElement();
devtools del navegador ...
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener);
da...
function eventHandler (ev) {
ev.preventDefault();
alert('eventHandler ran');
}
...como se esperaba.
Probando de nuevo:
- Haga clic en el enlace.
- Recibe la alerta.
- Descartar alerta.
- No se realiza ninguna navegación o actualización de la página ... que es lo que queremos.
Entonces, addEventListener
usar event.preventDefault()
como devolver falso no hace nada.
Recursos
La especificación html5 ( https://www.w3.org/TR/html5/webappapis.html#events ) confunde las cosas porque usan ambos onclick
y addEventListener
en sus ejemplos y dicen lo siguiente:
El algoritmo de procesamiento del controlador de eventos para un controlador de eventos H y un objeto de evento E es el siguiente:
...
- Procese el valor de retorno de la siguiente manera:
...
Si el valor de retorno es un valor falso booleano de Web IDL, cancele el evento.
Entonces parece implicar que return false
cancela el evento para ambos addEventListener
y onclick
.
Pero, si miras su definición vinculada event-handler
, ves:
Un controlador de eventos tiene un nombre, que siempre comienza con "on" y va seguido del nombre del evento al que está destinado.
...
Los controladores de eventos se exponen de dos formas.
La primera forma, común a todos los controladores de eventos, es como un atributo IDL del controlador de eventos.
La segunda forma es como un atributo de contenido del controlador de eventos. Los controladores de eventos en elementos HTML y algunos de los controladores de eventos en objetos de ventana se exponen de esta manera.
https://www.w3.org/TR/html5/webappapis.html#event-handler
Por lo tanto, parece que la return false
cancelación del evento solo se aplica a los controladores de eventos onclick
(o en general on*
) y no a los controladores de eventos registrados a través de los addEventListener
cuales tiene una API diferente.
Dado que la addEventListener
API no está cubierta por la especificación html5 (solo los on*
controladores de eventos) ... sería menos confuso si se apegaran a los on*
controladores de eventos de estilo en sus ejemplos.