71

I'm using the BrowserWindow to display an app and I would like to force the external links to be opened in the default browser. Is that even possible or I have to approach this differently?

Lipis
  • 21,388
  • 20
  • 94
  • 121

12 Answers12

155

Update: this does not work in electron >= 22 because on('new-window' has been removed.

I came up with this, after checking the solution from the previous answer.

mainWindow.webContents.on('new-window', function(e, url) {
  e.preventDefault();
  require('electron').shell.openExternal(url);
});

According to the electron spec, new-window is fired when external links are clicked.

NOTE: Requires that you use target="_blank" on your anchor tags.

Welcor
  • 2,431
  • 21
  • 32
René Herrmann
  • 1,566
  • 1
  • 9
  • 3
  • 4
    doesn't `new-window` only fire when `target` is set to `_blank`? – Yan Foto Sep 06 '15 at 21:11
  • 1
    @YanFoto maybe my bad.. but by external I meant that.. I tried the `will-navigate` but it didn't work in my case.. – Lipis Sep 06 '15 at 21:24
  • @lipis now i get it! I thought you meant everything which is not local :D – Yan Foto Sep 06 '15 at 21:26
  • 1
    Don't know what ```shell``` should be but I used the npm node module https://www.npmjs.com/package/open to open the external link. So my code looks exactly the same instead of line ```require('shell').openExternal(url);``` that is ```var open = require('open'); open(url);``` – Frank Roth Jul 04 '16 at 08:45
  • 2
    Instead of `require('shell')`, which threw an error for me, I was able to get this to work using `electron.shell.openExternal(url);` – Doug Aug 22 '16 at 16:15
  • 8
    `main` is the window returned when you create your browser window with `new BrowserWindow()` – edrian Dec 27 '16 at 16:48
  • 1
    @Doug `shell` is a part of `electron`, not a separate module. So you need to use `require( "electron" ).shell` or `const { shell } = require( "electron" )` – Joshua Pinter Sep 04 '18 at 13:57
  • 2
    `new-window` is deprecated in Electron 12. See https://stackoverflow.com/a/67409223/1394731 – theicfire May 05 '21 at 21:57
64

new-window is now deprecated in favor of setWindowOpenHandler in Electron 12 (see https://github.com/electron/electron/pull/24517).

So a more up to date answer would be as follows, this goes in your 'entry point' file for example main.js:

const mainWindow = new BrowserWindow()

mainWindow.webContents.setWindowOpenHandler(({ url }) => {
  shell.openExternal(url);
  return { action: 'deny' };
});

See also the electron docs Native window example.

This will affect all links in your electron app that have target="_blank" set.

icc97
  • 11,395
  • 8
  • 76
  • 90
theicfire
  • 2,719
  • 2
  • 26
  • 29
10

Improved from the accepted answer ;

  1. the link must be target="_blank" ;
  2. add in background.js(or anywhere you created your window) :

    window.webContents.on('new-window', function(e, url) {
      // make sure local urls stay in electron perimeter
      if('file://' === url.substr(0, 'file://'.length)) {
        return;
      }
    
      // and open every other protocols on the browser      
      e.preventDefault();
      shell.openExternal(url);
    });
    

Note : To ensure this behavior across all application windows, this code should be run after each window creation.

Cethy
  • 1,552
  • 2
  • 15
  • 22
  • Good point on the `target` - electron calls the 'entry point' where you create your window `main.js` - see their [Quick Start scaffolding](https://www.electronjs.org/docs/latest/tutorial/quick-start#scaffold-the-project) docs – icc97 Aug 24 '23 at 13:16
  • Also of course `webContents.on('new-window'...` is deprecated now – icc97 Aug 24 '23 at 13:17
8

If you're not using target="_blank" in your anchor elements, this might work for you:

  const shell = require('electron').shell;

  $(document).on('click', 'a[href^="http"]', function(event) {
    event.preventDefault();
    shell.openExternal(this.href);
  });
niieani
  • 4,101
  • 1
  • 31
  • 22
4

I haven't tested this but I assume this is should work:

1) Get WebContents of the your BrowserWindow

 var wc = browserWindow.webContents;

2) Register for will-navigate of WebContent and intercept navigation/link clicks:

wc.on('will-navigate', function(e, url) {
  /* If url isn't the actual page */
  if(url != wc.getURL()) {
    e.preventDefault();
    openBrowser(url);
  } 
}

3) Implement openBrowser using child_process. An example for Linux desktops:

var openBrowser(url) {
  require('child_process').exec('xdg-open ' + url);
}

let me know if this works for you!

SkyzohKey
  • 755
  • 7
  • 16
Yan Foto
  • 10,850
  • 6
  • 57
  • 88
  • @SkyzohKey you edited the condition from pseudo code that said "If url is external"; did you actually test that `url != wc.getURL()` is helpful? I imagine it would always be true. – 1j01 Aug 24 '18 at 22:13
4

For anybody coming by.

My use case:

I was using SimpleMDE in my app and it's preview mode was opening links in the same window. I wanted all links to open in the default OS browser. I put this snippet, based on the other answers, inside my main.js file. It calls it after it creates the new BrowserWindow instance. My instance is called mainWindow

let wc = mainWindow.webContents
wc.on('will-navigate', function (e, url) {
  if (url != wc.getURL()) {
    e.preventDefault()
    electron.shell.openExternal(url)
  }
})
Patrick Lee
  • 79
  • 1
  • 5
1

Check whether the requested url is an external link. If yes then use shell.openExternal.

mainWindow.webContents.on('will-navigate', function(e, reqUrl) {
  let getHost = url=>require('url').parse(url).host;
  let reqHost = getHost(reqUrl);
  let isExternal = reqHost && reqHost != getHost(wc.getURL());
  if(isExternal) {
    e.preventDefault();
    electron.shell.openExternal(reqUrl);
  }
}
cuixiping
  • 24,167
  • 8
  • 82
  • 93
1

Put this in renderer side js file. It'll open http, https links in user's default browser.

No JQuery attached! no target="_blank" required!

let shell = require('electron').shell
document.addEventListener('click', function (event) {
  if (event.target.tagName === 'A' && event.target.href.startsWith('http')) {
    event.preventDefault()
    shell.openExternal(event.target.href)
  }
})
GorvGoyl
  • 42,508
  • 29
  • 229
  • 225
1

For Electron 5, this is what worked for me:

  • In main.js (where you create your browser window), include 'shell' in your main require statement (usually at the top of the file), e.g.:

    // Modules to control application life and create native browser window
    const {
        BrowserWindow,
        shell
    } = require('electron');
    
  • Inside the createWindow() function, after mainWindow = new BrowserWindow({ ... }), add these lines:

    mainWindow.webContents.on('new-window', function(e, url) {
        e.preventDefault();
        shell.openExternal(url);
    });
    
Cave Johnson
  • 6,499
  • 5
  • 38
  • 57
Jaifroid
  • 437
  • 3
  • 6
0

I solved the problem by the following step

  1. Add shell on const {app, BrowserWindow} = require('electron')
const {app, BrowserWindow, shell} = require('electron')

  1. Set nativeWindowOpen is true
function createWindow () {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 1350,
    height: 880,
    webPreferences: {
      nativeWindowOpen: true,
      preload: path.join(__dirname, 'preload.js')
    },
    icon: path.join(__dirname, './img/icon.icns')
})
  1. Add the following listener code
  mainWindow.webContents.on('will-navigate', function(e, reqUrl) {
    let getHost = url=>require('url').parse(url).host;
    let reqHost = getHost(reqUrl);
    let isExternal = reqHost && reqHost !== getHost(wc.getURL());
    if(isExternal) {
      e.preventDefault();
      shell.openExternal(reqUrl, {});
    }
  })

reference https://stackoverflow.com/a/42570770/7458156 by cuixiping

hang gao
  • 421
  • 5
  • 6
0

I tend to use these lines in external .js script:

let ele = document.createElement("a");
let url = "https://google.com";
ele.setAttribute("href", url);
ele.setAttribute("onclick", "require('electron').shell.openExternal('" + url + "')");
0

If you are using new window using window.open() then use the setWindowOpenHandler as new-window is depreciated.

I was working on react and had to open my component in new window. Also, this window does not have a url I used the frameName property to open the browserWindow in electron. However you may use url also inside if condition block.

window.open("", "CallWindow", features);
mainWindow.webContents.setWindowOpenHandler(({ frameName }) => {
   // Dynamically give position to opened window
    const leftPos = screen.getPrimaryDisplay().size.width - 420;
    const topPos = 60;

    if (frameName === "CallWindow") {
      return {
        action: "allow",
        overrideBrowserWindowOptions: {
          width: 360,
          height: 728,
          minWidth: 220,
          minHeight: 220,
          x: leftPos,
          y: topPos,
          autoHideMenuBar: true,
        },
      };
    }
    return { action: "deny" };
  });

Following link shows how to use setWindowOpenHandler. https://github.com/electron/electron/blob/main/docs/api/window-open.md