1

I'm using PhantomJS v2.0 and CasperJS 1.1.0-beta3. I want to query a specific part inside the page DOM.

Here the code that did not work:

function myfunc()
{
    return document.querySelector('span[style="color:#50aa50;"]').innerText;    
}
var del=this.evaluate(myfunc());

this.echo("value: " + del);

And here the code that did work:

var del=this.evaluate(function() 
{
   return document.querySelector('span[style="color:#50aa50;"]').innerText; 
});

this.echo("value: " + del);

It seems to be the same, but it works different, I don't understand.

And here a code that did also work:

function myfunc()
{
    return document.querySelector('span[style="color:#50aa50;"]').innerText;    
}
var del=this.evaluate(myfunc);

this.echo("value: " + del);

The difference here, I call the myfunc without the '()'.

Can anyone explain the reason?

1 Answer 1

0

The problem is this:

var text = this.evaluate(myfunc());

Functions in JavaScript are first class citizen. You can pass them into other functions. But that's not what you are doing here. You call the function and pass the result into evaluate, but the result is not a function.

Also casper.evaluate() is the page context, and only the page context has access to the document. When you call the function (with ()) essentially before executing casper.evaluate(), you erroneously try to access the document, when it is not possible.

The difference to casper.evaluate(function(){...}); is that the anonymous function is defined and passed into the evaluate() function.

There are cases where a function should be called instead of passed. For example when currying is done, but this is not applicable to casper.evaluate(), because it is sandboxed and the function that is finally run in casper.evaluate() cannot use variables from outside. It must be self contained. So the following code will also not work:

function myFunc2(a){
    return function(){
        // a is from outer scope so it will be inaccessible in `evaluate`
        return a;
    };
}
casper.echo(casper.evaluate(myFunc2("asd"))); // null

You should use

var text = this.evaluate(myfunc);

to pass a previously defined function to run in the page context.

It's also not a good idea to use reserved keywords like del as variable names.

2
  • Ah thanks for your good explanation. That means, when I write myfunc without the () then the function will not be called before the evaluate will be called. Instead the myfuncwill be handled as like a variable and will be passed as an argument to the evaluate function. With the effect the myfuncwill be called after the evaluate will be called ? Right ? Commented Feb 12, 2015 at 14:08
  • evaluate uses the function that is passed in, so you cannot say that the function is executed after evaluate finished. If you want to know how it uses it, you can look into the source code of PhantomJS. The evaluate argument is not used as a callback when the execution is finished. It is a function to tell evaluate what to do.
    – Artjom B.
    Commented Feb 12, 2015 at 14:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.