How to send-retrieve information and manipulate the DOM from a webview with Electron Framework

How to send-retrieve information and manipulate the DOM from a webview with Electron Framework

Normally, instead of send information from an iframe to your main document, you may want to retrieve the entire DOM and do whatever you want with it. However, with electron framework you can't do that simply with iframe tags, as they're are not allowed normally. Instead you need to use the <webview> tag in your document to render third party (or maybe your own) websites in an electron application.

Although the use of webview is not a problem, it will be if you try to access to the DOM as you did it before normally with javascript on a iframe. You simply can't directly due to "security reasons" or something like that.

However, you are able to send information using the ipc-manager events and manipulate the DOM of the external website using the preload attribute of the webview.

How does it works

What you want to achieve will be possible thanks to the preload attribute of the webview which allow you to inject a script before the src is loaded, it specifies a script that will be loaded before other scripts run in the guest page. The protocol of script’s URL must be either file: or asar:, because it will be loaded by require in guest page under the hood. When the guest page doesn’t have node integration this script will still have access to all Node APIs, but global objects injected by Node will be deleted after this script has finished executing.

Implementation

As said before, we need to create a .js file that will help us to handle the messages that we'll send from the main view to our webview tag and that will contain the code that we'll use to manipulate the DOM of the document.

In this case our file will be inyector.js and it will contain the following code :

// inyector.js
// Get the ipcRenderer of electron
const {ipcRenderer} = require('electron');

// Do something according to a request of your mainview
ipcRenderer.on('request', function(){
    ipcRenderer.sendToHost(getScripts());
});

ipcRenderer.on("alert-something",function(event,data){
    alert(data);
});

ipcRenderer.on("change-text-element",function(event,data){
    // the document references to the document of the <webview>
    document.getElementById(data.id).innerHTML = data.text;
});

/**
 * Simple function to return the source path of all the scripts in the document
 * of the <webview>
 *
 *@returns {String}
 **/
function getScripts(){
    var items = [];
    
    for(var i = 0;i < document.scripts.length;i++){
        items.push(document.scripts[i].src);
    }
    
    return JSON.stringify(items);
}

Note: to send data from the <webview> to the main view, use the sendToHost method from the ipcRenderer as shown in the first method that sends a JSON string from the webview to the main view.

The logic is simple, an event listener with a custom name will react and will execute the function in the <webview> when the event is triggered from our main view. To trigger them from our main view, just use webview.send("identifier",data).

Remember that we need to add the route from the inyector.js to the preload attribute in the webview tag :

<webview id="myweb" src="http://ourcodeworld.com" preload="./inyector.js"></webview>

Note: you don't need to add the nodeintegration attribute directly to the webview as it will temporaly enabled and disabled when needed automatically.

Finally, handle the webview with javascript in your main js file :

var webview = document.getElementById("myweb");

// When everything is ready, trigger the events without problems
webview.addEventListener("dom-ready", function() {
    // Show devTools if you want
    //webview.openDevTools();
    console.log("DOM-Ready, triggering events !");
    
    // Aler the scripts src of the website from the <webview>
    webview.send("request");
    
    // alert-something
    webview.send("alert-something", "Hey, i'm alerting this.");
    
    // change-text-element manipulating the DOM
    webview.send("change-text-element",{
        id: "myelementID",
        text: "My text"
    });
});

// Process the data from the webview
webview.addEventListener('ipc-message',function(event){
    console.log(event);
    console.info(event.channel);
});

Isn't as simple as you did before with iframes normally, but it is not impossible as you can see.

Have fun !

This could interest you

Become a more social person