ale*_*cxe 119 javascript python selenium selenium-webdriver protractor
故事:
在StackOverflow上,我看到用户报告他们无法通过selenium WebDriver"click"命令单击一个元素,并且可以通过执行脚本单击JavaScript来解决它.
Python中的示例:
element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)
Run Code Online (Sandbox Code Playgroud)
WebDriverJS/Protractor中的示例:
var elm = $("#myid");
browser.executeScript("arguments[0].click();", elm.getWebElement());
Run Code Online (Sandbox Code Playgroud)
问题:
为什么在常规WebDriver点击时没有点击"通过JavaScript"?究竟是什么时候发生这种变通方法的缺点(如果有的话)?
我个人使用这种解决方法而没有完全理解为什么我必须这样做以及它可能导致什么问题.
Lou*_*uis 134
与目前接受的答案所暗示的相反,当涉及到WebDriver执行单击并在JavaScript中执行此操作之间的区别时,PhantomJS没有任何特定内容.
这两种方法之间的本质区别在于所有浏览器都很常见,可以简单地解释一下:
WebDriver:当WebDriver执行单击时,它会尽可能地模拟真实用户使用浏览器时发生的情况.假设你有一个元素A,它是一个按钮,上面写着"点击我",一个元素B是一个div透明的元素,但是它的尺寸和zIndex设置使它完全覆盖A.然后你告诉WebDriver点击A. WebDriver将模拟点击,以便B 首先接收点击.为什么?因为B覆盖A,并且如果用户试图点击A,那么B将首先获得该事件.A是否最终会获得click事件取决于B如何处理事件.无论如何,在这种情况下,WebDriver的行为与真实用户尝试单击A时的行为相同.
JavaScript:现在,假设你使用JavaScript来做A.click().这种单击方法不会重现用户尝试单击A时实际发生的情况 .JavaScript将click事件直接发送给A,B不会获取任何事件.
正如我上面提到的,WebDriver将尽力模拟真实用户使用浏览器时会发生什么.事实上,DOM可以包含用户无法与之交互的元素,而WebDriver将不允许您单击这些元素.除了我提到的重叠案例,这也需要不能点击隐形元素.我在Stack Overflow问题中看到的一个常见情况是尝试与已经存在于DOM中的GUI元素进行交互,但只有在操作了某些其他元素时才会显示.这有时会发生在下拉菜单中:您必须首先单击按钮,然后在选择菜单项之前调出下拉列表.如果有人在菜单可见之前尝试单击菜单项,WebDriver将拒绝并说该元素无法操作.如果该人然后尝试使用JavaScript,它将起作用,因为事件直接传递给元素,而不管可见性如何.
如果您使用Selenium 测试应用程序,我对这个问题的回答"几乎从不".总的来说,您的Selenium测试应该重现用户对浏览器的操作.以下拉菜单为例:测试应该首先点击下拉按钮,然后单击菜单项.如果GUI出现问题,因为按钮不可见,或者按钮无法显示菜单项或类似内容,那么您的测试将失败并且您将检测到该错误.如果您使用JavaScript进行单击,则无法通过自动化测试检测到这些错误.
我说"几乎从不",因为可能存在使用JavaScript有意义的例外情况.但它们应该非常罕见.
如果您使用Selenium来抓取网站,那么尝试重现用户行为并不重要.因此,使用JavaScript绕过GUI不是一个问题.
Flo*_* B. 29
当JavaScript HTMLElement.click()执行click事件的默认操作时,驱动程序执行的单击尝试尽可能接近地模拟真实用户的行为,即使该元素不可交互.
不同之处是:
驱动程序通过将元素滚动到视图中来确保元素是可见的,并检查元素是否可交互.
驱动程序将引发错误:
disabled是true)pointer-events为none)
JavaScript HTMLElement.click()将始终执行默认操作,或者如果元素被禁用,则最多会无声地失败.
如果可以集中精力,那么驱动程序应该将元素置于焦点上.
JavaScript HTMLElement.click()不会.
期望驱动程序像真实用户一样发出所有事件(mousemove,mousedown,mouseup,click,...).
JavaScript HTMLElement.click()只发出click事件.页面可能依赖于这些额外事件,如果不发出这些事件可能会有不同的行为.
这些是驱动程序针对Chrome点击发出的事件:
mouseover {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
mousemove {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
mousedown {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
mouseup {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
click {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
Run Code Online (Sandbox Code Playgroud)
这是使用JavaScript注入发出的事件:
click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
Run Code Online (Sandbox Code Playgroud)JavaScript发出的事件.click() 不受信任,可能不会调用默认操作:
https://developer.mozilla.org/en/docs/Web/API/Event/isTrusted
https://googlechrome.github.io/samples/event-istrusted/index.html
请注意,某些驱动程序仍在生成不受信任的事件.PhantomJS从2.1版开始就是这种情况.
JavaScript发出的事件.click() 没有点击的坐标.
属性clientX, clientY, screenX, screenY, layerX, layerY设置为0.页面可能依赖于它们,并且可能表现不同.
使用JavaScript .click()来废弃某些数据可能没问题,但它不在测试环境中.它违背了测试的目的,因为它不模拟用户的行为.因此,如果来自驱动程序的单击失败,那么真实用户很可能也无法在相同条件下执行相同的单击.
什么使得驱动程序在我们期望它成功时无法单击元素?
由于延迟或过渡效应,目标元素尚未可见/可交互.
一些例子 :
https://developer.mozilla.org/fr/docs/Web(下拉导航菜单) http://materializecss.com/side-nav.html(下拉侧栏)
Workarrounds:
添加服务员等待可见性,最小尺寸或稳定位置:
// wait visible
browser.wait(ExpectedConditions.visibilityOf(elem), 5000);
// wait visible and not disabled
browser.wait(ExpectedConditions.elementToBeClickable(elem), 5000);
// wait for minimum width
browser.wait(function minimumWidth() {
return elem.getSize().then(size => size.width > 50);
}, 5000);
Run Code Online (Sandbox Code Playgroud)
重试单击直到成功为止:
browser.wait(function clickSuccessful() {
return elem.click().then(() => true, (ex) => false);
}, 5000);
Run Code Online (Sandbox Code Playgroud)
添加与动画/过渡持续时间匹配的延迟:
browser.sleep(250);
Run Code Online (Sandbox Code Playgroud)
一旦滚动到视图中,目标元素最终会被浮动元素覆盖:
驱动程序会自动将元素滚动到视图中以使其可见.如果页面包含浮动/粘性元素(菜单,广告,页脚,通知,Cookie政策......),则该元素可能会被最终覆盖,并且将不再可见/可交互.
示例:https://twitter.com/?lang = en
解决方法:
将窗口的大小设置为较大的值以避免滚动或浮动元素.
移动带负Y偏移的元素,然后单击它:
browser.actions()
.mouseMove(elem, {x: 0, y: -250})
.click()
.perform();
Run Code Online (Sandbox Code Playgroud)
在单击之前将元素滚动到窗口的中心:
browser.executeScript(function scrollCenter(elem) {
var win = elem.ownerDocument.defaultView || window,
box = elem.getBoundingClientRect(),
dy = box.top - (win.innerHeight - box.height) / 2;
win.scrollTo(win.pageXOffset, win.pageYOffset + dy);
}, element);
element.click();
Run Code Online (Sandbox Code Playgroud)
如果无法避免,请隐藏浮动元素:
browser.executeScript(function scrollCenter(elem) {
elem.style.display = 'none';
}, element);
Run Code Online (Sandbox Code Playgroud)Lin*_*ham 16
注意:我们称之为"点击"是最终用户点击.'js click'是通过JS点击
为什么在常规WebDriver点击时没有点击"通过JavaScript"?
有两种情况发生:
然后这是最常见的已知行为PhantomJS.例如,某些元素有时不可点击<div>.这是因为它PhantomJS是用于模拟浏览器引擎的原始版本(如初始HTML + CSS - >计算CSS - >渲染).但这并不意味着以最终用户的方式(查看,点击,拖动)进行交互.因此PhamtomJS,只是部分支持最终用户的交互.
为什么JS点击工作?至于任何一次点击,它们都是平均点击.它就像一支有1个枪管和2个触发器的枪.一个来自视口,一个来自JS.由于PhamtomJS很好地模拟了浏览器的引擎,JS点击应该可以完美地运行.
例如,我们得到了一个 <div>
- >我们做一些计算
- >然后我们将点击事件绑定到<div>.
- >加上一些错误的角度编码(例如,没有处理范围的循环)
我们可能会得到相同的结果.单击将不起作用,因为WebdriverJS在没有单击事件处理程序时尝试单击该元素.
为什么JS点击工作?Js click就像将js直接注入浏览器一样.可能有两种方式,
拳头是通过devtools控制台(是的,WebdriverJS与devtools的控制台通信).
第二是将<script>标签直接注入html.
对于每个浏览器,行为将是不同的.但无论如何,这些方法比点击按钮更复杂.点击是使用已经存在的(最终用户的点击),js点击正在通过后门.
对于js,click似乎是一个asyncronus任务.这与" 浏览器异步任务和CPU任务调度 " 有点复杂的主题相关(读了一段时间后再找不到文章).简而言之,这主要是因为js click需要等待CPU的任务调度周期,并且在点击事件绑定后它会运行得慢一些. (当您发现元素有时可点击时,您可能知道这种情况,有时不会.)
究竟是什么时候发生这种变通方法的缺点(如果有的话)?
=>如上所述,两者都意味着一个目的,但关于使用哪个入口:
=>对于性能,很难说因为它依赖于浏览器.但一般来说:
=>缺点:
browser.wait()(查看更多信息)(我想缩短它,但结果很糟糕.任何与理论相关的事情都很难解释......)
小智 5
感谢您的精彩解释,我遇到了同样的问题,您的解释帮助解决了我的问题。
button = driver.wait.until(EC.presence_of_element_located(
(By.XPATH, "//div[@id='pagination-element']/nav[1]/span[3]/button[1]/span[1]/i[1]")
))
driver.execute_script("arguments[0].click();", button)
Run Code Online (Sandbox Code Playgroud)