Les utilisateurs cliquent sur ce lien:
<span onclick="slow_function_that_fills_the_panel(); $('#panel').show();">
Maintenant, je simule le clic dans phantomjs:
page.evaluate(
function() { $("#panel").click(); }
);
console.log('SUCCESS');
phantom.exit();
Phantom se ferme avant que la fonction lente ne termine son exécution et que le DIV ne devienne visible. Comment puis-je implémenter l'attente?
Voici un aperçu de la réponse de Cybermaxs:
function waitFor ($config) {
$config._start = $config._start || new Date();
if ($config.timeout && new Date - $config._start > $config.timeout) {
if ($config.error) $config.error();
if ($config.debug) console.log('timedout ' + (new Date - $config._start) + 'ms');
return;
}
if ($config.check()) {
if ($config.debug) console.log('success ' + (new Date - $config._start) + 'ms');
return $config.success();
}
setTimeout(waitFor, $config.interval || 0, $config);
}
Exemple d'utilisation:
waitFor({
debug: true, // optional
interval: 0, // optional
timeout: 1000, // optional
check: function () {
return page.evaluate(function() {
return $('#thediv').is(':visible');
});
},
success: function () {
// we have what we want
},
error: function () {} // optional
});
C'est un peu plus facile lorsque vous utilisez une variable de configuration.
PhantomJS s'exécute de manière asynchrone par défaut, provoquant des problèmes comme celui que vous décrivez ci-dessus (où le script se termine avant que vos résultats ne soient prêts)
Cependant, rien ne vous empêche de l'utiliser de manière synchrone.
Utilisez simplement phantom.page.sendEvent('mousemove')
dans une boucle while. Cela continuera à parcourir la pompe d'événements jusqu'à ce que le moteur du kit Web charge votre page ou traite les événements de navigateur nécessaires.
var page = require('webpage').create();
// Step 1: View item
page.open('http://localhost/item3324.php');
do { phantom.page.sendEvent('mousemove'); } while (page.loading);
page.render('step1-viewitem.png');
// Step 2: Add to cart
page.evaluate(function() {$('#add-to-cart').click(); });
do { phantom.page.sendEvent('mousemove'); } while (page.loading);
page.render('step2-viewcart.png');
// Step 3: Confirm contents
page.evaluate(function() {$('#confirm-cart').click(); });
do { phantom.page.sendEvent('mousemove'); } while (page.loading);
page.render('step3-confirm.png');
Notez que page.loading
Peut également être toute autre condition booléenne, par exemple:
do { phantom.page.sendEvent('mousemove'); }
while (page.evaluate(function() {return $("#panel").is(":visible");}));
J'ai découvert cette approche en travaillant sur le projet triflejs.org (la version Internet Explorer de fantôme) en essayant d'émuler des appels à trifle.wait(ms)
dans l'environnement PhantomJS.
Mon approche pour ce scénario est d'attendre que "quelque chose" soit fait ou vrai. Je vous suggère fortement de tester waitfor.js .
demo.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<title>Test</title>
</head>
<body id="body">
<div id="thediv">Hello World !</div>
<script type="text/javascript">
$('#thediv').hide();
setTimeout(function () {
$('#thediv').show();
}, 3000);
</script>
</body>
</html>
demoscript.js
var page = require('webpage').create();
var system = require('system');
function waitFor(testFx, onReady, timeOutMillis) {
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 5000, //< Default Max Timout is 5s
start = new Date().getTime(),
condition = false,
interval = setInterval(function () {
if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
// If not time-out yet and condition not yet fulfilled
condition = (typeof (testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
} else {
if (!condition) {
// If condition still not fulfilled (timeout but condition is 'false')
//console.log("'waitFor()' timeout");
typeof (onReady) === "string" ? eval(onReady) : onReady();
clearInterval(interval);
//phantom.exit(1);
} else {
// Condition fulfilled (timeout and/or condition is 'true')
console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
typeof (onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
clearInterval(interval); //< Stop this interval
}
}
}, 500); //< repeat check every 500ms
};
if (system.args.length != 1) {
console.log('invalid call');
phantom.exit(1);
} else {
//adapt the url to your context
page.open('http://localhost:40772/demo.html', function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
phantom.exit();
} else {
waitFor(
function () {
return page.evaluate(function () {
return $('#thediv').is(':visible');
});
},
function () {
page.render('page.png');
phantom.exit();
}, 5000);
}
});
}
Ce script évalue $('#thediv').is(':visible')
(code Jquery classique) toutes les 500 ms pour vérifier si le div est visible.
À l'intérieur de page.evaluate (), utilisez la propriété self.loading pour tester la cuisson ....
var fs = require('fs');
path = '/path/to/file.html';
address = 'http://google.com';
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to access page');
} else {
var p = page.evaluate(function () {
if(!self.loading){ // ah, such beauty
return document.documentElement.outerHTML;
}
});
fs.write(path, p, 'w');
}
phantom.exit();
});