2013-05-29 3 views
24

Пользователи нажмите на эту ссылку:как ждать видимости элемента в phantomjs

<span onclick="slow_function_that_fills_the_panel(); $('#panel').show();"> 

Теперь я имитирующий щелчок в phantomjs:

page.evaluate(
    function() { $("#panel").click(); } 
); 
console.log('SUCCESS'); 
phantom.exit(); 

Фантом выходы перед медленным функция завершает свое выполнение и DIV становится видимым. Как я могу реализовать ожидание?

+0

глупо решение SetTimeout() – igor

ответ

7

Мой подход к этому сценарию заключается в том, чтобы ждать, пока «что-то» будет сделано или верно. Я настоятельно рекомендую вам проверить 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 видна.

+0

Удивительного решения. Я не уверен, как применить это для узла 'phantom', из-за шаблона' Promise' @see https://github.com/amir20/phantomjs-node/issues/431 – loretoparisi

22

Heres спина ответа 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 
}); 

Это немного легче, когда вы используете переменную конфигурации.

+0

THANKYOU! Я провел два дня, пытаясь найти полтора десятка других способов надежно проверить, была ли представлена ​​форма (а не слепое «setTimeout»), и это единственное, что безупречно работало для меня. +1 – indextwo

14

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");})); 

Я обнаружил этот подход во время работы над проектом triflejs.org (версия Internet Explorer фантома) пытаемся эмулировать вызовы до trifle.wait(ms) внутри среды PhantomJS.

+0

может 'phantom.page.sendEvent ('mousemove')' работать на мобильной странице? –

+0

@FengYu конечно предмет. «mousemove» - правильное событие для использования. Phantom (например, ваш браузер для рабочего стола) не знает свою мобильную страницу. Все, что он знает, это открытие веб-страницы и ее рендеринг. –

+0

Можно ли удалить 'page.sendEvent ('mousemove')' in while loop? –

0

Внутри 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(); 
});  
Смежные вопросы