How to dynamically change css style properties with Node and Electron - javascript

I've got the following problem: I want to access the css properties of styles.css inside Electron. The problem is that I can't use document.getElementsByClassName() because there is no document in Node. The desired behaviour is to change the color of one div once the q key is pressed.
This is my code:
index.js
const url = require('url');
const path = require('path');
const {app, BrowserWindow, globalShortcut} = require('electron');
let mainWindow;
app.on('ready', function(){
// Create new window
mainWindow = new BrowserWindow({backgroundColor: '#000000', fullscreen : true, frame : false});
// Load html in window
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes:true
}))
globalShortcut.register('Esc', () => {
app.quit();
});
globalShortcut.register('q', () => {
leftLight();
});
});
//This doesn't work
function leftLight() {
var element = ;
element.style["background-color"] = "yellow";
}
index.html
<!DOCTYPE html>
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<div class = rect_green> <h2 class=blocktext >LEFT FENCER</h2></div>
<div class = rect_red><h2 class=blocktext> RIGHT FENCER</h2> </div>
<div class = crono> <h2 class=blocktext>3:00</h2></div>
</body>
</html>
styles.css
.rect_green {
display: flex;
align-items: center;
height: 400px;
width:60%;
background-color: green;
position:relative;
top:100px;
text-align: center;
}
.rect_red {
display: flex;
align-items: center;
height:400px;
width:60%;
background-color: red;
position:relative;
top:120px;
float:right;
}
.crono {
display: flex;
align-items: center;
height:300px;
width:40%;
background-color: beige;
position:fixed;
left: 50%;
bottom : 50px;
transform: translate(-50%, 0px);
margin: 0 auto;
}
.blocktext {
margin-left: auto;
margin-right: auto;
font-family: "Palatino", Times, serif;
font-size: 180px;
}
Edit
After the modifications suggested by Gr8Miller (still no communication):
index.html
<!DOCTYPE html>
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<div class = rect_green> <h2 class=blocktext >LEFT FENCER</h2></div>
<div class = rect_red><h2 class=blocktext> RIGHT FENCER</h2> </div>
<div class = crono> <h2 class=blocktext>3:00</h2></div>
</body>
<script type="text/javascript">
var ipc = require('electron').ipcRenderer;
ipc.on('key-pressed-q', (e) => {
//var element = document.getElementsByClassName("rect_green");
//element.style["background-color"] = "yellow";
console.log("q pressed in html file");
});
</script>
</html>
And index.js
const url = require('url');
const path = require('path');
const {app, BrowserWindow, globalShortcut, ipcMain, webContents} = require('electron');
let mainWindow;
app.on('ready', function(){
// Create new window
mainWindow = new BrowserWindow({
backgroundColor: '#000000',
fullscreen : true,
frame : false,
icon : __dirname + "/res/icon.jpg",
webPreferences: {
nodeIntegration : true
}
});
// Load html in window
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes:true
}))
globalShortcut.register('Esc', () => {
app.quit();
});
globalShortcut.register('q', () => {
leftLight();
});
});
function leftLight() {
mainWindow && mainWindow.webContents.send('key-pressed-q');
console.log("Sending q pressed to html...");
}

Possible duplicate of this.
Try to create another js file containing your code and load it as a script from your html document.
Add this to your index.html:
<script type="text/javascript" charset="utf8" src="./pageScript.js"></script>
And create a separate pageScript.js file with the code you want:
window.onload = function () {
// your code here
function leftLight() {
var element = ;
element.style["background-color"] = "yellow";
}
// Also don't forget to call the function
leftLight();
}

