How do I access DOM using keyboard shortcuts in Electron - javascript

I have a 50x50 px div element and I want to change its background color when I hit Ctrl+K. How do I do it?
UPDATE : After searching around for a bit, I tried this code and it still isn't working
index.html
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>
</head>
<body>
<div id="d" style="background-color: red; height: 50px; width: 50px;"></div>
<script>
const {ipcRenderer} = require('electron')
ipcRenderer.on('changeCol', (event,color) => {
document.getElementById('d').style.backgroundColor = color;
})
</script>
</body>
</html>
Here's the main.js
const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const globalShortcut = electron.globalShortcut;
let mainWindow
function createWindow () {
mainWindow = new BrowserWindow({width: 800, height: 600})
mainWindow.loadURL(`file://${__dirname}/index.html`)
mainWindow.webContents.openDevTools()
mainWindow.on('closed', function () {
mainWindow = null
})
}
app.on('ready', function() {
createWindow();
// registered a shortcut for Ctrl+K
globalShortcut.register('Control+K', () => {
mainWindow.webContents.on('did-fininsh-load', () => {
mainWindow.webContents.send('changeCol','green');
})
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
if (mainWindow === null) {
createWindow()
}
})

Alright, so I figured out I can't modify DOM directly from the main.js file. We have to use webContents.send() and ipcRenderer to send and recieve asynchronous messages via channels. Here's some simple code which lets you modify a div's background color.
main.js
app.on('ready', function() {
globalShortcut.register('A', () => {
mainWindow.webContents.send('changeColor','green');
});
});
index.html
<script>
const {ipcRenderer} = require('electron')
ipcRenderer.on('changeColor', (event,col) => {
var element = document.getElementById('element');
element.style.color = col;
})
</script>

Probably by using the Accelerator Api
const {app, globalShortcut} = require('electron')
app.on('ready', () => {
// Register a 'Control+K' shortcut listener.
globalShortcut.register('Control+K', () => {
// Do stuff when K and Command is pressed.
})
})
Then just some simple javascript where it says // Do stuff when K and Command is pressed. This is going to depend how your HTML/CSS is set up and the exact results you're looking for.

Related

electron, how to make my custom title button work

I'm trying to make my custom title button but it seems like some error has occur-ed. I tried the method from this one but not working with error message.
main.js:
const { app, BrowserWindow } = require('electron');
const path = require('path');
const electronIpcMain = require('electron').ipcMain;
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
// eslint-disable-next-line global-require
if (require('electron-squirrel-startup')) {
app.quit();
}
const createWindow = () => {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1370,
height: 755,
resizable: false,
autoHideMenuBar: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
icon: path.join(__dirname, 'res/applogo.png'),
frame: false,
movable: false,
});
// and load the index.html of the app.
mainWindow.loadFile(path.join(__dirname, 'index.html'));
// Open the DevTools.
mainWindow.webContents.openDevTools();
};
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
//set applogo.png to every window
app.on("browser-window-created", (event, window) => {
window.setIcon(path.join(__dirname, 'res/applogo.png'))
});
//win btns
electronIpcMain.on('window:minimize', () => {
window.minimize();
})
electronIpcMain.on('window:maximize', () => {
window.maximize();
})
electronIpcMain.on('window:restore', () => {
window.restore();
})
electronIpcMain.on('window:close', () => {
window.close();
})
preload.js:
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'window:minimize', // Channel names
'window:maximize',
'window:restore',
'window:close'
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>my app</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div class="title-container">
<img src="res/applogo.png" style="width: 22px; height: 22px;">
<div style="width: 5px; height: 22px;"></div>
<p class="title-text">Phigros Fanmade - Editor - Chart Name</p>
<div class="title-button" id="min-btn">
<img src="res/icons/titleButton/minimize_button.png" style="width: 20px; height: 20px; margin: 1px;">
</div>
<div class="title-button" id="res-btn">
<img src="res/icons/titleButton/restore_button.png" style="width: 20px; height: 20px; margin: 1px;">
</div>
<div class="title-button" id="max-btn">
<img src="res/icons/titleButton/maximize_button.png" style="width: 20px; height: 20px; margin: 1px;">
</div>
<div class="title-button" id="clo-btn">
<img src="res/icons/titleButton/close_button.png" style="width: 20px; height: 20px; margin: 1px;">
</div>
</div>
<div class="application-container">
<!-- ... -->
</div>
<script>
//developer tool open command
document.querySelector("div[data-title='Developer Tool']").addEventListener('click', () => {
window.open('devTool.html', "_blank", "width=1200,height=714,resizable=false,autoHideMenuBar=true,frame=false");
})
</script>
<script src="index.js"></script>
</body>
<script>
//title buttons call commands
document.getElementById('min-btn').addEventListener('click', () => {
window.ipcRender.send('window:minimize');
});
document.getElementById('max-btn').addEventListener('click', () => {
window.ipcRender.send('window:maximize');
});
document.getElementById('res-btn').addEventListener('click', () => {
window.ipcRender.send('window:restore');
});
document.getElementById('clo-btn').addEventListener('click', () => {
window.ipcRender.send('window:close');
});
</script>
</html>
index.js is empty and index.css is just basic stylings.
Error Message:
A JavaScript error occurred in the main process
Uncaught Exception:
ReferenceError: window is not defined
at IpcMainImpl. (C:\Users...\src\main.js:64:3)
at IpcMainImpl.emit (node:events:527:28)
at EventEmitter. (node:electron/js2c/browser_init:161:11014)
at EventEmiiter.emit (node:events:527:28)
Thank you for reading so far, please help me if you're able to. If you need me to provide more info, I'll update it as soon as possible when I see it. Thank you.
The primary problem is the scope of window within your main.js file.
The variable window is used within your electronIpcMain.on() functions, but it is not declared prior. As a result, you receive the window is not defined at error message.
To rectify this, you will need to add let window; before the createWindow() const.
Additionally, I would just declare the createWindow object as a function and not a const. This will simplify things. Additionally, this createWindow() function should return the Electron window object, allowing you to manipulate it further at a late time should you need to do so, like when you are minimizing, maximizing, restoring and closing. Other manipulations may be x, y, width, height, etc.
I have simplified the below main.js file to only include the above stated changes and code needed for a minimum working example.
main.js (main process)
const { app, BrowserWindow } = require('electron');
const path = require('path');
const electronIpcMain = require('electron').ipcMain;
// Declare window in the (file) scope, so it can be accessed by other functions in this file
let window;
function createWindow() {
// This window const is function scoped, therefore not accessible outside this function
const window = new BrowserWindow({
width: 1370,
height: 755,
resizable: false,
autoHideMenuBar: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
frame: false,
movable: false,
});
window.loadFile(path.join(__dirname, 'index.html'))
// The loadFile() function returns a promise, so let's use it correctly below
.then(() => {window.webContents.openDevTools();})
// Return this function scoped window const
return window;
}
app.on('ready', () => {
// Assign the returned value of the createWindow() function to this (file) scoped variable.
window = createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
// Assign the returned value of the createWindow() function to this (file) scoped variable.
window = createWindow();
}
});
app.on('browser-window-created', (event, window) => {
window.setIcon(path.join(__dirname, 'res/applogo.png'));
});
electronIpcMain.on('window:minimize', () => {
// Now we can access the window variable
window.minimize();
})
electronIpcMain.on('window:maximize', () => {
// Now we can access the window variable
window.maximize();
})
electronIpcMain.on('window:restore', () => {
// Now we can access the window variable
window.restore();
})
electronIpcMain.on('window:close', () => {
// Now we can access the window variable
window.close();
})
Your preload.js script remains unchanged.
preload.js (main process)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'window:minimize',
'window:maximize',
'window:restore',
'window:close'
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Lastly, I have simplified the index.html file for a minimum reproducible example.
index.html (render process)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>my app</title>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
</head>
<body>
<div>
<div class="title-button" id="min-btn"> Minimize </div>
<div class="title-button" id="res-btn"> Restore </div>
<div class="title-button" id="max-btn"> Maximize </div>
<div class="title-button" id="clo-btn"> Close </div>
</div>
</body>
<script>
document.getElementById('min-btn').addEventListener('click', () => {
window.ipcRender.send('window:minimize');
});
document.getElementById('res-btn').addEventListener('click', () => {
window.ipcRender.send('window:restore');
});
document.getElementById('max-btn').addEventListener('click', () => {
window.ipcRender.send('window:maximize');
});
document.getElementById('clo-btn').addEventListener('click', () => {
window.ipcRender.send('window:close');
});
</script>
</html>

