WebDriver click() vs JavaScript click()

故事:

在 StackOverflow 这里,我看到用户报告说,他们不能通过 selenium WebDriver 的“ click”命令单击一个元素,而是可以通过执行脚本使用 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());

问题:

为什么单击“ via JavaScript”可以工作,而常规的 WebDriver 单击却不能?什么时候会发生这种情况,这种变通方法的缺点是什么(如果有的话) ?

我个人在没有完全理解为什么我必须这样做以及它会导致什么问题的情况下使用了这个变通方法。

63518 次浏览

注意: 我们称“ click”为最终用户单击。“ JS click”是通过 JS 单击

为什么单击“ via JavaScript”可以工作,而常规的 WebDriver 单击却不能?

有两种情况会发生这种情况:

I. 如果您正在使用 PhamtomJS

那么这就是 PhantomJS最常见的行为。有些元素有时无法单击,例如 <div>。这是因为 PhantomJS最初是用来模拟浏览器引擎的(比如初始 HTML + CSS-> 计算 CSS-> 渲染)。但是它并不意味着以最终用户的方式(查看、点击、拖动)进行交互。因此,PhamtomJS只受到终端用户交互的部分支持。

为什么 JS CLICK 有效?至于任何一次点击,它们都是均匀点击。它就像一把带有 一桶两个触发器的枪。一个来自视窗,一个来自 JS。由于 PhamtomJS在模拟浏览器引擎方面非常出色,所以一个 JS 单击应该可以很好地工作。

“点击”事件处理器在不良时间段进行绑定。

例如,我们得到了 <div>

  • 我们做了一些计算

  • 然后我们将单击事件绑定到 <div>

  • - > 加上一些错误的角度编码(例如,没有正确处理范围的周期)

我们可能会得到同样的结果。Click 不起作用,因为 WebdriverJS 试图在没有 Click 事件处理程序的元素上单击。

为什么 JS 单击可以工作? JS 单击就像直接将 JS 注入浏览器,

首先 是通过 devtools 控制台(是的,WebdriverJS 确实与 devtools 的控制台通信)。

第二个 是将一个 <script>标记直接注入到 HTML 中。

对于每个浏览器,其行为都会有所不同。但无论如何,这些方法比单击按钮更复杂。Click 是使用已经存在的东西(最终用户单击) ,js Click 是通过后门进行的。

对于 js,click 看起来是一个异步任务。这与一个比较复杂的题目“ 浏览器异步任务和 CPU 任务调度”有关(前段时间读过后再也找不到这篇文章了)。简而言之,这主要是因为 js click 需要等待一个 中央处理机任务调度周期,并且在 click 事件绑定之后运行速度会稍微慢一些。 (当您发现元素有时可以单击,有时不可以单击时,您可以知道这种情况。 )

到底什么时候会发生这种情况以及这种情况的负面影响是什么 (如有的话) ?

正如上面提到的,两者的意思都是为了一个目的,但是关于使用哪个入口:

  • 单击: 使用浏览器默认提供的内容。
  • JS click: 正在走后门。

就性能而言,这很难说,因为它依赖于浏览器。但一般来说:

  • 单击: 并不意味着更快,只是在 CPU 执行任务的调度列表中签署了更高的位置。
  • JS click: 并不意味着更慢,只是它签入了 CPU 任务调度列表的最后一个位置。

= > 缺点:

  • Click: 除了使用 PhamtomJS 之外,似乎没有任何缺点。
  • JS click: very bad for health.你可能会不小心点击视图上没有的东西。当您使用它时,请确保元素已经存在并且可以作为最终用户的视点查看和单击。

另外,如果你正在寻找一个解决方案。

  • 使用 PhantomJS?我建议使用 Chrome 无头浏览器。是的,你可以在 Ubuntu 上设置 Chrome 的无头版本。它的运行方式和 Chrome 类似,但只是没有视图,不像 PhantomJS 那样漏洞百出。
  • 没有使用 PhamtomJS 但仍然有问题吗? 我建议使用 browser.wait()(看看这个,了解更多信息)的量角器的期望条件

(我想长话短说,但结果很糟糕。任何与理论相关的东西都很难解释清楚... ...)

目前接受的答案的建议相反,当涉及到让 WebDriver 执行单击操作和用 JavaScript 执行操作之间的区别时,PhantomJS 没有任何特定的内容。

区别

