WebDriver click() 与 JavaScript click()

IT技术 javascript python selenium selenium-webdriver protractor
2021-02-08 02:32:14

故事:

在 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());

问题:

为什么在常规 WebDriver 单击不起作用时单击“通过 JavaScript”起作用?这究竟是什么时候发生的,这种解决方法的缺点是什么(如果有的话)?

我个人使用了这种解决方法,但没有完全理解我为什么必须这样做以及它会导致什么问题。

5个回答

当前接受的答案所暗示的相反,当涉及到让 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 单击不起作用时,为什么 JavaScript 单击有效?

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

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

如果您使用 Selenium 来测试应用程序,我对这个问题的回答是“几乎从不”。总的来说,您的 Selenium 测试应该重现用户将使用浏览器执行的操作。以下拉菜单为例:测试应该先点击弹出下拉菜单的按钮,然后再点击菜单项。如果 GUI 出现问题,因为按钮不可见,或者按钮无法显示菜单项或类似情况,那么您的测试将失败并且您已经检测到错误。如果您使用 JavaScript 四处点击,您将无法通过自动化测试检测到这些错误。

我说“几乎从不”是因为可能存在使用 JavaScript 有意义的例外情况。不过,它们应该非常罕见。

如果您使用 Selenium 来抓取站点,那么尝试重现用户行为并不那么重要。因此,使用 JavaScript 绕过 GUI 问题不大。

更好的答案,这应该是公认的答案。上面的答案是关于 PhantomJS 特定的边缘情况,恕我直言,这个更准确。
2021-03-12 02:32:14
按计划向 Linh 授予赏金,但我将开始一个新的赏金,以感谢您提供另一个很棒的答案。谢谢。
2021-03-19 02:32:14
@Ardesco 绝对。标记为已接受。完美而详细的答案。
2021-03-24 02:32:14
@Alex 我没有方便的链接。我的回答特别来自 Selenium 的经验,以及一般模拟用户事件的经验。我 5 年前开始使用 Selenium。在我使用 Selenium 的时间里,我不得不阅读 Selenium 的代码来修复一些问题,我提交了很多关于事件调度的错误报告,并与 Selenium 开发人员讨论了这些错误。“尽其所能”是目标。我当然遇到过(现已修复)阻止它实现该目标的错误。可能会保留一些错误。
2021-03-29 02:32:14
非常好的和易于理解的答案。根据我的经验,WebDriver 在处理 AngularJS 页面时遇到了很多问题:对于某些元素,WebDriver 方法喜欢clicksendKeys有效 - 但并非总是如此。没有定位问题或其他异常,测试用例根本没有进一步进展。日志显示该操作已执行。有时使用Action帮助。如果我手动单击元素(在测试用例停止后),一切正常。所以我们在那个时候切换到原始 JS。过去 2 年我没有使用 AngularJS,所以现在情况可能会好一些。
2021-04-08 02:32:14

驱动程序执行的点击尝试尽可能模拟真实用户的行为,而 JavaScriptHTMLElement.click()执行click事件的默认操作,即使元素不可交互。

区别在于:

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

    驱动程序将引发错误:

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


    JavaScriptHTMLElement.click()将始终执行默认操作,或者如果元素被禁用,则充其量只会静默失败。

  • 如果元素可聚焦,驱动程序应将其置于焦点上。

    JavaScriptHTMLElement.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

    请注意,某些驱动程序仍在生成不受信任的事件。从 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=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);
    
这是一个超级有用的解决方案!谢谢
2021-03-17 02:32:14

注意:我们称“点击”是最终用户点击。'js click' 是通过 JS 点击

为什么在常规 WebDriver 单击不起作用时单击“通过 JavaScript”起作用?

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

一、如果你使用的是 PhamtomJS

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

为什么 JS 单击有效?至于任何一种点击,它们都是平均点击。它就像一把带有1 个枪管2 个扳机的枪一个来自视口,一个来自 JS。由于PhamtomJS在模拟浏览器引擎方面很棒,因此 JS 单击应该可以完美工作。

二、“click”的事件处理程序必须在糟糕的时间段内绑定。

例如,我们得到了一个 <div>

  • -> 我们做一些计算

  • -> 然后我们将点击事件绑定到<div>.

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

我们可能会得到相同的结果。单击将不起作用,因为 WebdriverJS 在没有单击事件处理程序时尝试单击该元素。

为什么 JS 单击有效?js点击就像是直接在浏览器中注入js一样。可能有 2 种方式,

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

其次是将<script>标签直接注入 HTML。

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

而对于 js 来说,点击似乎是一个异步任务。这与'浏览器异步任务和CPU任务调度'这个有点复杂的话题有关(看了一会儿又找不到文章了)。简而言之,这主要是因为js单击需要等待CPU的一个任务调度周期,并且在单击事件绑定后运行速度会慢一些。 (当您发现元素有时可点击,有时不可点击时,您可能知道这种情况。)

这究竟是什么时候发生的,这种解决方法的缺点是什么(如果有的话)?

=> 如上所述,两者都意味着一个目的,但关于使用哪个入口:

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

=> 对于性能,很难说,因为它依赖于浏览器。但一般来说:

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

=> 缺点:

  • Click: 除了您使用的是 PhamtomJS 之外,似乎没有任何缺点。
  • JS点击:对健康非常不利。您可能会不小心单击视图中不存在的内容。当您使用它时,请确保该元素在那里并且可以作为最终用户的观点进行查看和单击。

PS,如果您正在寻找解决方案。

  • 使用 PhantomJS?我会建议使用 Chrome 无头代替。是的,您可以在 Ubuntu 上设置 Chrome 无头。Thing 像 Chrome 一样运行,但它没有像 PhantomJS 那样的视图和更少的错误。
  • 不使用 PhamtomJS 但仍有问题?我会建议将量角器的 ExpectedCondition 与browser.wait()查看此信息以获取更多信息

(我想把它缩短,但结果很糟糕。任何与理论相关的东西都很难解释......)

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

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)

在此处输入图片说明

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(),我们将检查 Radio 按钮,但随后会弹出如上图所示的异常。

其实这不是点击后的页面加载动作,点击只是选择Radio按钮,不知道为什么WebDriverClick()会导致这个异常,谁能解释一下为什么会出现这个异常。

我使用Webdriver 3.141.59和 IE 11 以及selenium-server-standalone-3.141.59.jar进行远程测试。