1

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:

  1. I prevent the default behavior;
  2. I save in a variable lastClick the element clicked;
  3. I show my modal;
  4. If the user chooses to procede with the change page, i set formIsEdited to false, I pretend a click on the element saved in lastClick 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>
Jolly
  • 1,678
  • 2
  • 18
  • 37
  • Possible duplicate of [jQuery beforeunload custom pop up window for leaving a page](https://stackoverflow.com/questions/30712377/jquery-beforeunload-custom-pop-up-window-for-leaving-a-page) – Randy Casburn May 14 '18 at 18:17
  • @RandyCasburn Not entirely: the reason why I had my doubt was to prevent leaving the page, but the real question is wht that `stopPropagation()` doesn't actually stop the propagation. I add one more information I forgot to metion in the post – Jolly May 14 '18 at 18:19
  • _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._ - You cannot prevent the user from leaving. – Randy Casburn May 14 '18 at 18:21
  • Reference your edit: What do you suppose you might do if the user clicks on a **bookmark on their bookmarks bar** ? – Randy Casburn May 14 '18 at 18:24
  • Well, that was the last problem I should need to solve.. In that case I think I would show the "normal" browser prompt. If i cannot prevent the user to lose some edits because he clicks on a bookmark (or even refresh the page), it doesn't mean I shouldn't care neither when he clicks on a link, if I can handle that – Jolly May 14 '18 at 18:27

1 Answers1

1

I would suggest using preventDefault() instead of stopPropagation(), please read What's the difference between event.stopPropagation and event.preventDefault? for more information on the topic.

window.addEventListener("click", (e) => {
  var closestLink = $(e.target).closest('a');
  if (closestLink!= null) {
    e.preventDefault();
    }
});
#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"></span>
  </a>
  <span id="span-2">
  </span>
</div>

This still only works for links in the DOM, you can't control the user moving away by any other action, e.g. clicking on a link in the bookmarks-bar or an element that changes or replaces the location using a click handler.

Luca Kiebel
  • 9,790
  • 7
  • 29
  • 44
  • Ok, right now it works.. Honestly? I did that, even before trying the stopPropagation() method, but.. it didn't seems to work. Unfortunately, now I'm at home I can't try on the React application, maybe I wrote something wrongly: tomorrow I will try again and tell you the response :) – Jolly May 14 '18 at 18:29
  • Ok, no problem ^^ – Luca Kiebel May 14 '18 at 18:31
  • I remember correctly: `e.preventDefualt()` did not work, but apparently there's something React-related happening there, I guess (See EDIT2 for the explanation) – Jolly May 15 '18 at 07:44