communication between 2 browser windows in electron
Asked Answered
V

6

19

I need to build an app that will span across multiple monitor screens, something like this: multiple windows on different monitors Electron supports multiple windows but how do I communicate between them?

Volsung answered 25/10, 2016 at 23:34 Comment(0)
V
41

The main thing to remember is that in Electron, interProcess communication is done by ipcMain (in the main process) and ipcRenderer(in all the created windows). Like below: enter image description here From what i've seen in the GitHub comments - direct communication between the Renderer instances is not allowed. Everything must pass trough the mainProcess.

the code: mainProcess.js:

function createWindow1 () {
  window1 = new BrowserWindow({width: 800,height: 600})
  window1.loadURL(`file://${__dirname}/window1.html`)
  window1.webContents.openDevTools()
  window1.on('closed', function () {
     window1 = null
  })
  return window1
}
function createWindow2 () {
  window2 = new BrowserWindow({width: 1000, height: 600})
  window2.loadURL(`file://${__dirname}/window2.html`)
  window2.webContents.openDevTools()
  window2.on('closed', function () {
    window2 = null
  })
  return window2
}

app.on('ready', () => {
  window1 = createWindow1();
  window2 = createWindow2();

  ipcMain.on('nameMsg', (event, arg) => {
  console.log("name inside main process is: ", arg); // this comes form within window 1 -> and into the mainProcess
  event.sender.send('nameReply', { not_right: false }) // sends back/replies to window 1 - "event" is a reference to this chanel.
  window2.webContents.send( 'forWin2', arg ); // sends the stuff from Window1 to Window2.
});

window1.html:

<body>
    <input type="text" id="name" value="" placeholder="Enter your name">
    <button type="button" id="sendName" >Send the name! </button>
</body>
<script>
   // You can also require other files to run in this process
   require('./window1.js')
</script>

window1.js:

const ipcRenderer = require('electron').ipcRenderer

let name = document.getElementById('name');

ButtonSendName = document.getElementById('sendName');
ButtonSendName.addEventListener('click', (event) => {
  ipcRenderer.send('nameMsg', name.value);
})

ipcRenderer.on('nameReply', (event, arg) => {
  console.log(arg) // why/what is not right..
});

window2.html:

<body>
  <p id = "showName"></p>
</body>

<script>
  require('./window2.js')
</script>

window2.js:

const { ipcRenderer } = require('electron')

showName = document.getElementById('showName')
ipcRenderer.on('forWin2', function (event, arg){
  console.log(arg);
  showName.innerHTML = arg;
});
console.log("I'm Window2");

A demo would be better, but I don't know how to build an electron CodeBin app. This image gives you an idea: enter image description here

Enjoy the power of Electron !

Volsung answered 25/10, 2016 at 23:34 Comment(2)
Great explanation! Thanks. I don't know why, but every time I am reading about ipc I always have a strong feeling that Electron developers over-engineered something simple like passing a variable between two windows :)Spoil
@SystemsRebooter I'm not an Electron dev but since each BrowserWindow object is a separate process, unfortunately it's not as simple as you think. In fact, it'd take a lot more engineering to make it easier for us :)Takashi
K
4

EDIT: I've created a repository for this: electron-multi-monitor: enter image description here

We had a similar issue for our project. However both BrowserWindows had to pass JS objects & functions back and forth.

The proposed solution via IPC calls was the first thing we tried but wasn't sufficient. It works great when you only need to pass a few small object but you'll soon reach it's limits as Electron will serialize all data passed via IPC calls.

The way we went forward was by using the window.opener functionality. We use electron to spawn one main BrowserWindow, which then opens the wanted number of side Browser Windows via window.open(). Electron can position those windows as they spawn. Next each side window will register it's HTML DOM as its JS window instance with the main window. That way the main window has a reference to the side windows' DOM and JS window instance. From here on you the main window has full control over all the available windows, and can just render new HTML, pass JS objects, call JS functions, ... on all the windows. Personally we use React Portals to handle the rendering on the different windows.

Currently I can't share a complete example, but if I find the time I'll create a github repo.

A few things that can already help you forward:

  • BrowserWindows should have the same affinity (see BrowserWindow docs)
  • Enable nativeWindowOpen of your webPreferences

FYI: you can also use this technique straight in the browser, but they still don't let you move around windows

Kinchen answered 14/3, 2021 at 17:30 Comment(4)
Looks like affinity is deprecatedFatherly
Yes @user1689987. It seems they're moving away of the fact that they use different processes for child windows, which is good as it makes setting the affinity unnecessary. (more info on the deprecated affinity: github.com/electron/electron/issues/18397)Kinchen
Is it possible to set the menu of the new window to 'null'? and also to remove the 'frame' of the new window like you can when making the browser window from the main by setting frame: false?Tacye
Yes @Tacye that's possible. You can use the webContents.setWindowOpenHandler (electronjs.org/docs/latest/api/…) for that. You can assign your own handler when a new pop-up window is created and adapt some BrowserWindow settings, before the new window is created. Tip: when you open the new window via window.open() in JS, also pass the new window a name. That name will be available in the electron `` event, so you can easily filter on those pop-ups.Kinchen
H
3

You actually can communicate between 2 Electron windows via JS if you open the popup window from the renderer process of the main window, by using window.open(). This avoids the need to communicate via IPC calls. See Docs.

For example:

//renderer process
let popupWindow = window.open(
  './popup.html', 
  'popup'       
  'width=800,height=600'
);

popupWindow.onload = () => {       
  //now we have access to popup window dom   
  popupWindow.document.body.appendChild(myDomElement);
};

Note that for this to work you need to set the nativeWindowOpen webPreferences option when you initially create the main window.

// main process
const mainWindow = new BrowserWindow({
  width: 800,
  height: 600,
  webPreferences: {
    nativeWindowOpen: true
  }
})
Heerlen answered 20/8, 2021 at 21:8 Comment(0)
C
1

Whenever we talk about communicating from one window to another window inside of an Electron application, you always want to be thinking of the IPC system, Inter Process Communication.

So in one window you will listen for an event, for example, a form submittal.

Once the form is submitted, you can take the text out of that input and emit an event to the Electron app.

Then the Electron app will trigger its own event and send the event on over to the mainWindow which will receive the text and append it on to its list.

You can start this with just vanilla JavaScript in the secondary window.html file like so:

  document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
  });

