ElectronJS is an Open Source Framework used for building Cross-Platform native desktop applications using web technologies such as HTML, CSS, and JavaScript which are capable of running on Windows, macOS, and Linux operating systems. It combines the Chromium engine and NodeJS into a Single Runtime. To make desktop applications more attractive and engaging for the users, developers in addition to the pre-defined CSS would also like to provide a feature wherein the user can control the look and feel of the application and change the styling of the application dynamically during execution. For example, changing the theme of the application, adding animations to elements on the fly, etc. Electron provides a way by which we can successfully add Dynamic styling to the contents of the page using the Instance methods and events of the built-in BrowserWindow object and the webContents property. This tutorial will demonstrate how to add dynamic styling to the contents of the page in Electron. We assume that you are familiar with the prerequisites as covered in the above-mentioned link. For Electron to work, node and npm need to be pre-installed in the system.
- Project Structure:
Example: We will start by building the basic Electron Application by following the given steps.
- Step 1: Navigate to an Empty Directory to setup the project, and run the following command,
npm init
- To generate the package.json file. Install Electron using npm if it is not installed.
npm install electron --save
- This command will also create the package-lock.json file and install the required node_modules dependencies. Once Electron has been successfully installed, Open the package.json file and perform the necessary changes under the scripts key. package.json:
{ "name": "electron-dynamic", "version": "1.0.0", "description": "Dynamic Styling in Electron", "main": "main.js", "scripts": { "start": "electron ." }, "keywords": [ "electron" ], "author": "Radhesh Khanna", "license": "ISC", "dependencies": { "electron": "^8.3.0" } }
- Step 2: Create a main.js file according to the project structure. This file is the Main Process and acts as an entry point into the application. Copy the Boilerplate code for the main.js file as given in the following link. We have modified the code to suit our project needs. main.js:
javascript
const { app, BrowserWindow } = require( 'electron' ) function createWindow () { // Create the browser window. const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true } }) // Load the index.html of the app. win.loadFile( 'src/index.html' ) // Open the DevTools. win.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. // This method is equivalent to 'app.on('ready', function())' app.whenReady().then(createWindow) // Quit when all windows are closed. app.on( 'window-all-closed' , () => { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== 'darwin' ) { app.quit() } }) app.on( 'activate' , () => { // On macOS 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() } }) // In this file, you can include the rest of your // app's specific main process code. You can also // put them in separate files and require them here. |
- Step 3: Create the index.html file, the index.js file and the index.css file within the src directory. We will also copy the boilerplate code for the index.html file from the above-mentioned link. We have modified the code to suit our project needs. index.html:
html
<!DOCTYPE html> <!DOCTYPE html> < html > < head > < meta charset="UTF-8"> < title >Hello World!</ title > /security#csp-meta-tag --> < meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" /> < link rel="stylesheet" type="text/css" href="index.css"> </ head > < body > < h1 >Hello World!</ h1 > We are using node < script > document.write(process.versions.node) </ script >, Chrome < script > document.write(process.versions.chrome) </ script >, and Electron < script > document.write(process.versions.electron) </ script >. <!-- Adding Individual Renderer Process JS File --> < script src="index.js"></ script > </ body > </ html > |
- Output: At this point, our basic Electron Application is set up. To launch the Electron Application, run the Command:
npm start
Dynamic Styling in Electron: The BrowserWindow Instance and webContents Property are part of the Main Process. To import and use BrowserWindow in the Renderer Process, we will be using Electron remote module. The webContents.insertCSS(css, options) Instance method Injects CSS dynamically into the current BrowserWindow page contents and returns a unique key for the inserted stylesheet. This method returns a Promise and it resolves to a String which represents the unique Key for the inserted CSS into the BrowserWindow page contents. The same unique Key can be later used to remove the CSS from the page contents using the webContents.removeInsertedCSS() method. It takes in the following parameters.
- CSS: String This value should not be empty. The css String consists of the CSS that you want to apply to the contents of the BrowserWindow page. The css String follows all the same rules as that of CSS3 except that it is declared in a String.
- options: Object (Optional) It takes in the following parameters,
- CSSOrigin: String (Optional) Values can be either user or author. Setting the value as user enables the developer to prevent external websites from overriding the CSS set by the developer. Default value is author.
The webContents.removeInsertedCSS(key) Instance method removes the Inserted CSS from the current page contents based on the unique Key representing the stylesheet which was returned by the webContents.insertCSS() method. It returns a void Promise and it resolves when the Removal of the CSS was successful. To get the current BrowserWindow Instance in the Renderer Process, we can use some of the Static Methods provided by the BrowserWindow object.
- BrowserWindow.getAllWindows(): This method returns an Array of active/opened BrowserWindow Instances. In this application, we have only one active BrowserWindow Instance and it can be directly referred from the Array as shown in the code.
- BrowserWindow.getFocusedWindow(): This method returns the BrowserWindow Instance which is focused in the Application. If no current BrowserWindow Instance is found, it returns null. In this application, we only have one active BrowserWindow Instance and it can be directly referred using this method as shown in the code.
index.html: Add the following snippet in that file.
html
< h3 >Dynamic Styling in Electron</ h3 > < button id="style">Change Theme of Page</ button > < button id="clear">Revert to Original</ button > |
Note: At this point, the index.css file is empty. index.js: Add the following snippet in that file.
javascript
const electron = require( 'electron' ); // Importing BrowserWindow from Main Process const BrowserWindow = electron.remote.BrowserWindow; var style = document.getElementById( 'style' ); let win = BrowserWindow.getFocusedWindow(); // let win = BrowserWindow.getAllWindows()[0]; var cssKey = undefined; var css = "body { background-color: #000000; color: white; }" style.addEventListener( 'click' , () => { win.webContents.insertCSS(css, { cssOrigin: 'author' }).then(result => { console.log( 'CSS Added Successfully' ) console.log( 'Unique Key Returned ' , result) cssKey = result; }). catch (error => { console.log(error); }); }); var clear = document.getElementById( 'clear' ); clear.addEventListener( 'click' , () => { if (cssKey) { win.webContents.removeInsertedCSS(cssKey) .then(console.log( 'CSS Removed Successfully' )). catch (error => { console.log(error); }); } }); |
Output: Note: If we have specified an external Stylesheet such as the index.css file or used inline-styling within the HTML document then the webContents.insertCSS() Instance method will add CSS in addition to the already existing styles. It will not be able to overwrite or change the CSS defined in the external Stylesheet or the inline-styling, if any, for the current BrowserWindow contents. index.css: Add the following snippet in that file.
html
html, body { background-color: lightgray; } |
The background-color property is defined in the index.css file and we are also dynamically setting it in the index.js file in the webContents.insertCSS() method with a different value. Hence as per behavior, there should be no change in the background color of the BrowserWindow page. If we run the above code in addition to this code snippet, we should see the following Output: Output: