diff --git a/docs/api/index.md b/docs/api/index.md
index f7e0017ae78..7066aa55202 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -1172,6 +1172,13 @@ Valid options to configure PDF generation via [Page.pdf()](./puppeteer.page.pdf.
+[QueryOptions](./puppeteer.queryoptions.md)
+
+ |
+
+ |
+
+
[RemoteAddress](./puppeteer.remoteaddress.md)
|
diff --git a/docs/api/puppeteer.elementhandle.__.md b/docs/api/puppeteer.elementhandle.__.md
index cd3585a0253..4ab3553a882 100644
--- a/docs/api/puppeteer.elementhandle.__.md
+++ b/docs/api/puppeteer.elementhandle.__.md
@@ -11,7 +11,8 @@ Queries the current element for all elements matching the given selector.
```typescript
class ElementHandle {
$$(
- selector: Selector
+ selector: Selector,
+ options?: QueryOptions
): Promise>>>;
}
```
@@ -43,6 +44,19 @@ Selector
The selector to query for.
+ |
+
+
+options
+
+ |
+
+[QueryOptions](./puppeteer.queryoptions.md)
+
+ |
+
+_(Optional)_
+
|
**Returns:**
diff --git a/docs/api/puppeteer.elementhandle.md b/docs/api/puppeteer.elementhandle.md
index a3a7f6f5d7a..a7df5f4eec8 100644
--- a/docs/api/puppeteer.elementhandle.md
+++ b/docs/api/puppeteer.elementhandle.md
@@ -105,7 +105,7 @@ Queries the current element for an element matching the given selector.
-[$$(selector)](./puppeteer.elementhandle.__.md)
+[$$(selector, options)](./puppeteer.elementhandle.__.md)
|
diff --git a/docs/api/puppeteer.frame.__.md b/docs/api/puppeteer.frame.__.md
index a148a0168a5..9b2bae55161 100644
--- a/docs/api/puppeteer.frame.__.md
+++ b/docs/api/puppeteer.frame.__.md
@@ -11,7 +11,8 @@ Queries the frame for all elements matching the given selector.
```typescript
class Frame {
$$(
- selector: Selector
+ selector: Selector,
+ options?: QueryOptions
): Promise>>>;
}
```
@@ -43,6 +44,19 @@ Selector
[selector](https://pptr.dev/guides/page-interactions#query-selectors) to query page for. [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) can be passed as-is and a [Puppeteer-specific seletor syntax](https://pptr.dev/guides/page-interactions#p-selectors) allows quering by [text](https://pptr.dev/guides/page-interactions#text-selectors--p-text), [a11y role and name](https://pptr.dev/guides/page-interactions#aria-selectors--p-aria), and [xpath](https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath) and [combining these queries across shadow roots](https://pptr.dev/guides/page-interactions#-and--combinators). Alternatively, you can specify a selector type using a prefix [prefix](https://pptr.dev/guides/page-interactions#built-in-selectors).
+ |
+
+
+options
+
+ |
+
+[QueryOptions](./puppeteer.queryoptions.md)
+
+ |
+
+_(Optional)_
+
|
**Returns:**
diff --git a/docs/api/puppeteer.frame.md b/docs/api/puppeteer.frame.md
index 2848b7d24c1..cc9d8f502cf 100644
--- a/docs/api/puppeteer.frame.md
+++ b/docs/api/puppeteer.frame.md
@@ -125,7 +125,7 @@ Queries the frame for an element matching the given selector.
-[$$(selector)](./puppeteer.frame.__.md)
+[$$(selector, options)](./puppeteer.frame.__.md)
|
diff --git a/docs/api/puppeteer.page.__.md b/docs/api/puppeteer.page.__.md
index 65befca7b2e..14002caddd4 100644
--- a/docs/api/puppeteer.page.__.md
+++ b/docs/api/puppeteer.page.__.md
@@ -11,7 +11,8 @@ Finds elements on the page that match the selector. If no elements match the sel
```typescript
class Page {
$$(
- selector: Selector
+ selector: Selector,
+ options?: QueryOptions
): Promise>>>;
}
```
@@ -43,6 +44,19 @@ Selector
[selector](https://pptr.dev/guides/page-interactions#query-selectors) to query page for. [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) can be passed as-is and a [Puppeteer-specific seletor syntax](https://pptr.dev/guides/page-interactions#p-selectors) allows quering by [text](https://pptr.dev/guides/page-interactions#text-selectors--p-text), [a11y role and name](https://pptr.dev/guides/page-interactions#aria-selectors--p-aria), and [xpath](https://pptr.dev/guides/page-interactions#xpath-selectors--p-xpath) and [combining these queries across shadow roots](https://pptr.dev/guides/page-interactions#-and--combinators). Alternatively, you can specify a selector type using a prefix [prefix](https://pptr.dev/guides/page-interactions#built-in-selectors).
+ |
+
+
+options
+
+ |
+
+[QueryOptions](./puppeteer.queryoptions.md)
+
+ |
+
+_(Optional)_
+
|
**Returns:**
diff --git a/docs/api/puppeteer.page.md b/docs/api/puppeteer.page.md
index 1e3f61cf7f3..acb15ab0b58 100644
--- a/docs/api/puppeteer.page.md
+++ b/docs/api/puppeteer.page.md
@@ -250,7 +250,7 @@ Shortcut for [Page.mainFrame().$(selector)](./puppeteer.frame._.md).
-[$$(selector)](./puppeteer.page.__.md)
+[$$(selector, options)](./puppeteer.page.__.md)
|
diff --git a/docs/api/puppeteer.queryoptions.md b/docs/api/puppeteer.queryoptions.md
new file mode 100644
index 00000000000..fb318cb4863
--- /dev/null
+++ b/docs/api/puppeteer.queryoptions.md
@@ -0,0 +1,55 @@
+---
+sidebar_label: QueryOptions
+---
+
+# QueryOptions interface
+
+#### Signature:
+
+```typescript
+export interface QueryOptions
+```
+
+## Properties
+
+
+
+Property
+
+ |
+
+Modifiers
+
+ |
+
+Type
+
+ |
+
+Description
+
+ |
+
+Default
+
+ |
+
+
+isolate
+
+ |
+
+ |
+
+boolean
+
+ |
+
+Whether to run the query in isolation. When returning many elements from [Page.$$()](./puppeteer.page.__.md) or similar methods, it might be useful to turn off the isolation to improve performance. By default, the querying code will be executed in a separate sandbox realm.
+
+ |
+
+`true`
+
+ |
+
diff --git a/packages/puppeteer-core/src/api/ElementHandle.ts b/packages/puppeteer-core/src/api/ElementHandle.ts
index 9c3fe0d5ab0..e6cff11c7ab 100644
--- a/packages/puppeteer-core/src/api/ElementHandle.ts
+++ b/packages/puppeteer-core/src/api/ElementHandle.ts
@@ -31,7 +31,11 @@ import type {
MouseClickOptions,
} from './Input.js';
import {JSHandle} from './JSHandle.js';
-import type {ScreenshotOptions, WaitForSelectorOptions} from './Page.js';
+import type {
+ QueryOptions,
+ ScreenshotOptions,
+ WaitForSelectorOptions,
+} from './Page.js';
/**
* @public
@@ -371,8 +375,34 @@ export abstract class ElementHandle<
* elements matching the given selector.
*/
@throwIfDisposed()
- @ElementHandle.bindIsolatedHandle
async $$(
+ selector: Selector,
+ options?: QueryOptions
+ ): Promise>>> {
+ if (options?.isolate === false) {
+ return await this.#$$impl(selector);
+ }
+ return await this.#$$(selector);
+ }
+
+ /**
+ * Isolates {@link ElementHandle.$$} if needed.
+ *
+ * @internal
+ */
+ @ElementHandle.bindIsolatedHandle
+ async #$$(
+ selector: Selector
+ ): Promise>>> {
+ return await this.#$$impl(selector);
+ }
+
+ /**
+ * Implementation for {@link ElementHandle.$$}.
+ *
+ * @internal
+ */
+ async #$$impl(
selector: Selector
): Promise>>> {
const {updatedSelector, QueryHandler} =
diff --git a/packages/puppeteer-core/src/api/Frame.ts b/packages/puppeteer-core/src/api/Frame.ts
index 412087de6e8..659b2f2919e 100644
--- a/packages/puppeteer-core/src/api/Frame.ts
+++ b/packages/puppeteer-core/src/api/Frame.ts
@@ -10,6 +10,7 @@ import type {ClickOptions, ElementHandle} from '../api/ElementHandle.js';
import type {HTTPResponse} from '../api/HTTPResponse.js';
import type {
Page,
+ QueryOptions,
WaitForSelectorOptions,
WaitTimeoutOptions,
} from '../api/Page.js';
@@ -564,11 +565,12 @@ export abstract class Frame extends EventEmitter {
*/
@throwIfDetached
async $$(
- selector: Selector
+ selector: Selector,
+ options?: QueryOptions
): Promise>>> {
// eslint-disable-next-line rulesdir/use-using -- This is cached.
const document = await this.#document();
- return await document.$$(selector);
+ return await document.$$(selector, options);
}
/**
diff --git a/packages/puppeteer-core/src/api/Page.ts b/packages/puppeteer-core/src/api/Page.ts
index edf7468a7a2..ef1ff9b23de 100644
--- a/packages/puppeteer-core/src/api/Page.ts
+++ b/packages/puppeteer-core/src/api/Page.ts
@@ -346,6 +346,21 @@ export interface ScreencastOptions {
ffmpegPath?: string;
}
+/**
+ * @public
+ */
+export interface QueryOptions {
+ /**
+ * Whether to run the query in isolation. When returning many elements
+ * from {@link Page.$$} or similar methods, it might be useful to turn
+ * off the isolation to improve performance. By default, the querying
+ * code will be executed in a separate sandbox realm.
+ *
+ * @defaultValue `true`
+ */
+ isolate: boolean;
+}
+
/**
* All the events that a page instance may emit.
*
@@ -1081,9 +1096,10 @@ export abstract class Page extends EventEmitter {
* Shortcut for {@link Frame.$$ | Page.mainFrame().$$(selector) }.
*/
async $$(
- selector: Selector
+ selector: Selector,
+ options?: QueryOptions
): Promise>>> {
- return await this.mainFrame().$$(selector);
+ return await this.mainFrame().$$(selector, options);
}
/**
diff --git a/test/src/queryselector.spec.ts b/test/src/queryselector.spec.ts
index 13e20a4d1a9..c7f03314d18 100644
--- a/test/src/queryselector.spec.ts
+++ b/test/src/queryselector.spec.ts
@@ -167,6 +167,23 @@ describe('querySelector', function () {
});
expect(await Promise.all(promises)).toEqual(['A', 'B']);
});
+
+ it('should query existing elements without isolation', async () => {
+ const {page} = await getTestState();
+
+ await page.setContent('A
B ');
+ const elements = await page.$$('div', {
+ isolate: false,
+ });
+ expect(elements).toHaveLength(2);
+ const promises = elements.map(element => {
+ return page.evaluate(e => {
+ return e.textContent;
+ }, element);
+ });
+ expect(await Promise.all(promises)).toEqual(['A', 'B']);
+ });
+
it('should return empty array if nothing is found', async () => {
const {page, server} = await getTestState();
|