这两种方法的本质区别对于所有浏览器都是相同的,并且可以很简单地解释:

  • WebDriver: 当 WebDriver 执行单击操作时,它会尽可能地模拟真实用户使用浏览器时发生的情况。假设你有一个元素 A,它是一个说“ Click me”的按钮,还有一个元素 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 不会得到任何事件。

为什么 JavaScript 单击可以工作而 WebDriver 单击不能?

正如我上面提到的,WebDriver 将尽可能地模拟真实用户使用浏览器时发生的情况。事实上,DOM 可能包含用户无法交互的元素,WebDriver 不允许您单击这些元素。除了我提到的重叠情况之外,这还意味着无法单击不可见的元素。我在 Stack Overflow 问题中看到的一个常见情况是,有人试图与已经存在于 DOM 中的 GUI 元素进行交互,但只有在操纵了其他一些元素之后才能看到这个 GUI 元素。这种情况有时会发生在下拉菜单: 你必须首先点击按钮,弹出下拉菜单项之前,可以选择。如果有人试图在菜单可见之前单击菜单项,WebDriver 将阻止并表示该元素不能被操纵。如果接下来用户尝试使用 JavaScript 执行此操作,那么它将会工作,因为事件将直接传递给元素,而与可见性无关。

什么时候应该使用 JavaScript 进行单击?

如果您将 Selenium 用于 测试应用程序,我对这个问题的答案是 “几乎从未”。,总的来说,您的 Selenium 测试应该重现用户使用浏览器所做的事情。以下拉菜单为例: 测试应该首先单击打开下拉菜单的按钮,然后单击菜单项。如果 GUI 出现了问题,因为按钮是不可见的,或者按钮没有显示菜单项,或者类似的情况,那么您的测试将会失败,并且您已经检测到了 bug。如果您使用 JavaScript 进行单击,那么您将无法通过自动化测试来检测这些 bug。

我说“几乎从来没有”是因为可能会有例外,在这种情况下使用 JavaScript 是有意义的。不过应该很稀有。

如果对 清理现场使用 Selenium,那么尝试重现用户行为就不那么重要了。因此,使用 JavaScript 绕过 GUI 不是什么大问题。

驱动程序执行的单击试图尽可能接近真实用户的行为,而 JavaScript HTMLElement.click()执行 click事件的默认操作,即使元素不可交互。

区别在于:

  • 驱动程序通过将元素滚动到视图中并检查该元素是否为 互动的来确保该元素为 是可见的

    驱动程序将出现一个错误:

    • 当位于点击坐标顶部的元素不是目标元素或后代元素时
    • 当元素没有正的大小或者它是完全透明的时候
    • 当元素是禁用的输入或按钮时(属性/属性 disabledtrue)
    • 当元素禁用鼠标指针时(CSS pointer-eventsnone)


    JavaScriptHTMLElement.click()将始终执行默认操作,如果元素是禁用的,则最多默认失败。< br > < br >

  • 如果驱动程序是可对焦的,那么它应该是 使元素聚焦

    JavaScript HTMLElement.click()不会。 < br >

  • 驱动程序应该像一个真正的用户一样执行 发射所有事件(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, ... }
    
  • The event emitted by a JavaScript .click() is not trusted and the default action may not be invoked:

    https://developer.mozilla.org/en/docs/Web/API/Event/isTrusted
    https://googlechrome.github.io/samples/event-istrusted/index.html

    Note that some of the drivers are still generating untrusted events. This is the case with PhantomJS as of version 2.1.

  • The event emitted by a JavaScript .click() doesn't have the coordinates of the click.

    The properties clientX, clientY, screenX, screenY, layerX, layerY are set to 0. The page might rely on them and might behave differently.


It may be ok to use a JavaScript .click() to scrap some data, but it is not in a testing context. It defeats the purpose of the test since it doesn't simulate the behavior of a user. So, if the click from the driver fails, then a real user will most likely also fail to perform the same click in the same conditions.


What makes the driver fail to click an element when we expect it to succeed?

  • The targeted element is not yet visible/interactable due to a delay or a transition effect.

    Some examples :

    https://developer.mozilla.org/fr/docs/Web (dropdown navigation menu) http://materializecss.com/side-nav.html (dropdown side bar)

    Workarrounds:

    Add a waiter to wait for the visibility, a minimum size or a steady position :

    // 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);
    

谢谢你的好的解释,我遇到了同样的问题,你的解释帮助解决了我的问题。

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)

enter image description here

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()会导致这个异常,谁能解释为什么这个异常发生。

我使用 网络司机3.141.59、 IE11和 Selenium-server-stanalone-3.141.59. jar进行远程测试。