So the above assumes you are working with a form you are trying to submit.

Conti answered 12/11, 2018 at 2:50 Comment(0)
O
1

This question is quite old, but I'll add my two cents.

I have spent some time looking for an answer to this question and I can share what I've found out.

If you want to directly communicate between two windows without having to create a listener in the main process you can simply use ipcRenderer.sendTo function.

Example:

First window

ipcRenderer.on("first_window", (_, args) => console.log(args));

ipcRenderer.sendTo(2, "second_window", {
  hello: "world",
});

Second window

ipcRenderer.on("second_window", (_, args) => console.log(args));

ipcRenderer.sendTo(1, "first_window", {
  world: "hello",
});

The first window should print in the console:

{
  world: "hello",
}

The second window should print in the console:

{
  hello: "world",
}

The number in sendTo is the ID of the window. Windows in electron are numbered automatically in ascending order from what I've noticed. This means that first window you create has an ID of 1, the second window has an ID of 2 and so on...

Important thing to notice is windows that show up in production but not in development. E.g. When you have a main window, splash screen and a third window, you don't necessarily want to show the splash screen in the development mode, but do in production.

Then the frames are going to be indexed with -1. E.g. Configuration with a splash screen, main window, third window will have them indexed like this:

  • main window: 1
  • splash screen: 2
  • third window: 3

But when you drop the splash screen you end up with something like this.

  • main window: 1
  • third window: 2

https://www.electronjs.org/docs/latest/api/ipc-renderer#ipcrenderersendtowebcontentsid-channel-args

Orling answered 25/11, 2022 at 20:48 Comment(2)
I don't see sendTo in the docs any more, https://www.electronjs.org/blog/electron-27-0#deprecated-ipcrenderersendto shows it is now deprecated, "The ipcRenderer.sendTo() API has been deprecated"Tacye
Maybe you meant sendToHost? electronjs.org/docs/latest/api/…Catboat
J
0

Depending on your requirements... it is possible to create SharedWorker that acts as a proxy transferring MessagePorts between windows. Note: SharedWorker requires all windows run from the same origin (might not meet your requirements). So for example, in the main window create a MessageChannel (which has provides two ports), then transfer port1 via the SharedWorker to one window and port2 to the other window. Now the two windows can communicate directly via the ports using postMessage. As a bonus postMessage also supports transferables. I was playing around with the idea but haven't yet fully developed the library but you can get the idea from some work in progress here: https://github.com/lneir/electron-direct-comm

Jacques answered 31/10, 2018 at 2:35 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.