PhantomJS ist ein Webkit-basierter Headless Browser, also ein Browser ohne GUI. Damit kann man verschiedene Sachen machen, zum Beispiel Ladezeiten aufnehmen oder Tests automatisieren. Um eine Funktion im Kontext einer geladenen Seite auszuführen, wird page.evaluate() benutzt.

Dabei gibt es etwas wichtiges zu beachten, was ich gestern nur mühsam herausfand: evaluate() wandelt die angegebene Funktion in einen String um, bevor es sie ausführt. evaluate() hat keinen Zugriff auf Daten außerhalb des Seitenkontexts und dieses Verhalten ist ein Hackaround, damit Variablen übergeben werden können. Das bedeutet leider, dass auch keine Closures vorhanden sind. Verschiedene Skripte in die geladene Seite zu inkludieren, nach der Reihe, eins nach dem anderen, wird so schwierig. Callback Hell ahoi!

Noch mal zur Zusammenfassung, das hier funktioniert:

1page.evaluate( function() {
2        return document.getElementById('foo').textContent;
3});

Das hier nicht:

1var getText = function getText( id ) {
2        return document.getElementById( id ).textContent;
3}
4
5page.evaluate( getText( 'foo' ) );

Weil es so aussehen muss:

1page.evaluate( function( id ) {
2        return document.getElementById( id ).textContent;
3}, 'foo');

Closures funktionieren auch nicht:

1var id = "main";
2var text = page.evaluate( function() {
3        return document.getElementById( id ).textContent;
4});

Was schon gar nicht funktioniert:

 1// include css
 2var load = function load( url ) {
 3        var defer = jQuery.Deferred();
 4        page.evaluateAsync( function() {
 5                var link = document.createElement("link");
 6                link.rel = "stylesheet";
 7                link.href = url;
 8                var head = document.getElementsByTagName("head")[0];
 9                head.appendChild(link);
10                defer.resolve();
11        });
12        return defer.promise();
13}
14var load_funcs = [];
15$.each( urls, function( url ) {
16        load_funcs.push( load( url ) );
17});
18$.when.apply( $, load_funcs ).then( function() {
19        // all css included!
20});