以下是一些可能有助于人们更好地理解和解决问题的示例。
TL; 博士
on*
事件处理程序(例如onclick
按钮元素上的属性):返回 false 以取消事件
addEventListener
是一个不同的 API,返回值(例如false
)被忽略:使用event.preventDefault()
。
onclick="<somejs>"
有它自己的潜在混淆,因为它<somejs>
被包裹在一个 onclick 函数的主体中。
getEventListeners
如果您的事件处理程序没有按预期运行,请使用浏览器 devtools API 查看您的事件侦听器如何进行故障排除。
例子
此示例特定于click
带有<a>
链接的事件...但可以推广到大多数事件类型。
我们有一个带有类的锚点(链接)js-some-link-hook
,我们希望打开一个模态并防止任何页面导航发生。
以下示例在 MacOS Mojave 上的 google chrome (71) 中运行。
一个主要的陷阱是假设这onclick=functionName
与使用相同的行为addEventListener
假设我们有一个锚标记(链接),我们希望在启用 javascript 时使用 javascript 处理它。我们不希望浏览器在点击时跟随链接(“防止默认”行为)。
<a href="https://www.example.com/url/to/go/to/when/no/javascript"
class="js-some-link-hook">click me!</a>
点击属性
function eventHandler (event) {
// want to prevent link navigation
alert('eventHandler ran');
return false;
}
function addEventListenerToElement () {
var link = document.querySelector('.js-some-link-hook');
link.setAttribute('onclick', eventHandler);
}
addEventListenerToElement();
然后在浏览器的 devtools 控制台中运行:
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener); // to avoid truncation of output
......你会看到:
function onclick(event) {
function eventHandler (event) {alert('eventHandler ran'); return false;}
}
这根本行不通。使用时,onclick=
您的处理程序函数被包装在另一个函数中。
你可以看到我的函数定义被包含但没有被调用,因为我没有调用它就指定了函数引用。也就是说,我们必须onclick="functionName()"
不能onclick="functionName"
确保functionName
运行被点击的元素时。
此外,您可以看到,即使我的函数被调用并且我的函数返回了 false……该onclick
函数也不会返回那个 false 值……这是“取消”事件所必需的。
为了解决这个问题,我们可以设置onclick
为return myHandlerFunc();
which 确保从onclick
返回返回值 (false) myHandlerFunc
。
您也可以删除return false;
frommyHandlerFunc
并将其更改onclick
为 be ,myHandlerFunc(); return false;
但这没有意义,因为您可能希望将逻辑保持在处理程序函数中。
请注意,onclick
使用 javascript进行设置时,当您onclick
直接在 html 中而不是使用 javascript进行设置时(如我的示例),onclick
属性值为 string 类型并且一切正常。如果你onclick
使用javascript设置,你需要注意类型。如果您说element.setAttribute('onclick', myHandlerFunc())
myHandlerFunc
将立即运行并且结果将存储在属性中...而不是在每次单击时运行。相反,您应该确保将属性值设置为字符串。 element.setAttribute('onclick', 'return myHandlerFunc();')
现在我们看到了它是如何工作的,我们可以修改代码来做我们想做的任何事情。用于说明目的的奇怪示例(不要使用此代码):
function eventHandler (e) {
// want to prevent link navigation
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();
您会看到我们已将 eventHandler 函数定义包装到字符串中。具体来说:一个前面带有return语句的自执行函数。
再次在 chrome devtools 控制台中:
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener); // to avoid truncation of output
...显示:
function onclick(event) {
return (function eventHandler (e) {
// want to prevent link navigation
alert('eventHandler ran');
console.log(e);
return false;
})(event);
}
......所以是的,这应该有效。果然,如果我们点击链接,我们会收到警报并解除警报,页面不会导航到任何地方或刷新。
关于onclick
...的更多注意事项如果您想event
在事件发生时接收和使用该参数,您应该注意它被命名为“事件”。您可以使用名称event
(可通过父onclick
函数作用域获得)在处理程序中访问它。或者您可以构建您的处理程序以event
作为参数(更适合测试)...例如,onclick="return myEventHandler(event);"
或者如您在前面的示例中看到的那样。
添加事件监听器
function eventHandler (ev) {
// want to prevent link navigation
alert('eventHandler ran');
console.log(ev);
return false;
}
function addEventListenerToElement () {
var link = document.querySelector('.js-some-link-hook');
link.addEventListener('click', eventHandler, false);
}
addEventListenerToElement();
浏览器开发工具:
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener); // to avoid truncation of output
结果:
function eventHandler (ev) {
// want to prevent link navigation
alert('eventHandler ran');
console.log(ev);
return false;
}
所以你已经可以看出区别了。随着addEventListener
我们不是包裹在一个onclick
功能。我们的处理程序event
直接接收参数(因此我们可以随意调用它)。此外,我们return false
在这里处于“顶级”,我们不必担心添加额外的 return 语句,例如 with onclick
。
所以看起来它应该工作。单击链接,我们会收到警报。解除警报,页面导航/刷新。 即事件没有通过返回 false 取消。
如果我们查找规范(请参阅底部的资源),我们会发现 addEventListener 的回调/处理程序函数不支持返回类型。我们可以返回任何我们想要的东西,但由于它不是浏览器 API/接口的一部分,所以它没有任何效果。
解决方案:使用event.preventDefault()
代替return false;
...
function eventHandler (ev) {
// want to prevent link navigation
ev.preventDefault();
alert('eventHandler ran');
}
function addEventListenerToElement () {
var link = document.querySelector('.js-some-link-hook');
link.addEventListener('click', eventHandler, false);
}
addEventListenerToElement();
浏览器开发工具...
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener); // to avoid truncation of output
给...
function eventHandler (ev) {
// want to prevent link navigation
ev.preventDefault();
alert('eventHandler ran');
}
...正如预期的那样。
再次测试:
- 点击链接。
- 得到警报。
- 解除警报。
- 没有页面导航或刷新发生......这就是我们想要的。
所以addEventListener
使用event.preventDefault()
as 返回 false 什么都不做。
资源
html5 规范 ( https://www.w3.org/TR/html5/webappapis.html#events ) 混淆了事情,因为他们在他们的示例中同时使用onclick
和addEventListener
并且他们说如下:
一个事件处理器H和一个Event对象E的事件处理器处理算法如下:
...
- 处理返回值如下:
...
如果返回值为 Web IDL boolean false 值,则取消该事件。
因此,它似乎暗示,return false
取消两个事件addEventListener
和onclick
。
但是,如果您查看他们的链接定义,event-handler
您会看到:
事件处理程序有一个名称,该名称始终以“on”开头,后跟要处理的事件的名称。
...
事件处理程序以两种方式之一公开。
第一种方式,对所有事件处理程序通用,是作为事件处理程序 IDL 属性。
第二种方式是作为事件处理程序的内容属性。HTML 元素上的事件处理程序和 Window 对象上的一些事件处理程序以这种方式公开。
https://www.w3.org/TR/html5/webappapis.html#event-handler
因此,return false
取消事件似乎确实仅适用于onclick
(或通常on*
)事件处理程序,而不适用于通过addEventListener
具有不同 API注册的事件处理程序。
由于addEventListener
html5 规范未涵盖 API(仅包含on*
事件处理程序)……如果他们坚持使用on*
示例中的样式事件处理程序,就不会那么混乱。