问题:WebDriver click()与JavaScript click()
故事:
在StackOverflow上,我看到用户报告他们无法通过selenium WebDriver“单击”命令单击元素,并且可以通过执行脚本来解决JavaScript单击问题。
Python中的示例:
element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)
WebDriverJS /量角器中的示例:
var elm = $("#myid");
browser.executeScript("arguments[0].click();", elm.getWebElement());
问题:
为什么在常规WebDriver单击不起作用时单击“通过JavaScript”有效?这到底是什么时候发生的,这种解决方法(如果有)的缺点是什么?
我个人使用此变通办法时并未完全理解为什么必须这样做以及它可能导致什么问题。
回答 0
与当前接受的答案所暗示的相反,关于PhantomJS并没有特定于WebDriver单击和使用JavaScript的区别。
区别
两种方法之间的本质区别是所有浏览器都共有的,可以很简单地解释一下:
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单击不起作用时JavaScript单击有效?
正如我上面提到的,WebDriver会尽可能地模拟真实用户使用浏览器时发生的情况。事实是,DOM可以包含用户无法与之交互的元素,并且WebDriver不允许您单击这些元素。除了我提到的重叠情况之外,这还意味着无法单击不可见元素。我在“堆栈溢出”问题中看到的一个常见案例是某人试图与DOM中已经存在的GUI元素进行交互,但只有在处理了其他某些元素后才可见。有时在下拉菜单中会发生这种情况:您必须先单击按钮,然后弹出菜单,然后才能选择菜单项。如果有人尝试在菜单可见之前单击菜单项,如果此人随后尝试使用JavaScript进行操作,那么它将起作用,因为事件是直接传递给元素的,而与可见性无关。
什么时候应该使用JavaScript进行点击?
如果您使用Selenium来测试应用程序,那么我对这个问题的回答是“几乎不会”。总的来说,您的Selenium测试应该重现用户对浏览器的操作。以下拉菜单为例:测试应单击首先显示下拉菜单的按钮,然后单击菜单项。如果由于按钮不可见而导致GUI出现问题,或者按钮无法显示菜单项或类似内容,则测试将失败并且您将检测到该错误。如果使用JavaScript单击鼠标,则将无法通过自动测试检测到这些错误。
我说“几乎从不”是因为使用JavaScript可能会有exceptions。但是,它们应该很少见。
如果您使用Selenium 抓取站点,则尝试重现用户行为并不那么重要。因此,使用JavaScript绕过GUI并不是什么大问题。
回答 1
驱动程序执行的单击尝试在JavaScript HTMLElement.click()
对click
事件执行默认操作时,即使元素不可交互,也尽可能模拟真实用户的行为。
不同之处在于:
驱动程序通过将元素滚动到视图中来确保该元素可见,并检查该元素是否可交互。
驱动程序将引发错误:
- 当点击坐标顶部的元素不是目标元素或后代时
- 当元素没有正尺寸或完全透明时
- 当元素是禁用的输入或按钮(属性/属性
disabled
为true
)时 - 当元素的鼠标指针被禁用(CSS
pointer-events
为none
)时
HTMLElement.click()
如果元素被禁用, JavaScript 将始终执行默认操作,或者至多只会静默失败。如果元素是可聚焦的,则希望驾驶员将其聚焦。
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, ... }
这是JavaScript注入发出的事件:
click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
JavaScript发出的事件
.click()
不受信任,并且默认操作不能被调用:https://developer.mozilla.org/zh/docs/Web/API/Event/isTrusted
https://googlechrome.github.io/samples/event-istrusted/index.html请注意,某些驱动程序仍在生成不受信任的事件。从2.1版开始,PhantomJS就是这种情况。
JavaScript发出的事件
.click()
没有click的坐标。属性
clientX, clientY, screenX, screenY, layerX, layerY
设置为0
。该页面可能依赖于它们,并且可能会有所不同。
可以使用JavaScript .click()
抓取一些数据,但这不是在测试环境中。由于它无法模拟用户的行为,因此无法达到测试的目的。因此,如果来自驱动程序的单击失败,则实际用户很可能也将在相同条件下无法执行相同的单击。
是什么使驱动程序在我们期望成功时无法单击元素?
由于延迟或过渡效果,目标元素尚不可见/不可交互。
一些例子 :
https://developer.mozilla.org/fr/docs/Web(下拉导航菜单) http://materializecss.com/side-nav.html(下拉侧栏)
工作环境:
添加服务员以等待可见性,最小尺寸或稳定位置:
// 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);
重试单击直到成功:
browser.wait(function clickSuccessful() { return elem.click().then(() => true, (ex) => false); }, 5000);
添加与动画/过渡的持续时间匹配的延迟:
browser.sleep(250);
目标元素最终被浮动元素覆盖一旦滚动到视图中:
驱动程序自动将元素滚动到视图中以使其可见。如果页面包含浮动/粘滞元素(菜单,广告,页脚,通知,Cookie策略..),则该元素可能最终被覆盖,将不再可见/不可交互。
示例:https://twitter.com/?lang = zh-CN
解决方法:
将窗口的大小设置为较大的大小,以避免滚动或浮动元素。
Y
将鼠标移到具有负偏移量的元素上,然后单击它:browser.actions() .mouseMove(elem, {x: 0, y: -250}) .click() .perform();
单击之前,将元素滚动到窗口的中心:
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();
如果无法避免,请隐藏浮动元素:
browser.executeScript(function scrollCenter(elem) { elem.style.display = 'none'; }, element);
回答 2
注意:我们将“点击”称为最终用户点击。“ js click”是通过JS点击的
为什么在常规WebDriver单击不起作用时单击“通过JavaScript”有效?
有两种情况会发生这种情况:
I. 如果您正在使用PhamtomJS
这是的最常见的已知行为PhantomJS
。例如,某些元素有时不可单击<div>
。这是因为PhantomJS
最初是用来模拟浏览器引擎的(例如初始HTML + CSS->计算CSS->呈现)。但这并不意味着要以最终用户的方式进行交互(查看,单击,拖动)。因此PhamtomJS
,最终用户交互仅部分支持。
JS为什么点击工作?至于任一点击,它们都是平均点击。它就像带有1个枪管和2个扳机的枪。视口中的一个,JS中的一个。由于可以PhamtomJS
很好地模拟浏览器的引擎,因此JS单击应该可以很好地工作。
二。“ click”的事件处理程序必须在糟糕的时间内绑定。
例如,我们得到了一个 <div>
->我们做一些计算
->然后将click事件绑定到
<div>
。->加上一些错误的角度编码(例如,不能正确处理示波器的周期)
我们可能最终得到相同的结果。单击将不起作用,因为WebdriverJS在没有单击事件处理程序的情况下会尝试单击该元素。
JS为什么点击工作?JS单击就像将JS直接注入浏览器。可能有2种方式,
拳头是通过devtools控制台(是的,WebdriverJS确实与devtools的控制台进行通信)。
其次是将<script>
标记直接注入HTML。
对于每个浏览器,其行为将有所不同。但是无论如何,这些方法比单击按钮更复杂。Click正在使用已经存在的内容(最终用户单击),js click正在通过后门。
对于js,单击将显示为异步任务。这与一个复杂的主题“ 浏览器异步任务和CPU任务调度 ”相关(一会儿再读也找不到文章)。简而言之,这主要是因为js click需要等待CPU的任务调度周期,并且在click事件绑定后运行速度会变慢。 (当您发现元素有时可单击,有时却不可单击时,您可能会知道这种情况。)
这到底是什么时候发生的,这种解决方法(如果有)的缺点是什么?
=>如上所述,两者都意味着一个目的,但是关于使用哪个入口:
- 点击:使用默认情况下提供的浏览器。
- JS click:正在通过后门。
=>对于性能,很难说,因为它依赖于浏览器。但通常:
- 单击:并不意味着更快,而只是在CPU执行任务的计划列表中签名更高的位置。
- JS单击:并不意味着速度较慢,而仅是它登录到CPU任务计划列表的最后位置。
=>缺点:
- 点击:除了您使用PhamtomJS之外,似乎没有其他缺点。
- JS单击:对健康非常不利。您可能不小心单击了视图中没有的内容。使用此功能时,请确保该元素在那里并且可供查看,然后单击以作为最终用户的观点。
PS,如果您正在寻找解决方案。
- 使用PhantomJS?我建议改用无头的Chrome。是的,您可以在Ubuntu上无头设置Chrome。事物的运行方式与Chrome一样,但它没有视图,并且像PhantomJS一样没有虫子。
- 不使用PhamtomJS但仍然有问题吗?我建议使用带有
browser.wait()
(分度器的ExpectedCondition)(检查此以获得更多信息)
(我想简短一点,但结局很糟。与理论有关的任何事情都难以解释…)