Skip to content

Commit 88b9968

Browse files
abalone0204aslushnikov
authored andcommitted
feat(ElementHandle): introduce elementHandle.$eval (#2407)
This patch introduces `elementHandle.$eval` method. References #2401.
1 parent 1d225cf commit 88b9968

4 files changed

Lines changed: 52 additions & 6 deletions

File tree

docs/api.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@
189189
- [class: ElementHandle](#class-elementhandle)
190190
* [elementHandle.$(selector)](#elementhandleselector)
191191
* [elementHandle.$$(selector)](#elementhandleselector)
192+
* [elementHandle.$eval(selector, pageFunction, ...args)](#elementhandleevalselector-pagefunction-args)
192193
* [elementHandle.$x(expression)](#elementhandlexexpression)
193194
* [elementHandle.asElement()](#elementhandleaselement)
194195
* [elementHandle.boundingBox()](#elementhandleboundingbox)
@@ -2230,6 +2231,23 @@ The method runs `element.querySelector` within the page. If no element matches t
22302231

22312232
The method runs `element.querySelectorAll` within the page. If no elements match the selector, the return value resolve to `[]`.
22322233

2234+
#### elementHandle.$eval(selector, pageFunction, ...args)
2235+
- `selector` <[string]> A [selector] to query page for
2236+
- `pageFunction` <[function]> Function to be evaluated in browser context
2237+
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
2238+
- returns: <[Promise]<[Serializable]>> Promise which resolves to the return value of `pageFunction`
2239+
2240+
This method runs `document.querySelector` within the element and passes it as the first argument to `pageFunction`. If there's no element matching `selector`, the method throws an error.
2241+
2242+
If `pageFunction` returns a [Promise], then `frame.$eval` would wait for the promise to resolve and return its value.
2243+
2244+
Examples:
2245+
```js
2246+
const tweetHandle = await page.$('.tweet');
2247+
expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe('100');
2248+
expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe('10');
2249+
```
2250+
22332251
#### elementHandle.$x(expression)
22342252
- `expression` <[string]> Expression to [evaluate](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate).
22352253
- returns: <[Promise]<?[ElementHandle]>> Promise which resolves to ElementHandle pointing to the frame element.

lib/ElementHandle.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,21 @@ class ElementHandle extends JSHandle {
280280
return result;
281281
}
282282

283+
/**
284+
* @param {string} selector
285+
* @param {Function|String} pageFunction
286+
* @param {!Array<*>} args
287+
* @return {!Promise<(!Object|undefined)>}
288+
*/
289+
async $eval(selector, pageFunction, ...args) {
290+
const elementHandle = await this.$(selector);
291+
if (!elementHandle)
292+
throw new Error(`Error: failed to find element matching selector "${selector}"`);
293+
const result = await this.executionContext().evaluate(pageFunction, elementHandle, ...args);
294+
await elementHandle.dispose();
295+
return result;
296+
}
297+
283298
/**
284299
* @param {string} expression
285300
* @return {!Promise<!Array<!ElementHandle>>}

lib/FrameManager.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -366,12 +366,9 @@ class Frame {
366366
* @return {!Promise<(!Object|undefined)>}
367367
*/
368368
async $eval(selector, pageFunction, ...args) {
369-
const elementHandle = await this.$(selector);
370-
if (!elementHandle)
371-
throw new Error(`Error: failed to find element matching selector "${selector}"`);
372-
const result = await this.evaluate(pageFunction, elementHandle, ...args);
373-
await elementHandle.dispose();
374-
return result;
369+
const document = await this._document();
370+
const value = await document.$eval(selector, pageFunction, ...args);
371+
return value;
375372
}
376373

377374
/**

test/elementhandle.spec.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,22 @@ module.exports.addTests = function({testRunner, expect}) {
294294
expect(second).toBe(null);
295295
});
296296
});
297+
describe('ElementHandle.$eval', function() {
298+
it('should work', async({page, server}) => {
299+
await page.setContent('<html><body><div class="tweet"><div class="like">100</div><div class="retweets">10</div></div></body></html>');
300+
const tweet = await page.$('.tweet');
301+
const content = await tweet.$eval('.like', node => node.innerText);
302+
expect(content).toBe('100');
303+
});
304+
305+
it('should retrieve content from subtree', async({page, server}) => {
306+
const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"><div class="a">a-child-div</div></div>';
307+
await page.setContent(htmlContent);
308+
const elementHandle = await page.$('#myId');
309+
const content = await elementHandle.$eval('.a', node => node.innerText);
310+
expect(content).toBe('a-child-div');
311+
});
312+
});
297313

298314
describe('ElementHandle.$$', function() {
299315
it('should query existing elements', async({page, server}) => {

0 commit comments

Comments
 (0)