How to catch beforeunload and stop page to reload without alert

Hi, I’d like to add functionality, when it has been change and user clicks some link on the page it warns him about unsaved changes with my custom modal and prevents page to reload. I’v stucked in the moment when alert by the browser appears “Changes that you made may not be saved.”, how to get rid of it and stop page reload ?

    //this.leavingPage = this.leavingPage.bind(this)
    window.addEventListener("turbolinks:before-visit", this.leavingPage);
    window.addEventListener("beforeunload", this.leavingPage);
  }

  disconnect() {
    window.removeEventListener("turbolinks:before-visit", this.leavingPage);
    window.removeEventListener("beforeunload", this.leavingPage);
  }

  leavingPage(event) {
    event.stopPropagation()
    event.preventDefault()
    return false
  }

I’v tried it also by adding to the form data-action=“beforeunload@window->form-unsaved-changes#leavingPage” but same result

Im not sure how you would tap into the turbolinks event for visiting a new page but what you could do is listen for all clicks on that page and then you can check if the click events target is a link element and then do whatever code you want to stop them

Something like this?

    class ExitController extends Controller {
      preventDefault(event) {
        event.preventDefault();
        return false;
      }

      confirm(event) {
        event.returnValue = '';
        return this.preventDefault(event);
      }
    }
<body data-controller="exit" 
      data-action="turbo:click@window->exit#preventDefault beforeunload@window->exit#confirm">
  <a href="page2.html">Page 2</a>
</body>

Thats big step forward thanks :slight_smile: Above solution works perfect when user clicks some link, moving to the other page is stopped and I can display modal or sth. But when I click back button in the browser I’m getting (at least at Chrome) standard alert with text “Changes that you made may not be saved.”. Any way to prevent it ?

unfortunately after some testing I’v found that catching click is a fragile solution because any click is catched, like click some ajax link which adds some items to my form, or just clicking textarea to fill it etc … at least when I’v tried click event, no turbo.
I’d rather prefer some solution based only on beforeunload/onbeforeunload, if possible

Since turbo navigates pages using fetch, the beforeunload is not fired. The only accomplish this by disabling turbo. You could re-enable it once the page becomes valid?

     connect() {
        document.body.dataset.turbo = false; //. disable turbo
        window.addEventListener("beforeunload", this.leavingPage);
      }

      disconnect() {
        window.removeEventListener("beforeunload", this.leavingPage);
      }

      leavingPage(event) {
        event.preventDefault();
        event.returnValue = '';
        return false;
      }

Thanks, I do not use turbo/turbolinks it this project particullary, here’s no turbo version based on yours, it may help someone. I do not set listener in connect but in data-action attribute like beforeunload@window->form-unsaved-changes#confirm

I’v also extended it by possiblity of disabling that leave page prompt by setting skipCheck to true. I do it by sending respective custom event and receiving it here.

  export default class extends Controller {
  static value = { skipCheck: Boolean }

  isFormChanged() {
    // formChanges from here https://www.sitepoint.com/javascript-form-change-checker/
    const form_changes = formChanges(this.element) 
    return (form_changes && form_changes.length > 0)
  }

  receiveSkipCheck(event) {
    this.skipCheck = event.detail.value
  }


  leavingPage(event) {
    if(!this.skipCheck && this.isFormChanged()) {
      event.preventDefault()
      event.returnValue = ''
      return false;
    }
  }
}

btw, I think you’d just left event.preventDefault() in leavingPage() by coincidence, it’s rather not necessary here.

I’m wondering about one more feature here, is there any way to catch that Cancel button has been clicked in confirmation dialog ? I need to apply some functionality in that case.