Pass arguments with page.evaluate
I've had that exact problem. It can be done with a little trickery, because page.evaluate
also can accept a string.
There are several ways to do it, but I use a wrapper called evaluate
, which accepts additional parameters to pass to the function that must be evaluated on the webkit side. You would use it like this:
page.open(url, function() {
var foo = 42;
evaluate(page, function(foo) {
// this code has now has access to foo
console.log(foo);
}, foo);
});
And here is the evaluate()
function:
/*
* This function wraps WebPage.evaluate, and offers the possibility to pass
* parameters into the webpage function. The PhantomJS issue is here:
*
* http://code.google.com/p/phantomjs/issues/detail?id=132
*
* This is from comment #43.
*/
function evaluate(page, func) {
var args = [].slice.call(arguments, 2);
var fn = "function() { return (" + func.toString() + ").apply(this, " + JSON.stringify(args) + ");}";
return page.evaluate(fn);
}
How can I pass variable into an evaluate function?
You have to pass the variable as an argument to the pageFunction
like this:
const links = await page.evaluate((evalVar) => {
console.log(evalVar); // 2. should be defined now
…
}, evalVar); // 1. pass variable as an argument
You can pass in multiple variables by passing more arguments to page.evaluate()
:
await page.evaluate((a, b c) => { console.log(a, b, c) }, a, b, c)
The arguments must either be serializable as JSON or JSHandle
s of in-browser objects: https://pptr.dev/#?show=api-pageevaluatepagefunction-args
Passing arguments to page.evaluate (puppeteer) when used with pkg builds
If you just need to get some property value, you should probably use getProperty
(see https://pptr.dev/api/puppeteer.jshandle.getproperty):
const myValue: string = await element.getProperty("value").then(e => e._remoteObject.value);
Otherwise, I haven't tried this but it seems that evaluate
function can be called on a JSHandle
(https://pptr.dev/api/puppeteer.jshandle.evaluate), so maybe the following could work:
element.evaluate(
`((el) => {
return el.value;
})();`
);
If that doesn't work then you could add these functions to the spawn browsers:
// get elements by xpath (same as $x)
function getElementByXpath(thePath, target = document) {
const result = document.evaluate(thePath, target, null, XPathResult.ANY_TYPE, null);
const tags = [];
let node = null;
// eslint-disable-next-line no-cond-assign,@typescript-eslint/strict-boolean-expressions
while(node = result.iterateNext()) {
tags.push(node);
}
return tags;
}
// get elements from css or xpath
function getElements(selectorOrXpath, parent = document) {
const isxPath = selectorOrXpath.includes("//");
if(isxPath) {
return getElementByXpath(selectorOrXpath, parent)
}
else {
return parent.querySelectorAll(selectorOrXpath)
}
}
and then call getElements("${selector}");
instead of document.querySelectorAll("${selector}");
in your example,
this way it'll work with both xpath selectors as well as css.
Note that these functions will get removed from your spawn browsers when they refresh (easy way to get around this is to just include them in the same "evaluate" function).
How do pass arguments to a page.evaluate?
You pass arguments to the evaluate
method like this:
page.evaluate((a, b) => {
// you can use `a` and `b` here
}, a, b);
You can read more on arguments for the method in the docs here.
Is it possible to pass a function to Puppeteer's page.evaluate()
No, you cannot pass functions like that. The passed data needs to be serializable via JSON.stringify
, which is not possible for functions.
Alternative: Expose the function
To use a function from your Node.js environment inside the page, you need to expose it via page.exposeFunction
. When called the function is executed inside your Node.js environment
await page.exposeFunction('myfunction', text => `great ${text}`);
await page.evaluate(async (object) => {
return await window.myfunction(object); // 'great example'
}, 'example');
Alternative: Define the function inside page.evaluate
To use a function inside the page context, you can either define it inside of the context. This way, the function does not have access to your Node.js variables.
await page.evaluate((obj) => {
const myfunction = (stuff) => `great ${stuff}`;
return myfunction(obj); // 'great example'
}, 'example');
Puppeteer - Passing a variable to page.evaluate()
Figured it out. Of course 5 minutes after posting to SO following hours of frustration, but here goes:
const x = 2;
let value = await page.evaluate((x) => {
return Promise.resolve(document.querySelector("#pageBody > div:nth-child(" + x + ") > table > tr > td").innerText)
}, x);
console.log(value);
pass multiple parameters into page.evaluate()
You can pass as many as necessary parameters into page.evaluate
as long as they're serializable:
await page.evaluate( (selector, state) => {
console.log(selector)
console.log(state)
}, "div.top-select-option a.eccheckbox", state);
How to pass page as an argument in the function parameter of page.evaluate()?
I'm not sure if you understand how the .evaluate
method works. This method executes the code in the browser context, so to click on an element, you can use such code as if you were writing it in a browser. You cannot pass complex objects like page
between the browser and Node.js
. I changed your code inside the .evaluate
method, it should look something like this:
const result = await page.evaluate((selector1, selector2) => {
const result = [];
const sel = document.querySelectorAll(selector1);
if (sel.length) {
const total = sel.length;
for (let i = 0; i<total; i++) {
result.push(sel.querySelector(`div:nth-child(${i+1})`).textContent);
document.querySelector(`${selector2} div:nth-child(${i+1})`).click();
}
}
return result;
}, selector1, selector2);
Related Topics
Associative Array Versus Object in JavaScript
How to Get the Current Time Only in JavaScript
Break Promise Chain and Call a Function Based on the Step in the Chain Where It Is Broken (Rejected)
How to Give Keyboard Focus to a Div and Attach Keyboard Event Handlers to It
Node.Js: Read a Text File into an Array. (Each Line an Item in the Array.)
Normalizing Mousewheel Speed Across Browsers
This' Does Not Work Properly in Another Event. I'm Clueless as to Why
Need to Cancel Click/Mouseup Events When Double-Click Event Detected
In JavaScript, How to Check If an Array Has Duplicate Values
How to Convert a Plain Object into an Es6 Map
Automatic Semicolon Insertion & Return Statements
How to Flatten Nested Array in JavaScript
Can You Force Vue.Js to Reload/Re-Render
JavaScript .Queryselector Find <Div> by Innertext
Why Is Proxy to a Map Object in Es2015 Not Working