View related tasks should be handled in render process but not main process.
in Electron, the entry js(index.js in your case) runs in main process (it acts manager of all browser windows it creates) and the browser window itself runs in render process. html elements and imported/embedded js "live"/"run" in browser windows(in render process), so document can only be directly accessed in render process.
in your case. the style change task should be done in render process:
send a message(e.g. key-pressed-q) to render process from the main process on key q is pressed:
change the style in render process on receiving message (key-pressed-q):
index.js
mainWindow = new BrowserWindow({
backgroundColor: '#000000',
fullscreen : true,
frame : false,
webPreferences: {
nodeIntegration: true
}});
...
function leftLight() {
mainWindow && mainWindow.webContents.send('key-pressed-q');
}
index.html
...
<script type="text/javascript">
var ipc = require('electron').ipcRenderer;
ipc.on('key-pressed-q', (e) => {
console.log(e);
var element = ;
element.style.backgroundColor = "yellow";
});
</script>
...
Added in 2019-11-18
There are other errors need to be fixed in your code which is not related to electron but just html basics:
//var element = document.getElementsByClassName("rect_green");
//element.style["background-color"] = "yellow";
getElementsByClassName returns an array of Element(Array<Element>) but not a single Element.
element.style doesn't have a field named background-color, it should be backgroundColor.
the console.log in the render process won't print logs in the main process's console, it outputs to its hosting browser window's own console. if you want to check the log, you have to open the browser window's Devtools first.
// in your `index.js`
// Open the DevTools.
mainWindow.webContents.openDevTools(); // `console.log` in `index.html` output to its hosting browser window's own console.
// in your `index.html`
var ipc = require('electron').ipcRenderer;
ipc.on('key-pressed-q', (e) => {
var element = document.querySelector(".rect_green");
element.style.backgroundColor = "yellow";
console.log("q pressed in html file"); // this won't output to the main process's console.
});

Change your index.html to this :
<!DOCTYPE html>
<html lang="en">
<meta name="viewport" content="width=device-width, initial-scale=1">
<head>
<link rel="stylesheet" href="styles.css">
<title>Document</title>
</head>
<body>
<div class = rect_green> <h2 class=blocktext >LEFT FENCER</h2></div>
<div class = rect_red><h2 class=blocktext> RIGHT FENCER</h2> </div>
<div class = crono> <h2 class=blocktext>3:00</h2></div>
<script>
function leftLight() {
const element = document.getElementsByClassName("yourclassname")
element[0].style.color = "red"
}
window.onkeydown = function(e) {
if (e.key == "q") {
leftLight()
}
}
</script>
</body>
</html>

Related

MindAR JS: MINDAR.IMAGE.MindARThree is not a constructor

I am learning Web AR development using MindAR, https://hiukim.github.io/mind-ar-js-doc/#:~:text=MindAR%20is%20an%20opensource%20web,are%20written%20for%20AFRAME%20integration. I am following a tutorial on Udemy, https://www.udemy.com/course/introduction-to-web-ar-development/learn/lecture/29791078#overview. But it is not working unfortunately for me but for the instructor.
This is my index.html page.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AR Research</title>
<script src="https://cdn.jsdelivr.net/npm/mind-ar#1.1.5/dist/mindar-image.prod.js"></script>
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mind-ar#1.1.5/dist/mindar-image-aframe.prod.js"></script>
<script src="./main.js" type="module">
</script>
<style>
html, body {
position: relative;
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
</body>
</html>
As you can see there is not much in the code. I basically embedded the required Mind AR libraries through CDN and add the css styling a little bit and reference my main.js file.
This is my main.js file.
document.addEventListener(`DOMContentLoaded`, () => {
const start = async () => {
// create the AR world and specify the marker
const mindarThree = new window.MINDAR.IMAGE.MindARThree({
container: document.body,
imageTargetSec: './groot.mind'
})
const { renderer, scene, camera } = mindarThree;
const geometry = new THREE.PlaneGeometry(1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff, transparent: true, opacity: 0.5 });
const plane = new THREE.Mesh(geometry, material);
const anchor = mindarThree.addAnchor(0);
anchor.group.add(plane);
await mindarThree.start();
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
})
}
start();
})
When I run the code on the browser, I am getting the following error.
main.js:4 Uncaught (in promise) TypeError: MINDAR.IMAGE.MindARThree is not a constructor
at start (main.js:4:29)
at HTMLDocument.<anonymous> (main.js:24:5)
What is wrong with my code and how can I fix it?
I had the same problem. The solution is that a different MindAR package needs to be imported, the one that combines MindAR and Three.js:
Use this one:
https://cdn.jsdelivr.net/npm/mind-ar#1.1.5/dist/mindar-image-three.prod.js
Not this one:
https://cdn.jsdelivr.net/npm/mind-ar#1.1.5/dist/mindar-image.prod.js

