Search code examples
javascriptjqueryajaxtypescriptonbeforeunload

Is there a way to do something similar to onbeforeunload for an AJAX form?


Sadly, we are in a situation where we have to use a home-brew, spa-like engine, that hijacks all navigation and does it via ajax.

I am looking for a way to be able to add a tag to a form, maybe like this:

<form data-confirm-unsaved=""></form>

Which should cause a binding to the unloading of this form, whether it is an actual whole page unload (like someone closed the tab or typed in a URL) or a click on any of the hijacked navigation elements... but I do not want it to be triggered when the form is submitted/saved.

I have the beginning outline of what is NOT working code:

$('[data-confirm-unsaved]').on('beforeunload', function (e) {
        let isUnload = true;
        let message = $(this).attr('[data-confirm-unsaved]');
        if (message == '')
            message = "This data is unsaved. Are you certain that you want to cancel?";

        if (isFormDirty($(this)))
            isUnload = confirm(message);

        if (isUnload)
            e.preventDefault();
    });

Of course I can't bind the beforeunload event to a form... only the window, can I get some clues on how I would go about doing this?

The expected result is that when the form gets unloaded by whatever means except submission, with dirty inputs that a confirmation appears if they really want to do so.


Solution

  • You can use MutationObserver attached to <body> with childList option set to true, at MutationEvent, check if <form> is removed node.

    <body>
      <form id="form">
        <input>
      </form>
      <script>
        const form = document.getElementById("form");
        
        const observer = new MutationObserver(([{removedNodes}]) => {
          
          if (removedNodes.length) {
            // do stuff if `form` is removed from `document.body`
            checkRemovedNodes: for (const node of removedNodes) {
              if (node === form) {
                console.log(`${node.tagName}#${node.id} removed from document`);
                break checkRemovedNodes;
              }
            }
          }
    
        });
        // remove `form` from `document` in `3000` milliseconds
        observer.observe(document.body, {
          childList: true
        });
    
        setTimeout(() => document.body.removeChild(form), 3000);
      </script>
    </body>