Let me explain what I want to achieve: first of all, I'm building a React Application, but I think this is a JavaScript-related "problem".
Basically, what I want is to prevent the user to change page when he has made some changes on a form and he hasn't saved them. For this, let's just pretend we have a variable formIsEdited
that is set to true
if there are unsaved changes, false
otherwise.
I know something similar to this can be achieve with the beforeUnload
event, or with The <Prompt>
component belonging To React Router, but both don't allow me to have a custom modal: what I want to show it's a div with my own style, my own button, etc, ...
So, I came up with an idea: I put a eventListener
on the click
event and, if the click is on a link and formIsEdited
is true:
- I prevent the default behavior;
- I save in a variable
lastClick
the element clicked; - I show my modal;
- If the user chooses to procede with the change page, i set
formIsEdited
to false, I pretend a click on the element saved inlastClick
that will lead to the change page.
Then, it may happens that a whole div
is inside an anchor
element, so I also need to find the closest anchor
element of the div on which the click has been made: if the research of the link doesn't return null
, it means that the click would lead me to a link. So, I guessed that e.stopPropagation()
would achieve what I wanted, but... it doesn't!
If you look at the snippet code.. I thought that, if you click on the blue box, $(e.target).closest('a');
would find the link to stackOverflow and then, since it's not null, would stop the propagation, therefore would avoid the change page.. But it doesn't work.
Any idea why? Or any idea to achieve what I wanted in the first place?
EDIT: I know there are several other question about preventing the changePage, but this question is more about the reason why stopPropagation
does not avoid the click on the anchor
element.
One more info: if I would take document.anchors
and foreach element add a listener in which I do the action I itemize before, I can prevent the change page, with my own modal and my own logic!
But, unfortunately, since I'm using React, some anchor
item are not in the DOM when I can call the function that adds the listener just mentioned.
EDIT2: I tried Luca's solution and, as I remembered, it doesn't work.. But the behavior of React is "strange".
Let me explain: if I use the following code: let anchors = document.getElementsByTagName("a");
for (let i = 0; i < anchors.length; i++) {
anchors[i].addEventListener("click", (e) => {
clickListenerAnchorUnsavedChanges.call(this, e, anchors[i]);
}
}
function clickListenerAnchorUnsavedChanges(this: any, e: any, anchor: any) {
// if Edit has been made
console.log("Inside function1");
e.preventDefault();
}
The things works. Actually, let's pretend I click on the anchor
which links to the /Account page: the write inside function1
is printed before the write I've put inside the constructor of the Component Account.
Instead, with the following function:
function test1(this: any) {
window.addEventListener("click", (e) => {
console.log("inside function 2");
// if edits have been made
let closestLink = $(e.target).closest('a');
if (closestLink != null) e.preventDefault();
}
}
It appears that the first thing to be printed is the console.log inside the constructor of Account component, and then inside function 2
.
I don't know, it seems that, if I modify directly the behavior of an anchor, I can actually redirect/change the flow of the actions performed by a a click on a link. Instead, by modify the behavior on a click event, I cant.. But it seems that now this fact is more React-related, because in the snippet here the e.preventDefault()
works.
window.addEventListener("click", (e) => {
var closestLink = $(e.target).closest('a');
if (closestLink!= null) {
e.preventDefault();
e.stopPropagation();
}
});
#span-1, #span-2 {
display: inline-block;
width: 200px;
height: 100px;
}
#span-1 {
background-color: blue;
}
#span-2 {
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<a href="https://stackoverflow.com/">
<span id="span-1" />
</a>
<span id="span-2">
</span>
</div>