How to use electron tabs webview as to communicate from browser window to tabs?

I am trying to have my communicate between different rendering processes in electron. Rather than have multiple windows I am using electron-tabs which can be found here. To start I simply want my main window to be able to send a message using ipcRenderer to each tab. With multiple windows, you could store each window in a global variable like this stack overflow question and then use the web-contents to send a message.
UPDATE: In the electron-tabs documentation, it suggests you can use the tabs webview the same as you would a windows web-contents (see here). Unfortunately, either I am missing something or it is a little more complicated.
For the code: I have a main window mainWindow.html
<html>
<head></head>
<body>
<div class="etabs-tabgroup" >
<div class="etabs-tabs" ></div>
<div class="etabs-buttons" style="padding:5px 0px"></div>
</div>
<div class="etabs-views"></div>
<link rel="stylesheet" href="node_modules/electron-tabs/electron-tabs.css">
<script>
const TabGroup = require('electron-tabs') ;
const electron = require('electron') ;
const {ipcRenderer} = electron;
var tabs = [];
// Create a new tabGroup
let tabGroup = new TabGroup();
// Add 3 tabs to tabGroup
for (var i = 0; i <3;i++){
tabs.push(tabGroup.addTab({
src: 'file://' + __dirname + '/tab.html',
webviewAttributes: {
nodeintegration: true
}
}));
// Send test message to tabs... !!!! DOES NOT WORK !!!!
circuitTabs[i].webview.send('testMessage',i);
}
</script>
</html>
tab.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
const electron = require('electron') ;
const {ipcRenderer} = electron;
const { remote } = require('electron');
ipcRenderer.on ('testMessage', (event, i) => { alert(`Message from main window to tab ${i}`); });
</script>
</body>
</html>
main.js
const electron = require('electron');
const {app, BrowserWindow} = electron;
let mainWindow = null;
app.on('ready', function () {
mainWindow = new electron.BrowserWindow({width: 1200,height: 800,
webPreferences: {
nodeIntegration: true,
webviewTag: true
}
});
mainWindow.loadURL(path.join(__dirname, '/mainWindow.html'));
mainWindow.on('ready-to-show', function () {
mainWindow.show();
mainWindow.focus();
});
Try following this:
<webview>.send(channel, ...args)
channel String
...args any[]
Returns Promise<void>
Send an asynchronous message to renderer process via channel, you can also send arbitrary arguments. The renderer process can handle the message by listening to the channel event with the ipcRenderer module.
See webContents.send for examples.
Look at the docs below for more information.
https://www.electronjs.org/docs/api/webview-tag#webviewsendchannel-args
I figured it out. You can get the webcontentsID from the tab and use ipcRenderer.sendTo function.
For the code: I have a main window mainWindow.html
<html>
<head></head>
<body>
<div class="etabs-tabgroup" >
<div class="etabs-tabs" ></div>
<div class="etabs-buttons" style="padding:5px 0px"></div>
</div>
<div class="etabs-views"></div>
<link rel="stylesheet" href="node_modules/electron-tabs/electron-tabs.css">
<script>
const TabGroup = require('electron-tabs') ;
const electron = require('electron') ;
const {ipcRenderer} = electron;
var tabs = [];
// Create a new tabGroup
let tabGroup = new TabGroup();
// Add 3 tabs to tabGroup
for (var i = 0; i <3;i++){
tabs.push(tabGroup.addTab({
src: 'file://' + __dirname + '/tab.html',
webviewAttributes: {
nodeintegration: true
}
}));
// Send test message to tabs...
var webContentsID = tabs[i].webview.getWebContentsId();
ipcRenderer.sendTo(webContentsID,'testMessage');
}
</script>
</html>
tab.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
const electron = require('electron') ;
const {ipcRenderer} = electron;
const { remote } = require('electron');
ipcRenderer.on ('testMessage', (event, i) => { alert(`Message from main window to tab ${i}`); });
</script>
</body>
</html>
main.js
const electron = require('electron');
const {app, BrowserWindow} = electron;
let mainWindow = null;
app.on('ready', function () {
mainWindow = new electron.BrowserWindow({width: 1200,height: 800,
webPreferences: {
nodeIntegration: true,
webviewTag: true
}
});
mainWindow.loadURL(path.join(__dirname, '/mainWindow.html'));
mainWindow.on('ready-to-show', function () {
mainWindow.show();
mainWindow.focus();
});

How can I embed 2 tableau dashboards in 1 website?

Currently, based on JavaScript, I have embedded one tableau dashboard in a website, code is shown below. Now, I want to embed 2 dashboard in a same website (this second dashboard is from another tableau publish web link like the one I am using), how could I do it? Meanwhile, I need to create something like 'switch' button to allow me switch to different dashboard contents on click. Can anyone please help me with this?
Current code:
<!DOCTYPE html>
<html>
<head>
<title>Basic Embed</title>
<script type="text/javascript"
src="https://public.tableau.com/javascripts/api/tableau-2.min.js"></script>
<script type="text/javascript">
function initViz() {
var containerDiv = document.getElementById("vizContainer"),
url = "https://us-east-1.online.tableau.com/t/zuar/views/Regional/GlobalTemperatures",
options = {
hideTabs: true,
onFirstInteractive: function () {
console.log("Run this code when the viz has finished loading.");
}
};
var viz = new tableau.Viz(containerDiv, url, options);
// Create a viz object and embed it in the container div.
}
</script>
</head>
<body onload="initViz();">
<div id="vizContainer" style="width:800px; height:700px;"></div>
</body>
</html>
You could use a simple js-toggle func and toggle between the dashboards or whatever content you want to have. Just replace the text-content of the respective divs with the viz-containers of your dashboards..
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript"
src="https://public.tableau.com/javascripts/api/tableau-2.min.js">
</script>
<script>
function toggle() {
var x1 = document.getElementById("dashboard1");
var x2 = document.getElementById("dashboard2");
var bl = document.getElementById("button1");
if (x1.style.display === "none") {
x1.style.display = "block";
x2.style.display = "none";
bl.firstChild.data = "To Data B";
} else {
x1.style.display = "none";
x2.style.display = "block";
bl.firstChild.data = "To Data A";
}
}
function initialize() {
var containerDiv1 = document.getElementById("dashboard1"),
url1 = "https://public.tableau.com/views/liveintegration1/Dashboard1",
options1 = {
hideTabs: true,
onFirstInteractive: function () {
console.log("Run this code when the viz has finished loading.");
}
};
var viz1 = new tableau.Viz(containerDiv1, url1, options1);
// Create a viz object and embed it in the container div.
var containerDiv2 = document.getElementById("dashboard2"),
url2 ="https://public.tableau.com/views/LearnEmbeddedAnalytics/SalesOverviewDashboard",
options2 = {
hideTabs: true,
onFirstInteractive: function () {
console.log("Run this code when the viz has finished loading.");
}
};
var viz2 = new tableau.Viz(containerDiv2, url2, options2);
}
</script>
<style>
#dashboard1 {
border: 1px solid green;
width:800px;
height:700px;
width: 100%;
margin-top: 20px;
}
#dashboard2 {
border: 1px solid blue;
width:800px;
height:700px;
width: 100%;
margin-top: 20px;
}
</style>
</head>
<body onload="initialize()">
<button id="button1" onclick="toggle()">To Data B</button>
<div id="dashboard1" style="width:800px; height:700px;"></div>
<div id="dashboard2" style="width:800px; height:700px;"></div>
</body>
</html>
When inspecting the page you can now see the iframe being imported to the empty div:

Can't use a class from a script in another script

This is a basic problem I guess, but I can't find what the problem is. I have two scripts, one "main" and one containing a basic class, I try to instantiate the class in the main script like this:
Script.js
window.onload = function() {
var car = new Card('Ressources/Sprites/Axel.png', [50, 50]);
}
Card.js
class Card {
constructor(path, position) {
this.position = position;
this.texture = PIXI.Texture.from(sprite);
this.card = PIXI.Sprite(texture);
this.setPosition(position);
}
function setPosition(position){
card.x = position.x;
card.y = position.y;
}
}
Index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World</title>
</head>
<style>* {padding: 0; margin: 0}</style>
<script src="Ressources/Libraries/pixi.min.js"></script>
<script src="Scripts/Card.js"></script>
<script src="script.js"></script>
<body>
</body>
</html>
In the console I have this error :
Uncaught ReferenceError: Card is not defined
at window.onload

Rewrite module scripts to classic scripts in JavaScript

I developed a game using module scripts in JavaScript. Basically import and export statement inside my code. The game is only available running local server but I want it available through index.html. If I open it using index.html it throws
Access to Script at path from origin 'null' has been blocked by CORS policy: Invalid response. Origin 'null' is therefore not allowed access.
<!DOCTYPE html>
<html>
<head>
<title>My Game</title>
<style>
body{
margin: 0;
height: 100%;
width: 100%;
}
#screen {
display: block;
height: 100vh;
width: 100vw;
margin: 0;
image-rendering: pixelated;
}
</style>
<script type="module" src="js/main.js"></script>
</head>
<body>
<canvas id="screen" width="250" height="330"></canvas>
</body>
</html>
I think I need to rewrite my JS scripts to classic scripts.
Main.js
import Camera from './Camera.js';
import Entity from './Entity.js';
import PlayerController from './traits/PlayerController.js';
import Timer from './Timer.js';
import { loadFont } from './loaders/font.js';
import { loadEntities } from './entities.js';
import { createLevelLoader, createRandomEntity } from './loaders/level.js';
import { setupKeyboard } from './input.js';
import { createCollisionLayer } from './layers/collision.js';
import { createDashboardLayer } from './layers/dashboard.js';
import { createGameOverLayer } from './layers/gameover.js';
function createPlayerEnv(playerEntity) {
const playerEnv = new Entity();
const playerControl = new PlayerController();
playerControl.checkpoint.set(64, 64);
playerControl.setPlayer(playerEntity);
playerEnv.addTrait(playerControl);
return playerEnv;
}
async function main(canvas) {
const context = canvas.getContext('2d');
const [entityFactory, font] = await Promise.all([loadEntities(), loadFont()]);
const loadLevel = await createLevelLoader(entityFactory);
const level = await loadLevel('level');
const camera = new Camera();
const user = entityFactory.user();
const playerEnv = new createPlayerEnv(user);
level.entities.add(playerEnv);
//Bounding Box:
level.comp.layers.push(createCollisionLayer(level));
level.comp.layers.push(createDashboardLayer(font, playerEnv));
timer.start();
}
const canvas = document.getElementById('screen');
main(canvas);
Script with exports
import Keyboard from './KeyboardState.js';
export function setupKeyboard(pacman) {
const input = new Keyboard();
input.addMapping('KeyP', keyState => {
if (keyState) {
pacman.jump.start();
} else {
pacman.jump.cancel();
}
});
input.addMapping('KeyO', keyState => {
pacman.turbo(keyState);
});
input.addMapping('KeyD', keyState => {
pacman.go.dir += keyState ? 1 : -1;
});
input.addMapping('KeyA', keyState => {
pacman.go.dir += keyState ? -1 : 1;
});
return input;
}
Could you provide an example on how to rewrite module scripts to classic scripts? Thanks!

Categories

Resources