这里有一些例子,可以帮助人们更好地理解和解决他们的问题。
TL; DR
on*
事件处理程序(例如onclick
,按钮元素上的属性):返回false以取消事件
addEventListener
是不同的API,返回值(例如false
)将被忽略:use event.preventDefault()
。
onclick="<somejs>"
有其自身的潜在困惑,因为<somejs>
它包装在onclick函数的主体中。
- 使用浏览器devtools
getEventListeners
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>
onclick属性
function eventHandler (event) {
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);
...您会看到:
function onclick(event) {
function eventHandler (event) {alert('eventHandler ran'); return false;}
}
这根本不起作用。使用onclick=
处理程序函数时,它会包装在另一个函数中。
您可以看到包含但未调用我的函数定义,因为我没有调用它就指定了函数引用。也就是说,我们必须onclick="functionName()"
不能onclick="functionName"
确保functionName
运行被点击的元素时。
此外,您可以看到即使调用了我的函数并且我的函数返回了false……该onclick
函数也不会返回该false值……这是“取消”事件所必需的。
为了解决这个问题,我们可以设置onclick
为,以return myHandlerFunc();
确保从onclick
返回返回值(false)myHandlerFunc
。
您也可以return false;
从中删除frommyHandlerFunc
并将其更改onclick
为be,myHandlerFunc(); return false;
但这意义不大,因为您可能希望将逻辑保留在处理程序函数中。
请注意,onclick
使用javascript进行设置时,如果onclick
直接在html中而不是在javascript中进行设置(例如我的示例),则onclick
属性值为字符串类型,并且一切正常。如果onclick
使用javascript进行设置,则需要注意输入。如果您说element.setAttribute('onclick', myHandlerFunc())
myHandlerFunc
将立即运行,并且结果将存储在属性中,而不是在每次单击时运行。相反,您应确保将属性值设置为字符串。 element.setAttribute('onclick', 'return myHandlerFunc();')
现在我们了解了它是如何工作的,我们可以修改代码以执行我们想要的任何事情。出于说明目的的奇怪示例(请勿使用此代码):
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();
您会看到我们已将eventHandler函数定义包装到字符串中。特别是:一个自执行函数,在前面有一个return语句。
再次在chrome devtools控制台中:
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener);
...显示:
function onclick(event) {
return (function eventHandler (e) {
alert('eventHandler ran');
console.log(e);
return false;
})(event);
}
...是的,那应该起作用。果然,如果我们单击链接,我们将收到警报,并关闭警报,页面将无法导航或刷新。
关于onclick
...的另一条注释。如果您想event
在事件发生时接收和使用该参数,则应注意它被命名为“事件”。您可以使用名称event
(可通过父onclick
函数作用域使用)在处理程序中访问它。或者,您可以构造您的处理程序以event
作为参数(更好地进行测试)...例如onclick="return myEventHandler(event);"
或如您在前面的示例中看到的那样。
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:
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener);
结果:
function eventHandler (ev) {
alert('eventHandler ran');
console.log(ev);
return false;
}
这样您已经可以看到区别。有了addEventListener
我们,我们就不会包装在onclick
函数中。我们的处理程序event
直接接收参数(因此我们可以随意调用它)。同样,我们return false
在这里处于“最高层”,我们不必担心会添加诸如with的额外return语句onclick
。
因此,看起来它应该可以工作。单击链接,我们会收到警报。解除警报,页面导航/刷新。 即该事件未通过返回false取消。
如果我们查看规范(请参阅底部的资源),则会发现addEventListener的回调/处理函数不支持返回类型。我们可以返回所需的任何内容,但是由于它不是浏览器API /接口的一部分,因此它没有任何作用。
解决方案:使用event.preventDefault()
代替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 ...
var el = document.querySelector('a.js-some-link-hook'),
listener = getEventListeners(el).click[0].listener;
console.log(''+listener);
给...
function eventHandler (ev) {
ev.preventDefault();
alert('eventHandler ran');
}
...符合预期
再次测试:
- 点击链接。
- 得到警报。
- 解除警报。
- 没有页面导航或刷新发生...这就是我们想要的。
因此,addEventListener
将其event.preventDefault()
用作返回false不会执行任何操作。
资源资源
html5规范(https://www.w3.org/TR/html5/webappapis.html#events)使事情变得混乱,因为它们在示例中同时使用onclick
和addEventListener
,并表示以下内容:
事件处理程序H和事件对象E的事件处理程序处理算法如下:
...
- 处理返回值如下:
...
如果返回值是Web IDL布尔值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*
示例中的样式事件处理程序,则不会造成混淆。