Electron - How to know when renderer window is ready - electron

In my main process I create a renderer window:
var mainWindow = new BrowserWindow({
height: 600,
width: 800,
x: 0,
y: 0,
frame: false,
resizable: true
});
mainWindow.openDevTools();
mainWindow.loadURL('file://' + __dirname + '/renderer/index.html');
Then I want to communicate with it in some way:
mainWindow.webContents.send('message', 'hello world');
However the main window doesn't receive this message because it isn't fully done being created at the time I attempt to send it.
I have temporarily solved this by wrapping the latter code in a setTimeout() but that is most definitely not the right way to resolve a race condition.
Is there a callback for when the main window is ready? I tried the 'ready-to-show' event mentioned in the docs but it did not work.

A listener on "mainWindow" doesn't worked for me. I used instead "mainWindow.webContents".
mainWindow.webContents.once('dom-ready', () => {});

Have a look at the did-finish-load event mentioned in the Electron browser-window documentation.
mainWindow.once('did-finish-load', () => {
// Send Message
})
There seems to be a dom-ready event too.

not mentioned in the previous answers, loadURL returns a promise that resolves at the same time the 'did-finish-load' event is fired; i.e., they're essentially equivalent, except one's a promise, and the other's a callback.

Check this: https://github.com/electron/electron/blob/master/docs/api/web-contents.md
You can use this event to know if your windows is ready in you main.js [CASE 1], but if want to know when your page is full loaded you should add an event in your index.html [CASE 2] and then you can attach a function that send a message to his parent Main.js telling him, he is ready, using IPCrenderer and IPCmain
CASE 1
main.js:
mainWindows.webContents.on('did-finish-load',WindowsReady);
function WindowsReady() {
console.log('Ready');
}
CASE 2
html:
<script>
const {ipcRenderer} = require('electron');
document.addEventListener('DOMContentLoaded',pageLoaded);
function pageLoaded(){
alert('The page is loade');
ipcRenderer.send('Am_I_Ready',"Im ready");
}
</script>
Main.js:
const {ipcMain} = electron;
ipcMain.on('Am_I_Ready', doSomething)
function doSomething(){
console.log('Everything is ready.');
}

Use mainWindow.webContents like this:
mainWindow.webContents.on('did-finish-load', () => {
mainWindow.webContents.send('message', 'hello world');
}

I tried the following code in my app
window.webContents.once("did-finish-load", () => {
console.log("did-finish-load");
});
window.webContents.once("dom-ready", () => {
console.log("dom-ready");
});
window.once("ready-to-show", () => {
console.log("ready-to-show");
});
This is after loading an index.html from local file system:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Hi mom</title>
<script defer src="renderer.js"></script></head>
<body>
<div id="renderer"></div>
</body>
</html>
According to the console.log output they fired in the following sequence:
dom-ready
ready-to-show
did-finish-load
Therefore did-finish-load is probably the one to wait for -- because it's the latest therefore presumably the most-fully-loaded.
Also the API documentation for webContents.send includes this example:
// In the main process.
const { app, BrowserWindow } = require('electron')
let win = null
app.whenReady().then(() => {
win = new BrowserWindow({ width: 800, height: 600 })
win.loadURL(`file://${__dirname}/index.html`)
win.webContents.on('did-finish-load', () => {
win.webContents.send('ping', 'whoooooooh!')
})
})
If I remove the loading of an external script file ...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Hi mom</title>
<!-- <script defer src="renderer.js"></script> -->
</head>
<body>
<div id="renderer"></div>
</body>
</html>
... then the order of events is changed slightly ...
dom-ready
did-finish-load
ready-to-show
... which may explain why some of the other answers to this question contract each other.

These days, you use the "ready-to-show" event.
https://electronjs.org/docs/api/browser-window#using-ready-to-show-event

Related

How do I display console log in electron app

I'm currently creating a JS Desktop App using Electron. I'm able to get everything functional how I want it, but I want to be able to update the users on certain tasks and also display errors in the app itself.
Is there any way to add a section (terminal if you will) or something similar inside the UI, so I can log things out to the user?
Providing application feedback to your user is as simple as having a statusUpdate function in your main process send
a message (via your preload.js script) to your render process. Within your render process, listen for a message on the
assigned channel and once received, update the content of your DOM element.
main.js (main process)
// Import required electron modules
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
// Import required Node modules
const nodePath = require('path');
// Prevent garbage collection
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();
statusTest();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ---
function statusTest() {
let counter = 1;
let message;
setInterval(() => {
statusUpdate(`Status message ${counter} from main process.`);
counter++;
}, 1000);
}
function statusUpdate(message) {
window.webContents.send('statusMessage', message);
}
preload.js (main process)
// Import the necessary Electron modules
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// Exposed protected methods in the render process
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods
'ipcRenderer', {
// From main to render
statusMessage: (message) => {
ipcRenderer.on('statusMessage', message);
}
}
);
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Electron Test</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
</head>
<body>
<label for="status">Status: </label>
<textarea id="status" cols="40" rows="10" disabled></textarea>
</body>
<script>
let status = document.getElementById('status');
window.ipcRenderer.statusMessage((event, message) => {
status.value += message + '\n'; // Append message
status.scrollTop = status.scrollHeight; // Show last entry
});
</script>
</html>

