Implementation:Puppeteer Puppeteer Injected CustomQuerySelector
| Property | Value |
|---|---|
| sources | packages/puppeteer-core/src/injected/CustomQuerySelector.ts
|
| domains | Injected Scripts, Query Selectors |
| last_updated | 2026-02-12 00:00 GMT |
Overview
Description
The CustomQuerySelector module provides an in-page registry for user-defined query selector handlers. It is part of the injected subsystem, meaning it executes inside the browser page context rather than in Node.js. The module defines the CustomQuerySelector interface and implements the CustomQuerySelectorRegistry class, which stores name-to-handler mappings and normalises partial handler implementations (i.e., handlers that provide only queryOne or only queryAll are automatically completed with a derived counterpart). A singleton instance, customQuerySelectors, is exported for use by the broader Puppeteer query engine (notably PQuerySelector).
Usage
This module is consumed internally by the P-selector query engine (PQuerySelector.ts) when it encounters a pseudo-selector name that is not one of the built-in types (text, xpath, aria). At that point it looks up the custom handler from the singleton registry. Users register custom handlers through the public Puppeteer.registerCustomQueryHandler() API on the Node.js side; those registrations are mirrored into this in-page registry before query execution.
Code Reference
Source Location
packages/puppeteer-core/src/injected/CustomQuerySelector.ts (65 lines)
Signature
export interface CustomQuerySelector {
querySelector(root: Node, selector: string): Awaitable<Node | null>;
querySelectorAll(root: Node, selector: string): AwaitableIterable<Node>;
}
class CustomQuerySelectorRegistry {
register(name: string, handler: CustomQueryHandler): void;
unregister(name: string): void;
get(name: string): CustomQuerySelector | undefined;
clear(): void;
}
export const customQuerySelectors: CustomQuerySelectorRegistry;
Import
import { customQuerySelectors } from '../injected/CustomQuerySelector.js';
import type { CustomQuerySelector } from '../injected/CustomQuerySelector.js';
I/O Contract
CustomQuerySelectorRegistry.register
| Direction | Name | Type | Description |
|---|---|---|---|
| Input | name | string |
Unique name identifying the custom selector |
| Input | handler | CustomQueryHandler |
Handler providing queryOne and/or queryAll; at least one must be defined
|
| Output | (void) | void |
Stores the handler in the internal map |
| Error | Error | Error |
Thrown if neither queryOne nor queryAll is provided
|
CustomQuerySelectorRegistry.get
| Direction | Name | Type | Description |
|---|---|---|---|
| Input | name | string |
Name of the registered custom selector |
| Output | result | undefined | The registered selector or undefined if not found |
Usage Examples
// Registering a custom query handler that finds elements by data-test attribute
import { customQuerySelectors } from '../injected/CustomQuerySelector.js';
customQuerySelectors.register('testId', {
queryOne: (root: Node, selector: string) => {
return (root as Element).querySelector(`[data-test="${selector}"]`);
},
queryAll: (root: Node, selector: string) => {
return (root as Element).querySelectorAll(`[data-test="${selector}"]`);
},
});
// Retrieving a custom selector
const handler = customQuerySelectors.get('testId');
if (handler) {
const element = await handler.querySelector(document, 'my-button');
}
// Removing a custom selector
customQuerySelectors.unregister('testId');
// Clearing all custom selectors
customQuerySelectors.clear();