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, onclickatributo en un elemento de botón): devuelve falso para cancelar el evento
addEventListeneres 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
getEventListenersAPI 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 clickevento 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-hookque 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=functionNamees 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 functionNamese 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 onclickfunción no devolvería ese valor falso ... que se requiere para 'cancelar' el evento.
Para solucionar esto, podemos establecer onclickque sea lo return myHandlerFunc();que asegura que onclickdevuelve el valor de retorno (falso) de myHandlerFunc.
También puede eliminar el return false;de myHandlerFuncy cambiar el onclickpor 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 onclickcon javascript , cuando configura onclickdirectamente en html en lugar de hacerlo con javascript (como mis ejemplos), el onclickvalor del atributo es tipo cadena y todo funciona. Si está configurando onclickusando javascript, debe prestar atención al tipo. Si dice element.setAttribute('onclick', myHandlerFunc()) myHandlerFuncque 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 onclickalcance de la función principal ). O puede construir su controlador para tomarlo eventcomo 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 addEventListenerno estamos envueltos en una onclickfunción. Nuestro manejador recibe elevent parámetro directamente (y así podemos llamarlo como queramos). También nuestro return falseestá 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, addEventListenerusar 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 onclicky addEventListeneren 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 falsecancela el evento para ambos addEventListenery 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 falsecancelació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 addEventListenercuales tiene una API diferente.
Dado que la addEventListenerAPI 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.