browser window not receiving focus in electron

this problem happen after i update dependency "electron": "^5.0.13" to "electron": "^12.0.6" before this it was working fine.
it is hard to say what is a problem so I am telling you how to recreate
press p (index.html mousetrap.js), it will open the print window(window 10)
after returning from the print window dialog, the browser window did not receive focus (i think) if I try to select text in the browser it highlight with gray color rather than blue (before press p when I select text it highlight with blue)
press p,w, etc... are not working
but press esc working
if I try to minimize the window and then maximize then it starts working normally (selected text turn blue again)
I check when main.js print code return
success => false
errorType => failed
window works fine but if it returns
success => false
errorType => cancelled
or return with
success => true
errorType =>
the window does not received focus
main.js
...
//for print
ipcMain.on('print-to', function (event) {
printWindow.webContents.print({},(success, errorType) => {
console.log("success => ",success)
console.log("errorType => ",errorType)
})
})
...
index.html
<html class="no-js" lang="">
<head>
<!-- Insert this line above script imports -->
<script>
if (typeof module === 'object') {
window.module = module;
module = undefined;
}
</script>
<!-- normal script imports etc -->
<script src="jquery-3.3.1.js" type="text/javascript"></script>
<!-- Insert this line after script imports -->
<script>
if (window.module) module = window.module;
</script>
<script>
const electron = require('electron');
const {
ipcRenderer
} = electron;
var Mousetrap = require('mousetrap');
function printFun() {
ipcRenderer.send('print-to');
}
Mousetrap.bind('w', function () {
alert("w press")
});
Mousetrap.bind('m', function () {
alert('m press')
});
Mousetrap.bind('esc', function () {
ipcRenderer.send('close', 'printWindow');
});
Mousetrap.bind('p', function () {
printFun();
});
</script>
</head>
<body id="preview">
kdjklsf
</body>
</html>
Dependencies
"electron": "^12.0.6",
I have no idea what to do! anyone suggest what is wrong and how to solve this problem
Thank you.

Electron browser window

I have 2 BrowserWindow instances in my electron application, mainWindow and secondaryWindow. There is a button in mainWindow which when clicked should open secondaryWindow.
Now my issue is that I don't want to be able to click on anything in the mainWindow until the secondaryWindow is closed.
The closest I could get was to use mainWindow.hide() but this just completely hides the window, I want to still see the mainWindow while the secondaryWindow is active but it should be disabled / inactive.
Any suggestions?
There are 2 ways to open a child window:
1. from the main process:
You can open a child window from the main process. This is for example useful for a custom window in the menu.
Here you can use the constructor to make it a child of parent. If the attribute modal is true, the parent window will not be accessible until the child window is closed.
function createChild(parent, html) {
//creates modal window
child = new BrowserWindow({
width: 786,
height: 847,
parent: parent,
modal: true,
show: false
});
child.loadURL(html);
child.webContents.openDevTools();
child.once("ready-to-show", () => {
child.show();
});
}
2. from the renderer process
Now, we don't always want to send an event over the IPC to the main process just to open a child window, right?
We don't need to. We can use the open function on our window for that.
For example:
const button = document.querySelector('button')
button.addEventListener('click', e => {
self.open(`file://${__dirname}/child.html`)
})
To make this window a child of your parent and modal, you can register an eventlistener on the parent window:
parent.webContents.on(
"new-window",
(event, url, frameName, disposition, options, additionalFeatures) => {
Object.assign(options, {
parent: parent,
modal: true
});
}
);
With this, when window.open() is called on the parent window, it will open a modal child window.
Example
main.js
const { app, BrowserWindow } = require("electron");
let win;
function createWindow() {
win = new BrowserWindow({ width: 1000, height: 800 });
win.loadURL(`file://${__dirname}/index.html`);
win.webContents.openDevTools();
win.on("closed", () => {
win = null;
});
win.webContents.on(
"new-window",
(event, url, frameName, disposition, options, additionalFeatures) => {
Object.assign(options, {
parent: win,
modal: true
});
}
);
}
app.on("ready", createWindow);
index.html
<!DOCTYPE html>
<html>
<body>
<p>I am the parent, you can't touch me until you closed my child!</p>
<button>Open child!</button>
<script>
const button = document.querySelector('button')
button.addEventListener('click', e => {
self.open(`file://${__dirname}/child.html`)
})
</script>
</body>
</html>
child.html
<!DOCTYPE html>
<html>
<body>
I'm the child!
</body>
</html>
Update Electron 5 or higher
With electron 5 node integration was disabled in the renderer process by default for security reasons. Since this example uses __dirname (which is part of the node API) in the renderer process, we need to reintroduce it, because it is not available anymore. In this example I use a preload script for this purpose:
main.js
const { app, BrowserWindow } = require("electron");
let win;
function createWindow() {
win = new BrowserWindow({
width: 1000,
height: 800,
webPreferences: {
preload: `${__dirname}/preload.js`,
},
});
win.loadURL(`file://${__dirname}/index.html`);
win.webContents.openDevTools();
win.on("closed", () => {
win = null;
});
win.webContents.on(
"new-window",
(_event, _url, _frameName, _disposition, options, _additionalFeatures) => {
Object.assign(options, {
parent: win,
modal: true,
});
}
);
}
app.whenReady().then(createWindow).catch(console.error);
preload.js
window.__dirname = __dirname;
index.html
<!DOCTYPE html>
<html>
<body>
<p>I am the parent, you can't touch me until you closed my child!</p>
<button>Open child!</button>
<script>
const button = document.querySelector("button");
button.addEventListener("click", (e) => {
self.open(`file://${__dirname}/child.html`);
});
</script>
</body>
</html>
child.html
<!DOCTYPE html>
<html>
<body>
I'm the child!
</body>
</html>

