WebDriver click() 与 JavaScript click()
- 2024-12-24 08:56:00
- admin 原创
- 89
问题描述:
故事:
在 StackOverflow 上,我看到用户报告说他们无法通过 selenium WebDriver 的“单击”命令单击元素,但可以通过执行脚本使用 JavaScript 单击来解决这个问题。
Python 中的示例:
element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)
WebDriverJS/Protractor 中的示例:
var elm = $("#myid");
browser.executeScript("arguments[0].click();", elm.getWebElement());
问题:
为什么单击“通过 JavaScript”有效,而单击常规 WebDriver 无效?这种情况究竟是什么时候发生的,这种解决方法的缺点是什么(如果有的话)?
我个人使用了这个解决方法,但没有完全理解为什么必须这样做以及它会导致什么问题。
解决方案 1:
与当前接受的答案相反,当谈到让 WebDriver 执行点击和使用 JavaScript 执行点击之间的区别时,PhantomJS 并没有什么特别之处。
区别
这两种方法之间的本质区别对于所有浏览器来说都是相同的,并且可以很简单地解释:
WebDriver:当 WebDriver 执行点击时,它会尽可能地模拟真实用户使用浏览器时发生的情况。假设您有一个元素 A,它是一个按钮,上面写着“点击我”,还有一个元素 B,它是一个
div
透明的元素,但其尺寸和zIndex
设置使其完全覆盖 A。然后您告诉 WebDriver 单击 A。WebDriver 将模拟点击,以便 B首先收到点击。为什么?因为 B 覆盖了 A,如果用户尝试点击 A,那么 B 将首先收到事件。A 最终是否会收到点击事件取决于 B 如何处理该事件。无论如何,在这种情况下 WebDriver 的行为与真实用户尝试点击 A 时的行为相同。JavaScript: 现在,假设你使用 JavaScript 来做
A.click()
。这种点击方式并不能重现用户尝试点击 A 时真正发生的情况。JavaScript直接将事件发送click
给 A,而 B 不会收到任何事件。
为什么 JavaScript 单击有效而 WebDriver 单击无效?
正如我上面提到的,WebDriver 将尽可能地模拟真实用户使用浏览器时发生的情况。事实上,DOM 可能包含用户无法与之交互的元素,而 WebDriver 不允许您单击这些元素。除了我提到的重叠情况之外,这还意味着不可见元素无法被单击。我在 Stack Overflow 问题中看到的一个常见情况是,有人试图与 DOM 中已经存在的 GUI 元素进行交互,但只有当其他元素被操作后才可见。这有时会发生在下拉菜单中:您必须先单击按钮以调出下拉菜单,然后才能选择菜单项。如果有人在菜单可见之前尝试单击菜单项,WebDriver 会犹豫不决并说该元素无法被操作。如果此人随后尝试使用 JavaScript 执行此操作,它将起作用,因为事件直接传递给元素,而与可见性无关。
何时应使用 JavaScript 进行点击?
如果您使用 Selenium测试应用程序,我对这个问题的回答是“几乎从不”。总的来说,您的 Selenium 测试应该重现用户使用浏览器的操作。以下拉菜单为例:测试应该首先单击弹出下拉菜单的按钮,然后单击菜单项。如果 GUI 出现问题(因为按钮不可见,或者按钮无法显示菜单项,或类似情况),那么您的测试将失败,您将检测到错误。如果您使用 JavaScript 进行点击,您将无法通过自动化测试检测到这些错误。
我说“几乎从不”是因为可能存在使用 JavaScript 的例外情况。不过,这种情况应该非常罕见。
如果您使用 Selenium 来抓取网站数据,那么尝试重现用户行为就不那么重要了。因此,使用 JavaScript 绕过 GUI 并不是什么大问题。
解决方案 2:
驱动程序执行的点击会尝试尽可能接近地模拟真实用户的行为,同时 JavaScriptHTMLElement.click()
会执行事件的默认操作click
,即使元素不可交互。
不同之处在于:
驱动程序通过将元素滚动到视图中来确保元素可见,并检查元素是否可交互。
驱动程序将引发错误:
+ 当点击坐标顶部的元素不是目标元素或后代时
+ 当元素没有正尺寸或完全透明时
+ 当元素为禁用输入或按钮时(属性/属性`disabled`为`true`)
+ 当元素禁用鼠标指针时(CSS`pointer-events`为`none`)
如果元素被禁用, JavaScriptHTMLElement.click()
将始终执行默认操作,或者最多会默默失败。
如果元素可聚焦,则驱动程序将元素置于焦点之中。
JavaScript 则HTMLElement.click()
不会。
预计驱动程序将像真实用户一样发出所有事件(mousemove、mousedown、mouseup、click 等)。
JavaScriptHTMLElement.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/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(下拉侧边栏)
解决方法:
添加一个服务员来等待可见性,最小尺寸或稳定位置:
// 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=en
解决方法:
将窗口尺寸设置为较大的尺寸以避免滚动或浮动元素。
将鼠标移到具有负偏移的元素上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);
解决方案 3:
注意:我们将“点击”称为最终用户点击。“js 点击”是通过 JS 点击
为什么单击“通过 JavaScript”有效,而单击常规 WebDriver 无效?
有两种情况会导致这种情况发生:
一、如果你使用 PhamtomJS
那么这是最常见的行为PhantomJS
。有些元素有时不可点击,例如<div>
。这是因为PhantomJS
最初是为了模拟浏览器引擎(如初始 HTML + CSS -> 计算 CSS -> 渲染)而制作的。但它并不意味着以最终用户的方式进行交互(查看、点击、拖动)。因此PhamtomJS
仅部分支持最终用户的交互。
JS CLICK 为何有效?至于任何一种点击,它们都是平均点击。它就像一把有1 个枪管和2 个扳机的枪。一个来自视口,一个来自 JS。由于PhamtomJS
JS 点击可以很好地模拟浏览器引擎,因此它应该可以完美地工作。
II. “点击”事件处理程序在错误的时间段内进行绑定。
例如,我们得到了一个<div>
-> 我们做了一些计算
-> 然后我们将点击事件绑定到
<div>
。-> 加上一些角度错误编码(例如没有正确处理范围的循环)
我们可能最终会得到相同的结果。单击不起作用,因为 WebdriverJS 在没有单击事件处理程序的情况下尝试单击元素。
为什么 JS CLICK 有效? JS CLICK 就像直接将 JS 注入浏览器一样。可能有两种方式,
首先是通过 devtools 控制台(是的,WebdriverJS 确实与 devtools 的控制台通信)。
第二种是直接在 HTML 中注入<script>
标签。
对于每个浏览器,行为都会有所不同。但无论如何,这些方法比单击按钮更复杂。单击是使用已经存在的内容(最终用户单击),js单击是通过后门。
而对于 js 来说,点击会表现为一个异步任务。这又涉及到一个比较复杂的话题‘浏览器异步任务和 CPU 任务调度’(很久以前看过,现在找不到了)。简单来说,这主要是因为 js 点击需要等待一个CPU 任务调度周期,绑定点击事件后,运行速度会慢一些。
(当你发现元素有时可点击,有时不可点击时,你就知道这种情况了。)
这究竟是什么时候发生的,以及这种解决方法的缺点是什么(如果有的话)?
=> 如上所述,两者都意味着一个目的,但关于使用哪个入口:
点击:正在使用浏览器默认提供的。
JS 点击:正在通过后门。
=> 至于性能,很难说,因为它依赖于浏览器。但一般来说:
单击:并不代表速度更快,只是在CPU执行任务的调度列表中签署更高的位置。
JS点击:并不代表速度变慢,只是排在了CPU任务调度列表的最后位置。
=> 缺点:
单击:除了使用 PhamtomJS 之外,似乎没有任何缺点。
JS 点击:对健康非常不利。您可能会意外点击视图中不存在的内容。使用此功能时,请确保元素存在且可供最终用户查看和点击。
PS如果您正在寻找解决方案。
使用 PhantomJS?我建议改用 Chrome headless。是的,您可以在 Ubuntu 上设置 Chrome headless。它的运行方式与 Chrome 一样,但它没有视图,并且错误比 PhantomJS 少。
不使用 PhamtomJS 但仍有问题?我建议使用 Protractor 的 ExpectedCondition
browser.wait()
(查看此处了解更多信息)
(我想写得简短,但结果很糟糕。任何与理论相关的东西都很难解释......)
解决方案 4:
感谢您的详细解释,我遇到了同样的问题,您的解释帮助我解决了这个问题。
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)
解决方案 5:
if (theElement.Enabled)
{
if (!theElement.Selected)
{
var driver = (IJavaScriptExecutor)Driver;
driver.ExecuteScript("arguments[0].click();", theElement); //ok
//theElement.Click();//action performed on theElement, then pops exception
}
}
我不同意我们“几乎永远不会”使用 JS 来模拟点击动作。
上面theElement.Click()
,我们检查单选按钮,但随后会弹出异常,如上图所示。
其实这个Click之后并没有页面加载的动作,点击只是选中Radio按钮,不知道WebDriver为什么Click()
会引发这个异常,有谁能解释一下为什么会发生这个异常吗?
我使用Webdriver 3.141.59和 IE 11 以及selenium-server-standalone-3.141.59.jar进行远程测试。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)