Как ждать видимости элемента в фантомах
Пользователи нажимают на эту ссылку:
<span onclick="slow_function_that_fills_the_panel(); $('#panel').show();">
Теперь я имитирую клик в фантоме:
page.evaluate(
function() { $("#panel").click(); }
);
console.log('SUCCESS');
phantom.exit();
Призрак выходит до того, как медленная функция завершает свое выполнение, и DIV становится видимым. Как я могу реализовать ожидание?
4 ответа
Вот поворот ответа 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);
}
Пример использования:
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
});
Это немного проще, когда вы используете переменную конфигурации.
PhantomJS по умолчанию работает асинхронно, вызывая проблемы, подобные той, что вы описали выше (где сценарий завершается до того, как ваши результаты будут готовы)
Однако ничто не мешает вам использовать его синхронно.
Просто используйте phantom.page.sendEvent('mousemove')
в то время как цикл. Это будет повторять цикл событий, пока механизм webkit не загрузит вашу страницу или не обработает все необходимые события браузера.
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');
Обратите внимание, что page.loading
также может быть любое другое логическое условие, например:
do { phantom.page.sendEvent('mousemove'); }
while (page.evaluate(function() {return $("#panel").is(":visible");}));
Я обнаружил этот подход, работая над проектом http://triflejs.org/ (фантомная версия Internet Explorer), пытаясь эмулировать вызовы trifle.wait(ms)
внутри среды PhantomJS.
Мой подход к этому сценарию состоит в том, чтобы ждать, пока что-то будет сделано или верно. Я настоятельно рекомендую вам протестировать 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);
}
});
}
Этот скрипт оценивают $('#thediv').is(':visible')
(классический код Jquery) каждые 500 мс, чтобы проверить, является ли div видимым.
Внутри page.evaluate(), используйте свойство self.loading для проверки на донорство....
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();
});