Communicating with a shared worker in Dart

I'm trying to communicate with a shared worker from Dart, but I haven't had any success and I've tried everything I can think of.
As a test, I'm just trying to get a basic worker which simply responds with whatever it is sent working.
Starting from the HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="scaffolded-by" content="https://github.com/google/stagehand">
<title>wtf</title>
<link rel="stylesheet" href="styles.css">
<script defer src="main.dart" type="application/dart"></script>
<script defer src="packages/browser/dart.js"></script>
<!-- <script defer src="test.js"></script> -->
</head>
<body>
<div id="output"></div>
</body>
</html>
JavaScript worker (worker.js):
onconnect = function(e) {
var port = e.ports[0];
port.onmessage = function(e) {
port.postMessage("Worker received message: " + e.data);
}
}
The Dart code (main.dart):
import 'dart:html';
import 'dart:async';
void main() async {
var myWorker = new SharedWorker("worker.js");
myWorker.port.onMessage.listen((msg) {
print("Worker says: " + msg);
});
new Timer.periodic(const Duration(seconds: 1), (timer) {
myWorker.port.postMessage("Hello, world");
});
}
The writer does get invoked every second, and I've verified that the shared worker does run (with chrome://inspect).
To narrow the problem down I threw together a JavaScript version of the Dart code (in test.js) which works as expected. If you comment out the contents of main() and uncomment the reference in the HTML to test.js it works. Contents of test.js:
var myWorker = new SharedWorker("worker.js");
myWorker.port.onmessage = function(e) {
console.log("Worker says: " + e.data);
}
setInterval(function() {
myWorker.port.postMessage("Hello, world");
}, 1000);
However, when using the Dart version nothing is written to the console. There are no errors at either compile-time or run-time. It just silently fails.
Any help would be greatly appreciated. I'm probably missing something stupid, but I've been stuck on this for longer than I care to admit…
Since the worker is a SharedWorker, you should call the method:
myWorker.port.start();
just after attach the onMessage listener, (documentation at MDN):
If the onmessage event is attached using addEventListener, the port is manually started using its start() method
Also, at the listener, the parameter msg is a MessageEvent and the text can be retrieved by accessing data property (same as your JS code):
print("Worker says: " + msg.data);
Follow the whole code with the changes:
import 'dart:html';
import 'dart:async';
void main() async {
var myWorker = new SharedWorker("worker.js");
myWorker.port.onMessage.listen((msg) {
print("Worker says: " + msg.data);
});
myWorker.port.start();
new Timer.periodic(const Duration(seconds: 1), (timer) {
myWorker.port.postMessage("Hello, world");
});
}
tested with Dart 1.19.1

How to add data in data-content tag of popover at run time?

I am using Boostrap in MVC application.
I am trying to add the data to display in popover run time.
but its not working...
I have this code...
<html>
<body>
hover for popover
</body>
</html>
<script>
$(function () {
$("#example").popover("Adding data run time here...");
});
</script>
Use the .attr() function:
$("#example").attr("data-content", "Adding data run time here...");
I found the answer...
<html>
<body>
hover for popover
</body>
</html>
<script>
$(function () {
$("#example").attr("data-content", getData()).popover();
});
function getData()
{
return "Here is the runtime data";
}
</script>
hover for popover
$("#example").popover({
placement: 'top',
content: 'Add data here',
});

Resources