Existing answers are reasonable, and identify the fundamental issue with OP's code, which is that you can't return DOM nodes from evaluate
blocks, only serializable data. But these answers could be improved in a few respects.
This answer is the strongest so far, because it uses $eval
and $$eval
, which are shorthand for the common pattern of an evaluate
that immediately calls querySelector
/querySelectorAll
, but has an incorrect line at the time of writing. The correct way to use $$eval
is the following:
const textContents = await page.$$eval(
".title",
els => els.map(el => el.textContent)
);
or, applying the same pattern to issue a series of untrusted clicks:
await page.$$eval("button", els => els.forEach(el => el.click()));
Most of the time, $$eval
is the best way to work with the DOM.
If you only have one node, you can use:
const textContent = await page.$eval(".title", el => el.textContent);
If you need to trigger trusted events on multiple nodes, then use page.$
, page.$$
or on rare occasions, page.evaluateHandle
.
For example, this pattern is a fairly common way to click a series of buttons:
const btns = await page.$$("button");
for (const btn of btns) {
await btn.click();
}
The same pattern can be used to retrieve text contents, but is not recommended relative to $$eval
because it involves many network calls rather than one, which can lead to flakiness:
const titleHandles = await page.$$(".title");
const textContents = [];
for (const el of titleHandles) {
textContents.push(await el.evaluate(el => el.textContent));
}
As always, don't forget to use waitForSelector
or waitForFunction
to make sure the elements are on the page before you query them, if they're not in the static HTML. Puppeteer also has a new auto-waiting locator API, but it's currently experimental and I haven't used it as much as the Playwright locator API. If Puppeteer's locator API gains the same level of support as Playwright's locator API, it'll probably render most other selection methods described above obsolete, as it's done in Playwright.
Promise.resolve
isn't doing anything here, in addition to the DOM nodes not being JSON serializable. – Thermography