Added a custom title bar to a frameless window in order to make the window draggable. The title bar shows, but eventListener won't fire:
main.js:
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600,
frame: false,
scrollbar: false,
webPreferences:{
nodeIntegration: true,
contextIsolation: false,
}
})
win.loadFile('index.html')
}
index.html:
<body>
<div class="window">
<div class="window-content">
<textarea>some content</textarea>
</div>
</div>
<script src="index.js"></script>
</body>
index.js:
let titleBar = document.createElement('div')
titleBar.style.width = "100%"
titleBar.style.height = "32px"
titleBar.style.backgroundColor = "#fff"
titleBar.style.position = "absolute"
titleBar.style.top = titleBar.style.left = 0
titleBar.style.webkitAppRegion = "drag"
titleBar.textContent = 'My App';
document.body.appendChild(titleBar)
// Nothing happens
titleBar.addEventListener("mouseover", () => {
console.log("hello")
})
Yes, it is possible to add an eventListener to a custom title bar used for window dragging in a frameless window, but it requires implementation of own mouse event code for dragging the window.
The issue with mouse events not triggering the eventListener is caused by
titleBar.style.webkitAppRegion = "drag"
which interferes with the mechanism of propagating the events and prevents the mouse events to be detected by the listener.
In other words it seems that in Electron you can't have both at the same time for same DOM-object: dragging using .style.webkitAppRegion = "drag"
and receiving of the mouse events using .addEventListener("mouseover",
. If you want the dragging behavior you need to implement it yourself using the from the DOM-object received mouse events.
To see it yourself comment or remove the "drag" line and experience that the mouse event is then working as expected ( don't forget that the console output is provided after Ctrl+Shift+i in the window of the application and not in the Terminal window running the application ).
Please be aware that using some "programming tricks" you can usually achieve any desired effect in any software system, so statements that something is not possible are generally not really correct from this perspective stating only that there is no straightforward simple and well known way of achieving something.
A possible workaround is to have two title bars (or a dragging icon next to the title bar in same line): one for dragging the window and the other one for responding to mouse events:
console.log("renderer.js loaded");
document.addEventListener('DOMContentLoaded', () => {
// Create the draggable title bar at the very top
let draggableTitleBar = document.createElement('div');
draggableTitleBar.style.width = "100%";
draggableTitleBar.style.height = "24px";
draggableTitleBar.style.backgroundColor = "#e74c3c";
draggableTitleBar.style.position = "absolute";
draggableTitleBar.style.top = "0px";
draggableTitleBar.style.left = "0px";
draggableTitleBar.style.zIndex = "1000";
draggableTitleBar.style.webkitAppRegion = "drag";
draggableTitleBar.textContent = 'Draggable Title Bar';
// Create the non-draggable title bar below it
let nonDraggableTitleBar = document.createElement('div');
nonDraggableTitleBar.style.width = "100%";
nonDraggableTitleBar.style.height = "40px";
nonDraggableTitleBar.style.backgroundColor = "#3498db";
nonDraggableTitleBar.style.position = "absolute";
nonDraggableTitleBar.style.top = "24px"; // Adjust position as needed
nonDraggableTitleBar.style.left = "0px";
nonDraggableTitleBar.style.zIndex = "1000";
nonDraggableTitleBar.textContent = 'Non-Draggable Title Bar';
// Add event listeners to the non-draggable title bar
nonDraggableTitleBar.addEventListener("mouseover", () => {
console.log("Mouseover event triggered on non-draggable title bar");
});
nonDraggableTitleBar.addEventListener("click", () => {
console.log("Non-draggable title bar clicked");
});
// Append both title bars to the document body
document.body.appendChild(draggableTitleBar);
document.body.appendChild(nonDraggableTitleBar);
console.log("Title bars added to the DOM");
});
Here how it looks like:
For the sake of completeness below code of an application which uses the title bar for both: dragging the window and reception of mouse events (logged to the console):
~ $ cat package.json
{
"name": "oOosysElectronApp",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"keywords": [],
"author": "oOosys",
"license": "---",
"description": "PureGraphicWindowArea",
"dependencies": {
"electron": "^31.1.0"
}
}
~ $ cat main.js
const { app, BrowserWindow, ipcMain } = require('electron');
// const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 480,
frame: false,
scrollbar: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
}
});
win.loadFile('index.html');
ipcMain.on('get-window-position', (event) => {
event.returnValue = win.getPosition();
});
ipcMain.on('set-window-position', (event, x, y) => {
// console.log("set-window-position to x,y : ", x , y) // prints to TERMINAL
win.setPosition(x, y);
});
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
~ $ cat renderer.js
const { ipcRenderer } = require('electron');
console.log("renderer.js loaded");
document.addEventListener('DOMContentLoaded', () => {
// Create the title bar
let titleBar = document.createElement('div');
titleBar.style.width = "100%";
titleBar.style.height = "32px";
titleBar.style.backgroundColor = "lightblue"; // Temporary background color for visibility
titleBar.style.position = "absolute";
titleBar.style.top = "0px";
titleBar.style.left = "0px";
titleBar.style.zIndex = "1000"; // Ensure the title bar is on top
titleBar.textContent = 'Window Dragging Title';
document.body.appendChild(titleBar);
console.log("Title bar added to the DOM");
let isDragging = false;
let startX, startY;
let startWindowX, startWindowY;
titleBar.addEventListener("mousedown", (event) => {
event.preventDefault();
event.stopPropagation();
isDragging = true;
startX = event.clientX;
startY = event.clientY;
const [windowX, windowY] = ipcRenderer.sendSync('get-window-position');
startWindowX = windowX;
startWindowY = windowY;
console.log("MouseDOWN windowX,Y, mouseX,Y:", startWindowX, startWindowY, startX, startY);
});
// mousemove event interferes somehow with
// ipcRenderer.send('set-window-position', newWindowX, newWindowY);
// causing the window to resize instead of re-position
// In other words CONTINUOUS DRAGGING of the window FAILS TO WORK as expected
document.addEventListener("mouseup", (event) => {
if ( isDragging ) {
const currentX = event.clientX;
const currentY = event.clientY;
const deltaX = currentX - startX;
const deltaY = currentY - startY;
const newWindowX = startWindowX + deltaX;
const newWindowY = startWindowY + deltaY;
ipcRenderer.send('set-window-position', newWindowX, newWindowY);
console.log("mouseUP newWindowX,Y mouseMovedDistXY:", newWindowX, newWindowY, deltaX, deltaY);
isDragging = false;
}
});
});
~ $ cat index.html
<!DOCTYPE html>
<html><head><title>oOosys Electron App</title>
<style>
body, html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #d0eed0;
}
</style></head>
<body>
<script src="renderer.js"></script>
<br><br><br><br><br><br><br><br><br>
<textarea>some content</textarea>
</body></html>
~ $ npm start 2>/dev/null
> oOosysElectronApp@1.0.0 start
> electron .
And here how it looks like after the code documented in the log of a shell session provided above is executed:
Please notice that the window moves to the target position on mouseup
event.
The mousemove
event seems to interfere with the functionality of ipcRenderer.send('set-window-position', newWindowX, newWindowY);
resulting in resizing the window instead of moving
so this approach seems not be able to provide smooth continuous dragging of the window due to an issue with Electron functionality.