Stop page from unloading within the beforeunload event handler

前端 未结 3 1162
故里飘歌
故里飘歌 2020-12-11 10:02

Before user navigates the page code checks if he edited some of the form fields. If he did, I display a modal window with Yes and No buttons. If he

相关标签:
3条回答
  • 2020-12-11 10:18

    This is possible, but only if the user clicks a hyperlink within the page. If the user uses the browser to navigate away from the page, they will get the default browser dialog, which doesn't have an option to save.

    Dirty Forms automatically attaches (and removes the handler) to the beforeunload event, so your attempt to create another event handler will most certainly fail. You should never do this when using Dirty Forms.

    You didn't mention which modal dialog framework you wanted to use, so I will just show an example using jQuery UI dialog. Integrating other dialog frameworks is similar. To do so, you might want to check out the source code of the existing pre-built dialogs.

    Also, your use case is a bit short-sided. What if the user wants to navigate away and ignore the changes? I have added an example that includes an additional option to do just that.

    <html>
    <head>
        <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/jquery.ui/1.11.3/jquery-ui.min.css" />
    </head>
    <body>
    
        <script type="text/javascript" src="//cdn.jsdelivr.net/g/jquery@1.11.3,jquery.ui@1.11.3,jquery.dirtyforms@2.0.0"></script>
        <script type="text/javascript">
        $(document).ready(function() {
            // This is required by jQuery UI dialog
            $('body').append('<div id="dirty-dialog" style="display:none;" />');
    
            // This must be called before the first call to .dirtyForms
            $(document).bind('bind.dirtyforms', function (ev, events) {
                var originalOnRefireClick = events.onRefireClick;
    
                events.onRefireClick = function (ev) {
                    if (saveForm) {
                        // TODO: Replace this with your AJAX function
                        // to save the form.
                        alert('saving form...');
                    }
                    originalOnRefireClick(ev);
                };
            });
    
            // Flag indicating whether or not to save the form on close.
            var saveForm = false;
    
            $('.mainForm').dirtyForms({
                dialog: {
                    // Custom properties to allow overriding later using 
                    // the syntax $.DirtyForms.dialog.title = 'custom title';
    
                    title: 'Are you sure you want to do that?',
                    proceedAndSaveButtonText: 'Save Changes & Continue',
                    proceedAndCancelButtonText: 'Cancel Changes & Continue',
                    stayButtonText: 'Stay Here',
                    preMessageText: '<span class="ui-icon ui-icon-alert" style="float:left; margin:2px 7px 25px 0;"></span>',
                    postMessageText: '',
                    width: 650,
    
                    // Dirty Forms Methods
                    open: function (choice, message) {
                        $('#dirty-dialog').dialog({
                            open: function () {
                                // Set the focus on close button. This takes care of the 
                                // default action by the Enter key, ensuring a stay choice
                                // is made by default.
                                $(this).parents('.ui-dialog')
                                       .find('.ui-dialog-buttonpane button:eq(2)')
                                       .focus();
                            },
    
                            // Whenever the dialog closes, we commit the choice
                            close: choice.commit,
                            title: this.title,
                            width: this.width,
                            modal: true,
                            buttons: [
                                {
                                    text: this.proceedAndSaveButtonText,
                                    click: function () {
                                        // Indicate the choice is the proceed action
                                        choice.proceed = true;
    
                                        // Pass a custom flag to indicate to save the data first
                                        // in the onRefireClick event
                                        saveForm = true;
    
                                        $(this).dialog('close');
                                    }
                                },
                                {
                                    text: this.proceedAndCancelButtonText,
                                    click: function () {
                                        // Indicate the choice is the proceed action
                                        choice.proceed = true;
                                        // Pass a custom flag to indicate not to save the data
                                        // in the onRefireClick event
                                        saveForm = false;
    
                                        $(this).dialog('close');
                                    }
                                },
                                {
                                    text: this.stayButtonText,
                                    click: function () {
                                        // We don't need to take any action here because
                                        // this will fire the close event handler and
                                        // commit the choice (stay) for us automatically.
                                        $(this).dialog('close');
                                    }
                                }
                            ]
                        });
    
                        // Inject the content of the dialog using jQuery .html() method.
                        $('#dirty-dialog').html(this.preMessageText + message + this.postMessageText);
                    },
                    close: function () {
                        // This is called by Dirty Forms when the 
                        // Escape key is pressed, so we will close
                        // the dialog manually. This overrides the default
                        // Escape key behavior of jQuery UI, which would
                        // ordinarily not fire the close: event handler 
                        // declared above.
                        $('#dirty-dialog').dialog('close');
                    }
                }
            });
        });
        </script>
    
        Change one of the fields below, then click "Go to Google" to try to navigate away.
    
        <form class="mainForm" action="jqueryui.html">
            First name: <input type="text" name="fname"><br>
            Last name: <input type="text" name="lname"><br>
            <input type="submit" value="Submit">
        </form>
    
        <a href="http://www.google.com/">Go to Google</a>
    
    </body>
    </html>
    

    The example uses custom event binding to attach to the onRefireClick event handler, which is fired when choice.proceed = true, but not when it is false.

    0 讨论(0)
  • 2020-12-11 10:24

    I believe you just need to call preventDefault on the event.

     $(window).bind('beforeunload', function(e){
            // check if any field is dirty 
            if ($('div.form').dirtyForms('isDirty')) {
                var modalParams = {
                    Content: 'You have some unsaved changes, proceed?'
                    OnSave: navigateAndSave,
                    OnCancel: e.preventDefault();
                }
               ShowModal(modalParams);
            }
        })
    
    function navigateAndSave() {
        // Do stuff
    };
    
    0 讨论(0)
  • 2020-12-11 10:45

    That's not possible.

    You are limited to show a text message along with the option to continue on the page or go away.

    Please, see this link for more information: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload

    window.onbeforeunload = funcRef
    
    • funcRef is a reference to a function or a function expression.
    • The function should assign a string value to the returnValue property of the Event object and return the same string.

    Example:

    window.onbeforeunload = function(e) {
        return 'Dialog text here.';
    };
    

    You can, after the user decides to remain on the page, show your modal, but I think it's propose will be defeated at that time.

    As stated by others, you can intercept navigation by listening to click and key events on links and forms on your page, but this will not allow you to show any dialog when the user tries to enter a URL manually, close the tab, close the window, press the reload, back or forward buttons, etc.

    The beforeunload event is designed this way to protect users from malicious scripts.

    If permitted that kind of control someone could lock your browser in a desired page, not allowing you to navigate away or close the window.

    Edit.:

    I've managed to show a confirmation dialog that works almost the same way you want by attaching to the 2 events beforeunload and unload. It works on Internet Explorer 11, Firefox 40 and Safari 5.1 for Windows (that I can confirm right now):

    var alreadyTriggered = false;
    
    function onQuit() {
      if (alreadyTriggered) return true;
      alreadyTriggered = true;
    
      if (confirm('Sure?')) {
        alert('OK');
      } else {
        alert('Cancel');
      }
    }
    
    window.onbeforeunload = onQuit;
    window.onunload = onQuit;
    You can <a href="javascript: location.reload();">reload the page</a> to test it here.

    This relies on the break on the JS event loop that the confirmation dialog causes and there is no way of making this work with custom dialogs because the page will change or close before the users gets the chance to interact with it.

    There is still no way to avoid the page to change or close and also no way to make this work in Chrome. This will also not work in Firefox the event was not fired by an user interaction.

    0 讨论(0)
提交回复
热议问题