I would use
我会用
waitForElementPresent(locator)
This will wait until the element is present in the DOM.
这将等待元素出现在DOM中。
If you need to check the element is visible, you may be better using
如果需要检查元素是否可见,最好使用
waitForElementHeight(locator)
A more general solution than waiting for an element would be to wait for all the connections to the server to close. This will allow you to wait for all ajax calls to finish, even if they don't have any callback and thus don't affect the page. More details can be found here.
与等待元素相比,更一般的解决方案是等待与服务器的所有连接关闭。这将允许您等待所有的AJAX调用完成,即使它们没有任何回调,因此不会影响页面。更多细节可以在这里找到。
Using C# and jQuery, I have created the following method to wait for all AJax calls to complete (if anyone have more direct ways of accessing JS variables from C#, please comment):
使用C#和jQuery,我创建了以下方法来等待所有AJAX调用完成(如果有人有更直接的方式从C#访问JS变量,请评论):
internal void WaitForAjax(int timeOut = 15)
{
var value = "";
RepeatUntil(
() => value = GetJavascriptValue("jQuery.active"),
() => value == "0",
"Ajax calls did not complete before timeout"
);
}
internal void RepeatUntil(Action repeat, Func<bool> until, string errorMessage, int timeout = 15)
{
var end = DateTime.Now + TimeSpan.FromSeconds(timeout);
var complete = false;
while (DateTime.Now < end)
{
repeat();
try
{
if (until())
{
complete = true;
break;
}
}
catch (Exception)
{ }
Thread.Sleep(500);
}
if (!complete)
throw new TimeoutException(errorMessage);
}
internal string GetJavascriptValue(string variableName)
{
var id = Guid.NewGuid().ToString();
_selenium.RunScript(String.Format(@"window.$('body').append(""<input type='text' value='""+{0}+""' id='{1}'/>"");", variableName, id));
return _selenium.GetValue(id);
}
If using python, you may use this function, which clicks the button and waits for the DOM change:
如果使用python,你可以使用这个函数,它点击按钮并等待DOM的改变:
def click_n_wait(driver, button, timeout=5):
source = driver.page_source
button.click()
def compare_source(driver):
try:
return source != driver.page_source
except WebDriverException:
pass
WebDriverWait(driver, timeout).until(compare_source)
(CREDIT: based on this stack overflow answer)
(点评:基于此堆栈溢出答案)
This work for me
这对我很管用
public void waitForAjax(WebDriver driver) {
new WebDriverWait(driver, 180).until(new ExpectedCondition<Boolean>(){
public Boolean apply(WebDriver driver) {
JavascriptExecutor js = (JavascriptExecutor) driver;
return (Boolean) js.executeScript("return jQuery.active == 0");
}
});
}
With webdriver aka selenium2 you can use implicit wait configuration as mentionned on
https://www.selenium.dev/documentation/en/webdriver/waits/#implicit-wait
使用Web驱动程序(也称为selenium2),您可以使用https://www.selenium.dev/documentation/en/webdriver/waits/#implicit-wait上提到的隐式等待配置
Using Java:
使用Java:
WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));
Or using python:
或者使用Python:
from selenium import webdriver
ff = webdriver.Firefox()
ff.implicitly_wait(10) # seconds
ff.get("http://somedomain/url_that_delays_loading")
myDynamicElement = ff.find_element_by_id("myDynamicElement")
The code (C#) bellow ensures that the target element is displayed:
下面的代码(C#)确保显示目标元素:
internal static bool ElementIsDisplayed()
{
IWebDriver driver = new ChromeDriver();
driver.Url = "http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp";
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
By locator = By.CssSelector("input[value='csharp']:first-child");
IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
{
return d.FindElement(locator);
});
return myDynamicElement.Displayed;
}
If the page supports jQuery it can be used the jQuery.active function to ensure that the target element is retrieved after all the ajax calls are finished:
如果页面支持jQuery,则可以使用jQuery.active函数来确保在所有的aQuery调用完成后检索目标元素:
public static bool ElementIsDisplayed()
{
IWebDriver driver = new ChromeDriver();
driver.Url = "http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp";
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
By locator = By.CssSelector("input[value='csharp']:first-child");
return wait.Until(d => ElementIsDisplayed(d, locator));
}
public static bool ElementIsDisplayed(IWebDriver driver, By by)
{
try
{
if (driver.FindElement(by).Displayed)
{
//jQuery is supported.
if ((bool)((IJavaScriptExecutor)driver).ExecuteScript("return window.$ != undefined"))
{
return (bool)((IJavaScriptExecutor)driver).ExecuteScript("return $.active == 0");
}
else
{
return true;
}
}
else
{
return false;
}
}
catch (Exception)
{
return false;
}
}
I wrote next method as my solution (I hadn't any load indicator):
我编写了Next方法作为我的解决方案(我没有任何加载指示器):
public static void waitForAjax(WebDriver driver, String action) {
driver.manage().timeouts().setScriptTimeout(5, TimeUnit.SECONDS);
((JavascriptExecutor) driver).executeAsyncScript(
"var callback = arguments[arguments.length - 1];" +
"var xhr = new XMLHttpRequest();" +
"xhr.open('POST', '/" + action + "', true);" +
"xhr.onreadystatechange = function() {" +
" if (xhr.readyState == 4) {" +
" callback(xhr.responseText);" +
" }" +
"};" +
"xhr.send();");
}
Then I jsut called this method with actual driver.
More description in this post.
然后我就用实际的驱动程序调用了这个方法。在这篇文章中有更多的描述。
This works like a charm for me :
这对我来说就像是一种魔咒:
public void waitForAjax() {
try {
WebDriverWait driverWait = new WebDriverWait(driver, 10);
ExpectedCondition<Boolean> expectation;
expectation = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driverjs) {
JavascriptExecutor js = (JavascriptExecutor) driverjs;
return js.executeScript("return((window.jQuery != null) && (jQuery.active === 0))").equals("true");
}
};
driverWait.until(expectation);
}
catch (TimeoutException exTimeout) {
// fail code
}
catch (WebDriverException exWebDriverException) {
// fail code
}
return this;
}
I had a similar situation, i wanted to wait for ajax requests so that the loading panel would have disappeared, I have inspected the html before and after the requests, found that there is a div for the ajax loading panel, the dix is displayed during the ajax request, and hidden after the request ends. I have created a function to wait for the panel to be displayed, then wait for it to be hidden
我遇到过类似的情况,我想等待AJAX请求,这样加载面板就会消失,我检查了请求前后的html,发现有一个用于AJAX加载面板的div,DIX在AJAX请求期间显示,并在请求结束后隐藏。我已经创建了一个函数来等待面板显示,然后等待它被隐藏
public void WaitForModalPanel()
{
string element_xpath = ".//*[@id='ajaxLoadingModalPanelContainer' and not(contains(@style,'display: none'))]";
WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 2, 0));
wait.Until(ExpectedConditions.ElementIsVisible(By.XPath(element_xpath)));
element_xpath = ".//*[@id='ajaxLoadingModalPanelContainer' and contains(@style,'DISPLAY: none')]";
wait.Until(ExpectedConditions.ElementExists(By.XPath(element_xpath)));
}
Check this for more details
有关更多详细信息,请查看此处
Here's a groovy version based on Morten Christiansen's answer.
这是一个基于莫滕·克里斯汀森答案的时髦版本。
void waitForAjaxCallsToComplete() {
repeatUntil(
{ return getJavaScriptFunction(driver, "return (window.jQuery || {active : false}).active") },
"Ajax calls did not complete before timeout."
)
}
static void repeatUntil(Closure runUntilTrue, String errorMessage, int pollFrequencyMS = 250, int timeOutSeconds = 10) {
def today = new Date()
def end = today.time + timeOutSeconds
def complete = false;
while (today.time < end) {
if (runUntilTrue()) {
complete = true;
break;
}
sleep(pollFrequencyMS);
}
if (!complete)
throw new TimeoutException(errorMessage);
}
static String getJavaScriptFunction(WebDriver driver, String jsFunction) {
def jsDriver = driver as JavascriptExecutor
jsDriver.executeScript(jsFunction)
}
As mentioned above you can wait for active connections to get closed:
如上所述,您可以等待活动连接关闭:
private static void WaitForReady() {
WebDriverWait wait = new WebDriverWait(webDriver, waitForElement);
wait.Until(driver => (bool)((IJavaScriptExecutor)driver).ExecuteScript("return jQuery.active == 0"));
}
My observation is this is not reliable as data transfer happens very quickly. Much more time is consumed on data processing and rendering on the page and even jQuery.active == 0
data might not be yet on the page.
我的观察是,这是不可靠的,因为数据传输非常快。在页面上的数据处理和呈现上花费的时间要多得多,甚至jQuery.active==0数据可能还没有出现在页面上。
Much wiser is to use an explicit wait for element to be shown on the page, see some of the answers related to this.
更明智的做法是使用显式的等待元素显示在页面上,请参阅与此相关的一些答案。
The best situation is if your web application have some custom loader or indication that data is being processed. In this case you can just wait for this indication to hide.
最好的情况是,如果您的Web应用程序有一些自定义加载器或正在处理数据的指示。在这种情况下,您可以等待此指示隐藏。
If the control you are waiting for is an "Ajax" web element, the following code will wait for it, or any other Ajax web element to finish loading or performing whatever it needs to do so that you can more-safely continue with your steps.
如果您等待的控件是“AJAX”Web元素,下面的代码将等待它或任何其他AJAX Web元素完成加载或执行它需要做的任何事情,以便您可以更安全地继续您的步骤。
public static void waitForAjaxToFinish() {
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver wdriver) {
return ((JavascriptExecutor) driver).executeScript(
"return jQuery.active == 0").equals(true);
}
});
}
Below is my code for fetch. Took me while researching because jQuery.active doesn't work with fetch. Here is the answer helped me proxy fetch, but its only for ajax not fetch mock for selenium
下面是我的FETCH代码。因为jQuery.active不能与FETCH一起工作,所以我在研究时使用了它。以下是帮助我代理提取的答案,但它只适用于AJAX,而不适用于Selence
public static void customPatchXMLHttpRequest(WebDriver driver) {
try {
if (driver instanceof JavascriptExecutor) {
JavascriptExecutor jsDriver = (JavascriptExecutor) driver;
Object numberOfAjaxConnections = jsDriver.executeScript("return window.openHTTPs");
if (numberOfAjaxConnections instanceof Long) {
return;
}
String script = " (function() {" + "var oldFetch = fetch;"
+ "window.openHTTPs = 0; console.log('starting xhttps');" + "fetch = function(input,init ){ "
+ "window.openHTTPs++; "
+ "return oldFetch(input,init).then( function (response) {"
+ " if (response.status >= 200 && response.status < 300) {"
+ " window.openHTTPs--; console.log('Call completed. Remaining active calls: '+ window.openHTTPs); return response;"
+ " } else {"
+ " window.openHTTPs--; console.log('Call fails. Remaining active calls: ' + window.openHTTPs); return response;"
+ " };})" + "};" + "var oldOpen = XMLHttpRequest.prototype.open;"
+ "XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {"
+ "window.openHTTPs++; console.log('xml ajax called');"
+ "this.addEventListener('readystatechange', function() {" + "if(this.readyState == 4) {"
+ "window.openHTTPs--; console.log('xml ajax complete');" + "}" + "}, false);"
+ "oldOpen.call(this, method, url, async, user, pass);" + "}" +
"})();";
jsDriver.executeScript(script);
} else {
System.out.println("Web driver: " + driver + " cannot execute javascript");
}
} catch (Exception e) {
System.out.println(e);
}
}
In my case the issue seemed due to ajax delays but was related to internal iframes inside the main page.
In seleminum it is possible to switch to internal frames with:
在我的例子中,这个问题似乎是由于AJAX延迟造成的,但与主页内部的iframe有关。在selinum中,可以通过以下方式切换到内部框架:
driver.switchTo().frame("body");
driver.switchTo().frame("bodytab");
I use java. After that I was able to locate the element
我用爪哇咖啡。在那之后,我找到了那个元素
driver.findElement(By.id("e_46")).click();
The solution below can be used for cases where jQuery is not available.
下面的解决方案可以用于jQuery不可用的情况。
By actively monitoring the logs, we can ensure that all files and responses have been completed with certainty.
通过积极监控日志,我们可以确保所有文件和回复都已确定完成。
I have tested this solution for Chrome, but you should also ensure it is supported in other browsers. For example, Safari does not support this method.
我已经为Chrome测试了这个解决方案,但你也应该确保它在其他浏览器中得到支持。例如,Safari不支持此方法。
Also, don't forget to set the capability for these logs to be visible.
另外,不要忘记将这些日志的功能设置为可见。
LoggingPreferences preferences = new LoggingPreferences();
preferences.enable(LogType.PERFORMANCE, Level.ALL);
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setCapability("goog:loggingPrefs", preferences);
You should use with WebDriverWait instance. After that, just call the waitUntilPageIsReady() method.
应与WebDriverWait实例一起使用。之后,只需调用waitUntilPageIsReady()方法。
public class IsPageReady implements ExpectedCondition<Boolean> {
private Logger log = LogManager.getLogger(IsPageReady.class);
@NullableDecl
@Override
public Boolean apply(@NullableDecl WebDriver webDriver) {
try {
if (webDriver == null)
return false;
LogEntries logs = WebDriverManager.getInstance().getWebDriver().manage().logs().get(LogType.PERFORMANCE);
return ready = logs.getAll().isEmpty();
} catch (Exception ex) {
log.warn("There was a problem checking if the page was ready!");
}
return false;
}
}
just call:
只需拨打:
public void waitUntilPageIsReady() {
getWebDriverWait().until(new IsPageReady());
}
It is possible to capture this by writing a custom HTTP listener in JavaScript as follows. For those who wish to examine this script, you can check out this link: https://gist.github.com/erdemkiiliic/3e3caa666ca1c950c4db3e94a8d3f049
可以通过如下所示用JavaScript编写一个定制的HTTP侦听器来实现这一点。对于那些希望研究此脚本的人,您可以查看此链接:https://gist.github.com/erdemkiiliic/3e3caa666ca1c950c4db3e94a8d3f049
public static void waitForAjaxToFinish() {
String customScriptForHTTPListener = "if(void 0===window.jsXMLHTTPListener){let i={isCallFinishedStatus:1,pointerSize:0,pointerFinishSize:0,isCallFinished:function(){return 1===this.isCallFinishedStatus},setCallFinished:function(i=0){this.isCallFinishedStatus=i},bindEvents:function(i,e){var t=e.XMLHttpRequest.prototype.send;e.XMLHttpRequest.prototype.send=function(){i.setCallFinished(0);var s=this;i.pointerSize++;var n=e.setInterval(function(){4==s.readyState&&(i.pointerFinishSize++,i.pointerSize===i.pointerFinishSize?i.setCallFinished(1):i.setCallFinished(0),clearInterval(n))},1);return t.apply(this,[].slice.call(arguments))}},init:function(){this.bindEvents(this,window)}};window.jsXMLHTTPListener=i,window.jsXMLHTTPListener.init();let e=window.jsXMLHTTPListener.isCallFinished();console.log(e);return e;}else{let t=window.jsXMLHTTPListener.isCallFinished();console.log(t);return t;}";
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(new ExpectedCondition < Boolean > () {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState").equals("complete") &&
((JavascriptExecutor) driver).executeScript(
customScriptForHTTPListener).equals(true);
}
});
}
This script returns either true or false. If there is no active XHR request at that moment, it returns true.
此脚本返回TRUE或FALSE。如果此时没有活动的XHR请求,则返回TRUE。
For those who is using primefaces, just do:
对于那些使用PrimeFaces的人,只需这样做:
selenium.waitForCondition("selenium.browserbot.getCurrentWindow().$.active==0", defaultWaitingPeriod);
更多回答
What if ajax does redirect and selenium doesn't know about it? How to work with that?
如果AJAX确实重定向,而Selify不知道呢?如何应对这一点?
this.selenium.GetEval("(window.jQuery || { active : 0 }).active ") is a little more direct than the input box. It also doesn't error if jQuery is not on the page.
与输入框相比,this.selenium.GetEval(“(window.jQuery||{active:0}).active”)更直接一些。如果jQuery不在页面上,它也不会出错。
What is the type of the variable __selenium? I am not able to fin it in the object model
变量__SENSE的类型是什么?我无法在对象模型中找到它
Mauricio, it's been quite a while since I worked with Selenium so I can't recall the details, but my guess would be the IWebDriver instance.
Mauricio,我已经有很长一段时间没有使用Selify了,所以我记不起细节了,但我猜应该是IWebDriver实例。
@AskarKalykov exactly.
@AskarKalykov。
Unfortunately some pages have loading animations which ruin this trick because page_source is updated but you don't have the data yet.
不幸的是,一些页面的加载动画破坏了这一技巧,因为Page_source已经更新,但您还没有数据。
It worked for me, I have a page with multiple XHR requests at on single button click, so just waiting for jQuery.active == 0
does not work.
这对我来说很有效,我有一个页面,只需按一下按钮,就会有多个XHR请求,所以只等待jQuery.active==0是行不通的。
Thanks @nimo, this works well. I had a loading animation on my page so I modified the lines driver.page_source
to driver.find_element_by_xpath(comparator_xpath).get_attribute('innerHTML')
. This way it didn't matter what was going on when the loading element was in animation.
谢谢@nimo,这个运行得很好。我的页面上有一个正在加载的动画,所以我将行driver.page_SOURCE修改为driver.find_element_by_xpath(comparator_xpath).get_attribute(‘innerHTML’).这样,当加载元素处于动画中时,发生什么都无关紧要。
downvote. answers are supposed to contain actual answers... they are not for driving traffic to your personal blog.
投反对票。答案应该包含实际的答案。它们不是用来给你的个人博客带来流量的。
@CoreyGoldberg I dont care about traffic, but Ok, I'll insert code from post here. This post is only another option, because other answers wont worked for me.
@CoreyGoldberg我不在乎流量,但好吧,我会在这里插入POST代码。这个帖子只是另一种选择,因为其他答案对我来说都不起作用。
@CoreyGoldberg I hope that now it is OK for you ;)
@CoreyGoldberg我希望现在对你来说没问题;)
Please try to add some explanation over your code on how you think this code solve the OP's problem.
请尝试在您的代码上添加一些解释,说明您认为此代码如何解决OP的问题。
waitForCondition void waitForCondition(java.lang.String script, java.lang.String timeout) Runs the specified JavaScript snippet repeatedly until it evaluates to "true". The snippet may have multiple lines, but only the result of the last line will be considered. Note that, by default, the snippet will be run in the runner's test window, not in the window of your application. To get the window of your application, you can use the JavaScript snippet selenium.browserbot.getCurrentWindow(), and then run your JavaScript in there
WaitForCondition void waitForCondition(java.lang.String脚本,java.lang.StringTimeout)重复运行指定的JavaScript代码段,直到它的计算结果为“true”。代码段可能有多行,但只考虑最后一行的结果。请注意,默认情况下,代码段将在运行者的测试窗口中运行,而不是在应用程序的窗口中运行。要获得应用程序的窗口,您可以使用JavaScript代码片段selenium.browserbot.getCurrentWindow(),然后在其中运行您的JavaScript
it is only for Selenium RC. Selenium WebDriver doesnot support feature
它只适用于硒RC。Selify WebDriver不支持此功能
Is WebDriver the same as IDE ?
WebDriver和IDE一样吗?
我是一名优秀的程序员,十分优秀!