'How to capture the window close event on Electron's renderer process?

On a classic front-end JavaScript, capturing the "window close" event can be done in a multiple ways. It can be done with:

// Method A: `close` event via an event listener
window.addEventListener( 'close', function(event){ /** your magic here **/ } )

// Method B: `beforeunload` event via an event listener
window.addEventListener( 'beforeunload', function(event){ /** your magic here **/ } )

// Method C: classic `onclose` event binder
window.onclose = function(event){ /** your magic here **/ }

// Method D: classic `onbeforeunload` event binder
window.onbeforeunload = function(event){ /** your magic here **/ }

I tried these methods inside the Electron's renderer script/environment but the window close event doesn't seem to get triggered.

Which brings to my question, how to capture the "window close" event on Electron's renderer process?



Solution 1:[1]

According to Electron's official documentation:

Event: "before-quit" Emitted before the application starts closing its windows. Calling event.preventDefault() will prevent the default behavior, which is terminating the application.

So you need to use the before-quit event on your Electron application.

Solution 2:[2]

Normally, proving that these events trigger (or not) is difficult as the render process would normally close prior to confirmation.

Instead, through the use of Inter-Process Communication, one can tell if any of these events trigger by sending one or more messages back to the main process for viewing.

To test this, I have implemented a simple preload.js, main.js and index.html script.


preload.js (main process)

const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

contextBridge.exposeInMainWorld(
    'ipcRender', {
        send: (message) => {
            ipcRenderer.send('closing', message);
        }
    });

main.js (main process)

const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;

const nodePath = require('path');

let window;

function createWindow() {
    const window = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    window.loadFile('index.html')
        .then(() => { window.show(); });

    return window;
}

electronApp.on('ready', () => {
    window = createWindow();
});

electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createWindow();
    }
});

// Testing
electronIpcMain.on('closing', (event, message) => {
    console.log(message);
})

index.html (render process)

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Electron Test</title>
    </head>

    <body>
        <input type="button" id="button" value="Close">
    </body>

    <script>
        // Method A: `close` event via an event listener
        window.addEventListener('close', (event) => {
            window.ipcRender.send('Method A'); // Doesn't work
        })

        // Method B: `beforeunload` event via an event listener
        window.addEventListener('beforeunload', (event) => {
            window.ipcRender.send('Method B'); // Works
        })

        // Method C: classic `onclose` event binder
        window.onclose = (event) => {
            window.ipcRender.send('Method C'); // Doesn't work
        }

        // Method D: classic `onbeforeunload` event binder
        window.onbeforeunload = (event) => {
            window.ipcRender.send('Method D'); // Works
        }

        // Method E: `click' event via an event listener
        document.getElementById('button').addEventListener('click', () => {
            // Let's pretent that this can close the window from the main process
            window.ipcRender.send('Method E');
        })
    </script>
</html>

Below is the result when closed via the traffic light close button or the task bar close menu.

Method B // `beforeunload` event via an event listener
Method D // classic `onbeforeunload` event binder

Depending on how one (correctly or incorrectly) implements their preload.js script, one may receive no results compared to the results shown above.

Whilst I usually implement my preload.js script differently to that shown above, I have kept it simple and inline with what most people appear to be familiar with. IE: Context Isolation (Enabled)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 akaAbdullahMateen
Solution 2 midnight-coding