Electron - How to pass data to renderer from a new opened window?

I'm struggling to pass a data from newly opened site window to renderer. What I want to achieve is to find a login button on site and listen on click event. I was reading about webview in electron, but I couldn't make It work. For now I'm stuck on window.open() method. Can you please point me what aspect I am missing? Here are my files:
//main.js
const {app, BrowserWindow, ipcMain} = require('electron')
const path = require('path')
function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true
}
})
mainWindow.loadFile('index.html')
// Open the DevTools.
mainWindow.webContents.openDevTools()
}
app.whenReady().then(() => {
createWindow()
app.on('activate', function () {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
ipcMain.on("open-login-window", () => {
console.info('dostal');
});
//renderer.js
const electron = require("electron");
const ipc = electron.ipcRenderer
const button = document.getElementById("login-button");
button.addEventListener("click", () => {
ipc.send("open-login-window")
const windowProxy = window.open('https://www.somesite.com/login', null, 'minimizable=false')
windowProxy.postMessage('hi', '*')
});

Electron app opening all pages upon startup

I am setting up a multi-paged electron application, however whenever I start my application it opens all the pages, and anytime I click a button that is supposed to bring the user to the next page, it skips ahead and opens the other pages as well.
I believed the issue was with setting the parent/child relation of each window, however when commenting or removing those properties the issue persists.
const{app, BrowserWindow, ipcMain} = require('electron');
import{fstat} from 'fs';
import{resolve} from 'path';
const packagejson = require('../package.json')
app.commandLine.appendSwitch('touch-events', 'enabled');
if (require('electron-squirrel-startup')) {
app.quit();
}
let mainWindow;
let startWindow;
let setOriginsWindow;
const createWindow = () => {
startWindow = new BrowserWindow({
width: 800,
height: 600,
});
startWindow.loadURL(`file://${__dirname}/index.html`);
startWindow.webContents.openDevTools();
startWindow.on('closed', () => {
startWindow = null;
});
};
app.on('ready', createWindow);
ipcMain.on('set-origins', (event) => {
setOriginsWindow = new BrowserWindow({
})
setOriginsWindow.on('close', function() {setOriginsWindow = null});
setOriginsWindow.loadURL(`file://${__dirname}/set_origin_page.html`)
setOriginsWindow.once('ready-to-show', () => {
setOriginsWindow.show();
});
setOriginsWindow.openDevTools();
if(packagejson.ENV == "dev"){
setOriginsWindow.openDevTools();
}
})
// parent:startWindow,
// fullscreen: true,
// modal:true,
// show:false
ipcMain.on('start-procedure', (event) => {
mainWindow = new BrowserWindow({
})
mainWindow.on('close', function () {mainWindow = null});
mainWindow.loadURL(`file://${__dirname}/main_page.html`);
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
mainWindow.openDevTools();
if(packagejson.ENV == "dev"){
mainWindow.openDevTools();
}
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (startWindow === null) {
createWindow();
}
});
'''
The 1st page to open should be a splashscreen with button that when pressed, will open page 2. Page 2 has a button that when pressed will open page 3.
Electron is not good for multipage apps, and displays a blank screen for around a quarter of a second when changing pages. Instead, you might want to create a file with all pages combined and switch between them with DOM methods
For Example
var splash_screen = document.getElementById('splash-screen');
var second_screen = document.getElementById('second-screen');
var third_screen = document.getElementById('third-screen');
document.removeChild(second_screen)
document.removeChild(third_screen)
var splash_button_click = () => {
document.removeChild(splash_screen);
document.appendChild(second_screen);
}

Run nightmare js script inside electron app

So I have this nightmarejs code I would like to execute(open new window and run the script) when you click a button inside an electron app. However I searched through the internet and nothing worked for me :/ (I have a mac)
var Nightmare = require('nightmare');
var nightmare = Nightmare({
electronPath: require('${__dirname}/node_modules/electron'),
show: true
});
nightmare
.goto('http://yahoo.com')
.type('form[action*="/search"] [name=p]', 'github nightmare')
.click('form[action*="/search"] [type=submit]')
.wait('#main')
.evaluate(function () {
return document.querySelector('#main .searchCenterMiddle li a').href
})
.end()
.then(function (result) {
document.getElementById("results").innerHTML = result;
})
.catch(function (error) {
console.error('Search failed:', error);
});
const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
title: "Dummy",
fullscreenable: false,
resizable: false,
alwaysOnTop: false,
width: 420,
height: 250,
'web-preferences': {
'web-security': false
}
})
mainWindow.loadURL(`file://${__dirname}/index.html`)
mainWindow.on('closed', function() {
mainWindow = null
})
}
app.on('ready', createWindow)
app.on('window-all-closed', function() {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function() {
if (mainWindow === null) {
createWindow()
}
})
Thanks,
Bertram
I don't see anything that will fire off the code you want to run.
If you gave it to me to make work, I'd do two things:
wrap the nightmare code in a function so you can
require("./mynighmare.js").sleepPoorly()
in your index.html, add a button that calls the above line to actually run your code.
... then I'd do a whole bunch of testing, because my first draft wouldn't work right :)

Can't pass an IPC message to a browserwindow

I'm messing around with electron, one of the things i'm trying to do is monitor a directory for file changes, and send the full path of the file that changed to a browser window via IPC to a renderer. I know the file watcher is working since it's logging to the console, but the path never makes it to the browserwindow.
Putting logging lines in the renderer never log, and remote debugging and stepping through the code, i can see it never fires off to the renderer.
Any idea? I'm new to this javascript stuff so it's probably something simple i'm missing. Thanks in advance!
main.js:
const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const ipc = electron.ipcMain
const watchfiles = require('./watchfiles')
const windows = []
app.on('ready', _ => {
[1].forEach(_ => {
let win = new BrowserWindow({
height: 400,
width: 400
})
win.loadURL(`file://${__dirname}/watchfiles.html`)
win.on('closed', _ => {
mainWindow = null
})
windows.push(win)
})
})
ipc.on('filewatch-start', _ => {
//Log
console.log('Filewatch started')
watchfiles(fullPath => {
windows.forEach(win => {
win.webContents.send('changedfiles', fullPath)
})
})
})
renderer.js:
const electron = require('electron')
const ipc = electron.ipcRenderer
document.getElementById('start').addEventListener('click', _ =>{
ipc.send('filewatch-start')
})
ipc.on('changedfiles', (event, message) => {
document.getElementById('changedfiles').innerHTML = message
})
watchfiles.js:
module.exports = function watchfiles(watchr) {
// Import the watching library
var watchr = require('watchr')
// Define our watching parameters
var path = process.cwd()
function listener (changeType, fullPath, currentStat, previousStat) {
switch ( changeType ) {
case 'update':
console.log('the file', fullPath, 'was updated', currentStat, previousStat)
break
case 'create':
console.log('the file', fullPath, 'was created', currentStat)
break
}
}
function next (err) {
if ( err ) return console.log('watch failed on', path, 'with error', err)
console.log('watch successful on', path)
}
// Watch the path with the change listener and completion callback
var stalker = watchr.open(path, listener, next)
// Close the stalker of the watcher
//stalker.close()
}
watchfile.html:
<html>
<head>
<link rel="stylesheet" href="watchfiles.css" />
</head>
<body>
<div id="container">
<h1 class="title">Watching files:</h1>
<div class="watchfiles" id="changedfiles"></div>
<button class="btn" id="start">Start</button>
</div>
<script>require('./renderer')</script>
</body>
</html>
Regarding only the IPC communication this simpler version works:
main.js
const { app, ipcMain, BrowserWindow } = require('electron')
const windows = []
app.on('ready', _ => {
/* ... */
})
ipcMain.on('filewatch-start', _ => {
console.log('Filewatch started')
windows.forEach(win => {
win.webContents.send('changedfiles', 'examplePath')
})
})
renderer.js
const { ipcRenderer } = require('electron')
window.onload = () => {
document.getElementById('start').addEventListener('click', _ =>{
ipcRenderer.send('filewatch-start')
})
ipcRenderer.on('changedfiles', (event, message) => {
document.getElementById('changedfiles').innerHTML = message
})
}
watchfile.html
<html>
<head>
<script type="text/javascript" src="./renderer.js"></script>
</head>
<body>
<div id="container">
<h1 class="title">Watching files:</h1>
<div class="watchfiles" id="changedfiles" />
<button class="btn" id="start">Start</button>
</div>
</body>
</html>
Regarding the overall design, you can do the job of watchfiles.js in main.js, no need to separate them:
main.js (compact)
const { app, ipcMain, BrowserWindow } = require('electron')
const watchr = require('watchr')
const path = process.cwd()
let windows = []
let stalker = null
const listener = (changeType, fullPath, currentStat, previousStat) => {
var msg = ''
console.log('change detected: ', changeType, currentStat, previousStat)
switch ( changeType ) {
case 'update':
msg = 'the file' + fullPath + ' was updated'
break
case 'create':
msg = 'the file' + fullPath + ' was created'
break
}
windows.forEach(win => {
win.webContents.send('changedfiles', msg)
})
}
const next = (err) => {
if ( err ) return console.log('watch failed on', path, 'with error', err)
console.log('watch successful on', path)
}
app.on('ready', () => {
[1].forEach(() => {
let win = new BrowserWindow({
height: 400,
width: 400
})
win.loadURL(`file://${__dirname}/watchfile.html`)
win.on('closed', _ => {
mainWindow = null
})
windows.push(win)
})
})
app.on('quit', () => {
if (stalker) stalker.close()
})
ipcMain.on('filewatch-start', _ => {
console.log('Filewatch started')
if (!stalker) {
stalker = watchr.open(path, listener, next)
}
})

Categories

Resources