event.preventDefault() vs. return false (no jQuery)

会有一股神秘感。 提交于 2019-11-26 11:43:20
Thorben Croisé

The W3C Document Object Model Events Specification in 1.3.1. Event registration interfaces states that handleEvent in the EventListener has no return value:

handleEvent This method is called whenever an event occurs of the type for which the EventListener interface was registered. [...] No Return Value

under 1.2.4. Event Cancelation the document also states that

Cancelation is accomplished by calling the Event's preventDefault method. If one or more EventListeners call preventDefault during any phase of event flow the default action will be canceled.

which should discourage you from using any effect that returning true / false could have in any browser and use event.preventDefault().

Update

The HTML5 spec actually specifies how to treat a return value different. Section 7.1.5.1 of the HTML Spec states that

If return value is a WebIDL boolean false value, then cancel the event.

for everything but the "mouseover" event.

Conclusion

I would still recommend to use event.preventDefault() in most projects since you will be compatible with the old spec and thus older browsers. Only if you only need to support cutting edge browsers, returning false to cancel is okay.

Here are a few examples that might help people to understand and troubleshoot their problems better.

TL;DR

  • using onclick directly in html markup or via setAttribute('onclick'...) has its own pitfalls in terms of making sure you are actually returning because the contents of onclick= are wrapped into an onclick function.
  • when using onclick, you can cancel the event by making sure the onclick function returns false...your function gets wrapped...so you need to do onclick="return myHandler();" or onclick="myHandler(); return false;" or something like that to make sure that onclick returns false.
  • when using addEventListener...returning false doesn't have any effect. The interface in the spec says return type void. Use event.preventDefault() instead.
  • You can use the browser devtools getEventListeners API to see what your event listener looks like to avoid guess and check if your event handler is not behaving as expected.

Example

This example is specific to the click event with an <a> link...but can be generalized for most event types.

We have an anchor (link) with class js-some-link-hook that we want to open a modal and prevent any page navigation from happening.

The below examples were run in google chrome (71) on MacOS Mojave.

One major pitfall is assuming that onclick=functionName is the same behaviour as using addEventListener

onclick attribute

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();

Then run in the browser devtools console:

var el = document.querySelector('a.js-some-link-hook'),
        listener = getEventListeners(el).click[0].listener;
    console.log(''+listener); // to avoid truncation of output

...and you see:

function onclick(event) {
function t(n){return alert("eventHandler ran"),!1}
}

This doesn't work at all. When using onclick= your handler function is wrapped in another function. In this case you can see that my function definition is included but not called because I specified the function reference without invoking it. Further you can see that even if my function was invoked and my function returned false...the onclick function would not return that false value...which is required to 'cancel' the event. We really need return myHandlerFunc(); to be wrapped in the onclick function for things to work.

Now that we see how that is working, we can modify the code to do whatever we want. Odd example for illustration purposes (don't use this code):

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', 'return ('+eventHandler+')();');
}
addEventListenerToElement();

You see we have wrapped our eventHandler function definition into string. Specifically: a self-executing function with a return statement at the front.

Again in chrome devtools console:

var el = document.querySelector('a.js-some-link-hook'),
        listener = getEventListeners(el).click[0].listener;
    console.log(''+listener); // to avoid truncation of output

...shows:

function onclick(event) {
return (function t(e){return alert("eventHandler ran"),!1})();
}

...so yeah, that should work. Our return false (!1 due to minification running, sorry). Sure enough if we click on the link we get the alert and dismissing the alert the page doesn't navigate anywhere or refresh.

addEventListener

function eventHandler (event) {
    // want to prevent link navigation
    alert('eventHandler ran');
    return false;
}

function addEventListenerToElement () {
    var link = document.querySelector('.js-some-link-hook');
    link.addEventListener('click', eventHandler, false);
}
addEventListenerToElement();

browser devtools:

var el = document.querySelector('a.js-some-link-hook'),
        listener = getEventListeners(el).click[0].listener;
    console.log(''+listener); // to avoid truncation of output

result:

function e(n){return alert("eventHandler ran"),!1}

So you can already see the difference. We aren't wrapped in an onclick function. So our return false (return !1 due to minification) is at the top level here and we don't have to worry about adding an extra return statement like before.

So it looks like it should work. Clicking on the link we get the alert. Dismiss the alert and the page navigates/refreshes. ie the event was NOT cancelled by returning false.

If we lookup the spec (see resources at bottom), we see that our callback/handler function for addEventListener does not support a return type. We can return whatever we want, but since it isn't part of the interface, it doesn't have any effect.

Solution: using event.preventDefault() instead of return false;...

function eventHandler (event) {
    // want to prevent link navigation
    event.preventDefault();
    alert('eventHandler ran');
}

function addEventListenerToElement () {
    var link = document.querySelector('.js-some-link-hook');
    link.addEventListener('click', eventHandler, false);
}
addEventListenerToElement();

browser devtools...

var el = document.querySelector('a.js-some-link-hook'),
        listener = getEventListeners(el).click[0].listener;
    console.log(''+listener); // to avoid truncation of output

gives...

function n(e){e.preventDefault(),alert("eventHandler ran")}

...as expected.

Testing: get the alert. Dismiss alert. No page navigation or refresh...which is what we want.

Resources

The html5 spec (https://www.w3.org/TR/html5/webappapis.html#events) confuses things because they use both onclick and addEventListener in their examples and they say the following:

The event handler processing algorithm for an event handler H and an Event object E is as follows:

...

  1. Process return value as follows:

...

If return value is a Web IDL boolean false value, then cancel the event.

So it seems to imply that return false cancels the event for both addEventListener and onclick.

But, if you look at their linked definition of event-handler you see:

An event handler has a name, which always starts with "on" and is followed by the name of the event for which it is intended.

...

Event handlers are exposed in one of two ways.

The first way, common to all event handlers, is as an event handler IDL attribute.

The second way is as an event handler content attribute. Event handlers on HTML elements and some of the event handlers on Window objects are exposed in this way.

https://www.w3.org/TR/html5/webappapis.html#event-handler

So it seems that the return false cancelling the event really does only apply to the onclick (or generally on*) event handlers and not to event handlers registered via addEventListener which has a different API. Since the addEventListener API is not covered under the html5 spec and only the on* event handlers...it would be less confusing if they stuck to that in their examples and specifically called out the difference.

Rahul Shinde

Difference between preventDefault, stopPropogation, return false

Default Action – Server side action when control event raise.

Suppose we have div control and inside it we have a button. So div is the parent control of the button. We have Client side click and server side click event of the button. Also we have client side click event of the div.

On click event of the button on client side, we can control the actions of parent control and server side code using following three ways:

  • return false - This allow only client side event of the control. Server side event and client side event of the parent control is not fired.

  • preventDefault() - This allow client side event of control and its parent control. Server side event ie. Default action of the control is not fired.

  • stopPropogation() – This allow client side as well as server side event of the control. Client side event of the control is notallowed.

return false is just for IE, event.preventDefault() is supported by chrome,ff... modern browsers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!