dkirchhof
dkirchhof

Reputation: 385

Confirm beforeunload in Electron

i know that there are hundreds of questions like: "How can i prevent close event in electron" or something like that.
After implementing a confirmation box (electron message box) in the beforeunload event i was able to close my app and cancel the close event. Since the dev tools are always open, i didn't recognize that it doesn't work while the dev tools are closed...

window.onbeforeunload = e =>
{           
    // show a message box with "save", "don't save", and "cancel" button
    let warning = remote.dialog.showMessageBox(...) 

    switch(warning)
    {
        case 0:
            console.log("save");
            return;
        case 1:
            console.log("don't save");
            return;
        case 2:
            console.log("cancel");
            return false;
            // e.returnValue = "false";
            // e.returnValue = false;
    }
};

So, when the dev tools are opened, i can close the app with saving, without saving and cancel the event.
When the dev tools are closed, the cancel button doesn't work anymore.

Btw.:

window.onbeforeunload = e =>
{           
    return false;
    alert("foo");
};

will cancel the close event and obviously wouldn't show the message (doesn't matter if dev tools are open or closed)

window.onbeforeunload = e =>
{           
    alert("foo");
    return false;
};

will cancel the close event after pressing ok if dev tools are open and will close the app after pressing ok if dev tools are closed

Intentionally i'm using the synchronous api of the message box and while i'm writing this question i figured out that a two windowed app (new remote.BrowserWindow()) will behave exactly like with the dev tools.

Has anyone an idea how i can resolve this problem?
Many thanks in advance

Upvotes: 6

Views: 11413

Answers (4)

Johnathan Agepas
Johnathan Agepas

Reputation: 1

You can simply use 'pagehide' event. It seems to be working fine for electron apps. It works slightly different from 'beforeunload' as it can't prevent closing window/tab, but if you only need to do something before the page is closed(send some async request with navigator.sendBeacon(), etc) then this event might suit your needs.

You can read more info about it here, here and in the docs

Example of usage:

window.addEventListener('pagehide', () => {
    window.navigator.sendBeacon(url, data);
}

Upvotes: 0

Kostyantin2216
Kostyantin2216

Reputation: 646

Maybe this will help someone with similar needs as i had, i have a react app wrapped in an electron app, the react app is agnostic to electron and can also run in the browser and the requirements i had was to show the default browser prompt, the infamous Leave Site? alert.

In the browser this is easy, for example with react i just do this:

useEffect(() => {
   window.onbeforeunload = promptOnProjectLeave ? () => true : undefined;
    return () => {
        window.onbeforeunload = undefined;
    }
}, [promptOnProjectLeave]);

Which will show the default browser Leave Site? prompt, but in electron this will only prevent the window from being closed without any action prompt asking you if you are sure, so my approach was a mix of this post and another post.

This is the solution

mainWindow.webContents.on('will-prevent-unload', (event) => {
    const options = {
        type: 'question',
        buttons: ['Cancel', 'Leave'],
        message: 'Leave Site?',
        detail: 'Changes that you made may not be saved.',
    };
    const response = dialog.showMessageBoxSync(null, options)
    if (response === 1) event.preventDefault();
});

This will allow me to use window.onbeforeunload in my react code as i would in the browser, in the browser i will get the default browser prompt and in electron i will get a message box :)

This is my first time working with electron so might be some ways to improve this but either way hope this helps someone, i know it would have helped me when i started with this task.

Upvotes: 4

Niklas
Niklas

Reputation: 1299

Update:

As I mentioned above in the comments of the accepted answer, the preventDefault was ignored on Windows. To be precise, I had it placed in the callback of a native electron dialog that opened when the user closed the app.

Therefore I have implemented a different approach:

let close: boolean = false    

win.on('close', (ev: any) => {

  if (close === false) {
    ev.preventDefault()

    dialog.showMessageBox({
      type: 'warning',
      buttons: ['Cancel', 'Ok'],
      title: 'Do not forget to safe your changes',
      cancelId: 0,
      defaultId: 1,
      noLink: true
    }).then((val) => {

      if (val.response === 0) {
        // Cancel the close process
      } else if (win) {
        close = true
        app.quit()
      }
    })
  }

})

Upvotes: 0

Jerome
Jerome

Reputation: 396

Instead of onbeforeunload prefer working with the event close. From this event, you'll be able to catch the closing event before the whole closure process is completed (event closed). With close, you'll be able to take the control and stop whenever you need the completion of the closure.

This is possible when you create your BrowserWindow, preferably in the main process:

// Create the browser window.
window = new BrowserWindow({});

// Event 'close'
window.on('close', (e) => {
    // Do your control here
    if (bToStop) {
        e.preventDefault();
    }
})

// Event 'closed'
window.on('closed', (e) => {
    // Fired only if you didn't called e.preventDefault(); above!
})

In addition, be aware that the function e.preventDefault() is spreading in the whole code. If you need to be back to the natural behaviour of Electron, you need to toggle the variable e.defaultPrevented to false.

Actually, it seems e.preventDefault() function is handling the variable e.defaultPrevented to true until any change on it.

Upvotes: 13

Related Questions