Chrome-like findbar for your Electron application
Install the electron-findbar
package via npm:
npm install electron-findbar
The electron-findbar
is a BrowserWindow
component designed to emulate the Chrome findbar layout, leveraging the webContents.findInPage
method to navigate through matches. Inter-process communication (IPC) is used for interaction between the main
and renderer
processes.
To optimize memory usage, the Findbar window is created only when the findbar is open. The implementation is lightweight, including only essential code.
All public methods are documented with JSDoc and can be referenced during import.
To import the Findbar class:
const Findbar = require('electron-findbar')
You can pass a BrowserWindow
instance as a single parameter to use it as the parent window. The BrowserWindow.WebContents
will be used as the findable content:
// Create or retrieve the findbar associated to the browserWindow.webContents. If a new findbar is created, the browserWindow is used as parent.
const findbar = Findbar.from(browserWindow)
Alternatively, you can provide a custom WebContents
as the second parameter. In this case, the first parameter can be any BaseWindow
, and the second parameter will be the findable content:
// Create or retrieve the findbar associated to the webContents. If a new findbar is created, the baseWindow is used as parent.
const findbar = Findbar.from(baseWindow, webContents)
Is also possible to create a findbar without a parent window (even though it is not recommended):
// Create or retrieve the findbar associated to the webContents. If a new findbar is created, it will be displayed in the middle of the screen without a parent to connect to.
const findbar = Findbar.from(webContents)
Note: The findbar is ALWAYS linked to the webContents not the window. The parent is only the window to connect the events and stay on top. If the .from(webContents)
is used to retrieve an existing findbar previously created with a parent, the findbar will stay connected to the parent.
If there is no intention to create a new findbar in case it does not exist, use:
// Get the existing findbar or undefined.
const existingFindbar = Findbar.fromIfExists(browserWindow)
/* OR */
const existingFindbar = Findbar.fromIfExists(webContents)
You can customize the Findbar window options using the setWindowOptions
method:
findbar.setWindowOptions({ resizable: true, alwaysOnTop: true, height: 100 })
To handle the Findbar window directly after it is opened, use the setWindowHandler
method:
findbar.setWindowHandler(win => {
win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })
});
The findbar has a default position handler which moves the findbar to the top-right corner. To change the position handler, use the setBoundsHandler
method. The bounds handler is called when the parent window moves or resizes and provides both the parent and findbar bounds as parameters.
findbar.setBoundsHandler((parentBounds, findbarBounds) => ({
x: parentBounds.x + parentBounds.width - findbarBounds.width - 20,
y: parentBounds.y - ((findbarBounds.height / 4) | 0)
/* width: OPTIONAL, current value will be used */
/* height: OPTIONAL, current value will be used */
}))
The Findbar is a child window of the BaseWindow
passed during construction. To open it use:
findbar.open()
When the Findbar is closed, its window is destroyed to free memory resources. Use the following method to close the Findbar:
findbar.close()
A new internal window will be created the next time the open
method is called. There is no need to instantiate another Findbar for the same parent window.
Here is a quick example demonstrating how to use the electron-findbar
:
const { app, BrowserWindow } = require('electron')
const Findbar = require('electron-findbar')
app.whenReady().then(() => {
const window = new BrowserWindow()
window.loadURL('https://github.com/ECRomaneli/electron-findbar')
// Create and configure the Findbar object
const findbar = Findbar.from(window)
// [OPTIONAL] Customize window options
findbar.setWindowOptions({ movable: true, resizable: true })
// [OPTIONAL] Handle the window object when the Findbar is opened
findbar.setWindowHandler(win => { win.webContents.openDevTools() })
// Open the Findbar
findbar.open()
})
The Findbar component can be controlled using keyboard shortcuts. Below are two implementation approaches to help you integrate search functionality seamlessly into your application's user experience.
Note: The following examples demonstrate only the ideal (happy path) scenarios. For production use, make sure to thoroughly validate all inputs and handle edge cases appropriately.
The before-input-event
approach allows you to capture keyboard events directly in the main process before they're processed by the web contents, giving you precise control:
window.webContents.on('before-input-event', (event, input) => {
// Detect Ctrl+F (Windows/Linux) or Command+F (macOS)
if ((input.control || input.meta) && input.key.toLowerCase() === 'f') {
// Prevent default browser behavior
event.preventDefault()
// Access and open the findbar
const findbar = Findbar.from(window)
if (findbar) {
findbar.open()
}
}
// Handle Escape key to close the findbar
if (input.key === 'Escape') {
const findbar = Findbar.from(window)
if (findbar && findbar.isOpen()) {
event.preventDefault()
findbar.close()
}
}
})
For a more integrated approach, you can modify your application's menu system to include findbar controls with keyboard accelerators. This method makes shortcuts available throughout your application:
// Get reference to the parent window
const parent = currentBrowserWindowOrWebContents
// Get or create application menu
const appMenu = Menu.getApplicationMenu() ?? new Menu()
// Add Findbar controls to menu
appMenu.append(new MenuItem({
label: 'Find',
submenu: [
{
label: 'Find in Page',
click: () => Findbar.from(parent)?.open(),
accelerator: 'CommandOrControl+F'
},
{
label: 'Close Find',
click: () => Findbar.from(parent)?.close(),
accelerator: 'Esc'
}
]
}))
// Apply the updated menu
Menu.setApplicationMenu(appMenu)
Both approaches have their advantages - the first offers fine-grained control over exactly when shortcuts are activated, while the second provides better integration with standard application menu conventions.
Once open, the Findbar appears by default in the top-right corner of the parent window and can be used without additional coding. Alternatively, you can use the following methods to trigger findInPage
and navigate through matches in the main process:
/**
* Get the last state of the findbar.
* @returns {{ text: string, matchCase: boolean, movable: boolean }} Last state of the findbar.
*/
getLastState()
/**
* Initiate a request to find all matches for the specified text on the page.
* @param {string} text - The text to search for.
* @param {boolean} [skipRendererEvent=false] - Skip update renderer event.
*/
startFind(text, skipRendererEvent)
/**
* Whether the search should be case-sensitive.
* @param {boolean} status - Whether the search should be case-sensitive. Default is false.
* @param {boolean} [skipRendererEvent=false] - Skip update renderer event.
*/
matchCase(status, skipRendererEvent)
/**
* Select the previous match, if available.
*/
findPrevious()
/**
* Select the next match, if available.
*/
findNext()
/**
* Stop the find request and clears selection.
*/
stopFind()
/**
* Whether the findbar is opened.
* @returns {boolean} True if the findbar is open, otherwise false.
*/
isOpen()
/**
* Whether the findbar is focused. If the findbar is closed, false will be returned.
* @returns {boolean} True if the findbar is focused, otherwise false.
*/
isFocused()
/**
* Whether the findbar is visible to the user in the foreground of the app.
* If the findbar is closed, false will be returned.
* @returns {boolean} True if the findbar is visible, otherwise false.
*/
isVisible()
As an alternative, the findbar can be controlled using IPC events in the renderer
process of the WebContents
provided during the findbar construction.
If the contextIsolation
is enabled, the electron-findbar/remote
will not be available, but the IPC events can be used directly through the preload script:
const $remote = (ipc => ({
getLastState: async () => ipc.invoke('electron-findbar/last-state'),
inputChange: (value) => { ipc.send('electron-findbar/input-change', value) },
matchCase: (value) => { ipc.send('electron-findbar/match-case', value) },
previous: () => { ipc.send('electron-findbar/previous') },
next: () => { ipc.send('electron-findbar/next') },
close: () => { ipc.send('electron-findbar/close') }
})) (require('electron').ipcRenderer)
$remote.open()
$remote.inputChange('findIt')
With the contextIsolation
disabled, the remote library is available to use:
const FindbarRemote = require('electron-findbar/remote')
FindbarRemote.open()
FindbarRemote.inputChange('findIt')
Created by Emerson Capuchi Romaneli (@ECRomaneli).
This project is licensed under the MIT License.