【问题标题】:WebDriver click() vs JavaScript click()WebDriver click() 与 JavaScript click()
【发布时间】:2016-04-06 08:21:59
【问题描述】:

故事:

在 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”起作用?这究竟是什么时候发生的,这种解决方法的缺点是什么(如果有的话)?

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

【问题讨论】:

    标签: javascript python selenium selenium-webdriver protractor


    【解决方案1】:

    currently accepted answer 所建议的相反,PhantomJS 并没有什么特别之处,因为它涉及到让 WebDriver 执行点击和在 JavaScript 中执行它之间的区别。

    区别

    这两种方法的本质区别是所有浏览器都共有的,可以很简单地解释:

    • 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 不会收到任何事件。

      李>

    为什么当 WebDriver 点击无效时 JavaScript 点击有效?

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

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

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

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

    如果您将 Selenium 用于抓取网站,那么尝试重现用户行为就不是那么重要了。因此,使用 JavaScript 绕过 GUI 问题不大。

    【讨论】:

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

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

    区别在于:

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

      驱动会报错:

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


      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, ... }
      

      这是通过 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() 发出的事件没有点击的坐标

      属性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 click'是通过JS点击

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

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

    我。 如果您使用的是 PhamtomJS

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

    为什么 JS CLICK 有效? 至于任何一次点击,它们都是平均点击。它就像一把枪,有 1 个枪管2 个扳机。一个来自视口,一个来自 JS。由于PhamtomJS 非常适合模拟浏览器的引擎,所以 JS 点击应该可以完美运行。

    二。 “点击”的事件处理程序必须在错误的时间段内绑定。

    例如,我们得到了<div>

    • ->我们做一些计算

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

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

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

    为什么 JS 点击会起作用? js 点击就像将 js 直接注入浏览器。可能有两种方式,

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

    第二是将<script>标签直接注入HTML。

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

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

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

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

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

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

    • 点击:并不意味着更快,只是在CPU执行任务的调度列表中签名更高的位置。
    • JS点击:不代表慢,只是登录到CPU任务调度列表的最后一个位置。

    => 缺点:

    • 点击:除了您使用的是 PhamtomJS 之外,似乎没有任何缺点。
    • JS 点击:对健康非常不利。您可能会不小心点击视图中不存在的内容。使用此功能时,请确保该元素存在且可供最终用户查看和点击。

    附言如果您正在寻找解决方案。

    • 使用 PhantomJS?我会建议改用无头 Chrome。是的,您可以在 Ubuntu 上设置 Chrome 无头。 Thing 的运行方式与 Chrome 类似,但它不像 PhantomJS 那样拥有视图和更少的 bug。
    • 不使用 PhamtomJS 但仍有问题?我会建议使用 browser.wait() (check this for more information) 的量角器的 ExpectedCondition

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

    【讨论】:

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-12
          相关资源
          最近更新 更多