Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: update custom query handler #10726

Merged
merged 1 commit into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
77 changes: 74 additions & 3 deletions docs/guides/query-selectors.md → docs/guides/query-selectors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,81 @@ const node = await page.waitForSelector(

Puppeteer provides users the ability to add their own query selectors to Puppeteer using [Puppeteer.registerCustomQueryHandler](../api/puppeteer.registercustomqueryhandler.md). This is useful for creating custom selectors based on framework objects or other vendor-specific objects.

#### Example
#### Custom Selectors

You can register a custom query handler that allows you to create custom selectors. For example, define a query handler for `getById` selectors:

```ts
Puppeteer.registerCustomQueryHandler('getById', {
queryOne: (elementOrDocument, selector) => {
return elementOrDocument.querySelector(`[id="${CSS.escape(selector)}"]`);
},
// Note: for demonstation perpose only `id` should be page unique
queryAll: (elementOrDocument, selector) => {
return elementOrDocument.querySelectorAll(`[id="${CSS.escape(selector)}"]`);
},
});
```

You can now use it as following:

```ts
const node = await page.waitForSelector('::-p-getById(elementId)');
// OR used in conjunction with other selectors
const moreSpecificNode = await page.waitForSelector(
'.side-bar ::-p-getById(elementId)'
);
```

#### Custom framework components selector

:::caution

Be careful when relying on internal APIs of libraries or frameworks. They can change at any time.

:::

Find Vue components by name by using Vue internals for querying:

```ts
Puppeteer.registerCustomQueryHandler('vue', {
queryOne: (element, name) => {
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
do {
const currentNode = walker.currentNode;
if (
currentNode.__vnode?.ctx?.type?.name.toLowerCase() ===
name.toLocaleLowerCase()
) {
return currentNode;
}
} while (walker.nextNode());

return null;
},
});
```

Query the Vue component as following:

```ts
const element = await page.$('::-p-vue(MyComponent)');
```

#### Web Components

Web Components create their own tag so you can query them by the tag name:

```ts
const element = await page.$('my-web-component');
```

Suppose you register a custom selector called `lit`. You can use it like so:
Extend `HTMLElementTagNameMap` to define types for custom tags. This allows Puppeteer to infer the return type for the ElementHandle:

```ts
const node = await page.waitForSelector('::-p-lit(LitElement)');
declare global {
interface HTMLElementTagNameMap {
'my-web-component': MyWebComponent;
}
}
```
3 changes: 0 additions & 3 deletions packages/puppeteer-core/src/common/CustomQueryHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,6 @@ export class CustomQueryHandlerRegistry {
* @internal
*/
register(name: string, handler: CustomQueryHandler): void {
if (this.#handlers.has(name)) {
throw new Error(`Cannot register over existing handler: ${name}`);
}
assert(
!this.#handlers.has(name),
`Cannot register over existing handler: ${name}`
Expand Down