How to correctly use preventDefault(), stopPropagation(), or return false; on events
I’m sure this has been written about many times before and probably has hundreds of answers on StackOverflow.
Despite this we still find ourselves going through code bases and repeatedly finding the misuse (or interchangeable use, or combined use) of event.preventDefault()
, event.stopPropagation()
and return false;
.
So today we’re going to learn what the differences are between the three, and exactly how they function.
preventDefault()
, stopPropagation(),
and return false;
are not interchangeable, nor are they tools of trial-and-error.
We’ll start off with a pretty common UI pattern — a file upload panel — and see how each of them affect its behaviour.
https://github.com/FineUploader/fine-uploader
Image courtesy of FineUploader
HTML
<div class="file-upload"> <input type="file" name="upload-file" class="file-upload__input" style="display: none;" /> <div class="file-upload__drop-zone"> <span class="file-upload__drop-zone-text">Drop files here</span> <a href="#" class="file-upload__btn--upload">Upload files</a> </div> </div>
Our HTML consists of three parts:
- An
input
to handle the file upload dialog. This is hidden (display: none;
) , as we will be triggering the upload dialog using the following two elements. - A
div
with the class offile-upload__dropzone
which acts as the main ‘drop zone’ where we will be able to drag-and-drop files (code not included) or click to open a file upload dialog. - An
a
tag with the class offile-upload__btn--upload
which will act as the “Upload files” button, which when clicked will open a file upload dialog.
JavaScript
function fileUpload() { document.querySelector(‘.file-upload__input‘).click(); } const dropzone = document.querySelector(‘.file-upload__drop-zone‘); const button = document.querySelector(‘.file-upload__btn--upload‘); dropzone.addEventListener(‘click‘, fileUpload); button.addEventListener(‘click‘, fileUpload);
Our JavaScript, like our HTML, also consists of three parts:
- A
fileUpload
function to trigger theclick
event on the file uploadinput
. - Assigning both the dropzone
div
anda
button to variables. - Adding event listeners to those, which when clicked invoke the
fileUpload
function.
If we were to try this out now, we may see some odd behaviour — after the first dialog has opened and we have chosen our file, a second one will open prompting us again. Keep reading and all will be revealed.
event.preventDefault()
Prevents the browsers default behaviour (such as opening a link), but does not stop the event from bubbling up the DOM.
In our scenario, clicking on the “Upload files” button will invoke the fileUpload
function, as we would expect.
Being an a
tag, however, it also has a default behaviour — this being to navigate the browser to the location in the href
attribute. In this instance we have this set to #
, which in most browsers will just cause the page to jump back to the top.
Jumping back to the top of the page is not really our desired behaviour, so we can prevent this by using the preventDefault
method. This prevents the default behaviour of an element.
Modifying our JavaScript code, we can fix this so that clicking the link prevents the default behaviour of navigating to the location in the href
attribute, but still opens the file upload dialog.
dropzone.addEventListener(‘click‘, fileUpload); button.addEventListener(‘click‘, (event) => { event.preventDefault(); fileUpload(); });
Here we have taken the click
event and prevented its default behaviour using event.preventDefault()
, then invoked the fileUpload()
function.
We still have the dialog box popping up twice, but hopefully the next section will solve this issue.
event.stopPropagation()
Prevents the event from bubbling up the DOM, but does not stop the browsers default behaviour.
For an in-depth explanation of event bubbling, I’d recommend this article about event propagation. But essentially, when an event is called on an element, that event bubbles up the DOM and gets called on all of the elements parents.
In our case, that means that when we click on the “File upload” button, that click
event is also called on all of its parent elements, including our dropzone.
To see this in action, we can remove the fileUpload()
call in the button event listener and the function will still be invoked when we click on the button because the click
event will bubble up the DOM and be called on the dropzone.
dropzone.addEventListener(‘click‘, fileUpload); button.addEventListener(‘click‘, event => event.preventDefault());
This bubbling is an example of event propagation, which is where the stopPropagation
method comes into play. We can use it to prevent this default bubbling behaviour so that the event is only registered by the element it is called upon.
dropzone.addEventListener(‘click‘, fileUpload); button.addEventListener(‘click‘, event => event.stopPropagation());
Now we see that not only does the click
event not bubble up the DOM, but by removing the preventDefault
method call the a
tag acts as it should again, by navigating to its href
attribute.
But this isn’t what we want. We wanted to call the fileUpload
function and also prevent the element’s default behaviour and prevent the event from bubbling up the DOM.
We could use both preventDefault
and stopPropagation
then call the fileUpload
function, like so.
dropzone.addEventListener(‘click‘, fileUpload); button.addEventListener(‘click‘, (event) => { event.preventDefault(); event.stopPropagation(); fileUpload(); });
return false;
Usually seen in jQuery code, it Prevents the browsers default behaviour, Prevents the event from bubbling up the DOM, and immediately Returns from any callback.
In vanilla JavaScript, returning false
doesn’t have any effect on the default behaviour or event propagation of the element, as we can see here, it acts exactly as it did at the start.
dropzone.addEventListener(‘click‘, fileUpload); button.addEventListener(‘click‘, (event) => { fileUpload(); return false; });
It calls the click
event on the button, also navigating to it’s href
value, then bubbles up the DOM, calling the click
event on the dropzone too.
However…
…in the context of jQuery, returning false
will immediately exit the event listeners callback. This has the effect of both:
- Preventing the default behaviour — navigating the browser to the
a
tag’shref
attribute. - Stopping any event propagation — stopping the
click
event from bubbling up the DOM.
If we refactor our code to jQuery, we can see this in practice.
const dropzone = $(‘.file-upload__drop-zone‘); const button = $(‘.file-upload__btn--upload‘); $(dropzone).on(‘click‘, fileUpload); $(button).on(‘click‘, (event) => { fileUpload(); return false; });
We call the fileUpload
method, then return false
to prevent any default behaviour or event propagation.
Conclusion
We should use these tools correctly and wisely.
Next time when we’re in this kind of situation, we shouldn’t just play around with event.preventDefault()
, event.stopPropagation()
and return false;
until we get the desired result.
We should think what it is we want to achieve, and how to get there — not through trial-and-error and luck — but through thinking through the problem and applying the correct solution.
原文地址:https://www.cnblogs.com/chucklu/p/11165455.html