Python WebDriver click() 与 JavaScript click()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34562061/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 15:09:54  来源:igfitidea点击:

WebDriver click() vs JavaScript click()

javascriptpythonseleniumselenium-webdriverprotractor

提问by alecxe

The Story:

故事:

Here on StackOverflow, I've seen users reporting that they cannot click an element via selenium WebDriver "click" command and can work around it with a JavaScript click by executing a script.

在 StackOverflow 上,我看到用户报告说他们无法通过 selenium WebDriver“单击”命令单击元素,并且可以通过执行脚本通过 JavaScript 单击来解决它。

Example in Python:

Python 中的示例:

element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)

Example in WebDriverJS/Protractor:

WebDriverJS/Protractor 中的示例:

var elm = $("#myid");
browser.executeScript("arguments[0].click();", elm.getWebElement());

The Question:

问题:

Why is clicking "via JavaScript" works when a regular WebDriver click does not? When exactly is this happening and what is the downside of this workaround (if any)?

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

I personally used this workaround without fully understanding why I have to do it and what problems it can lead to.

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

采纳答案by Louis

Contrarily to what the currently accepted answersuggests, there's nothing specific to PhantomJS when it comes to the difference between having WebDriver do a click and doing it in JavaScript.

当前接受的答案所暗示的相反,当涉及到让 WebDriver 执行单击操作和在 JavaScript 中执行操作之间的区别时,没有任何特定于 PhantomJS 的内容。

The Difference

区别

The essential difference between the two methods is common to all browsers and can be explained pretty simply:

这两种方法之间的本质区别对所有浏览器都是通用的,可以非常简单地解释:

  • WebDriver: When WebDriver does the click, it attempts as best as it can to simulate what happens when a real user uses the browser.Suppose you have an element A which is a button that says "Click me" and an element B which is a divelement which is transparent but has its dimensions and zIndexset so that it completely covers A. Then you tell WebDriver to click A. WebDriver will simulate the click so that B receives the click first. Why? Because B covers A, and if a user were to try to click on A, then B would get the event first. Whether or not A would eventually get the click event depends on how B handles the event. At any rate, the behavior with WebDriver in this case is the same as when a real user tries to click on A.

  • JavaScript: Now, suppose you use JavaScript to do A.click(). This method of clicking does not reproduce what really happens when the user tries to click A.JavaScript sends the clickevent directly to A, and B will not get any event.

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

Why a JavaScript Click Works When a WebDriver Click Does Not?

当 WebDriver 单击不起作用时,为什么 JavaScript 单击有效?

As I mentioned above WebDriver will try to simulate as best it can what happens when a real user is using a browser. The fact of the matter is that the DOM can contain elements that a user cannot interact with, and WebDriver won't allow you to click on these element. Besides the overlapping case I mentioned, this also entails that invisible elements cannot be clicked. A common case I see in Stack Overflow questions is someone who is trying to interact with a GUI element that already exists in the DOM but becomes visible only when some other element has been manipulated. This sometimes happens with dropdown menus: you have to first click on the button the brings up the dropdown before a menu item can be selected. If someone tries to click the menu item before the menu is visible, WebDriver will balk and say that the element cannot be manipulated. If the person then tries to do it with JavaScript, it will work because the event is delivered directly to the element, irrespective of visibility.

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

When Should You Use JavaScript for Clicking?

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

If you are using Selenium for testing an application, my answer to this question is "almost never".By and large, your Selenium test should reproduce what a user would do with the browser. Taking the example of the drop down menu: a test should click on the button that brings up the drop down first, and then click on the menu item. If there is a problem with the GUI because the button is invisible, or the button fails to show the menu items, or something similar, then your test will fail and you'll have detected the bug. If you use JavaScript to click around, you won't be able to detect these bugs through automated testing.

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

I say "almost never" because there may be exceptions where it makes sense to use JavaScript. They should be very rare, though.

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

If you are using Selenium for scraping sites, then it is not as critical to attempt to reproduce user behavior. So using JavaScript to bypass the GUI is less of an issue.

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

回答by Linh Pham

NOTE: let's call 'click' is end-user click. 'js click' is click via JS

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

Why is clicking "via JavaScript" works when a regular WebDriver click does not?

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

There are 2 cases for this to happen:

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

I. If you are using PhamtomJS

一、如果你使用的是 PhamtomJS

Then this is the most common known behavior of PhantomJS. Some elements are sometimes not clickable, for example <div>. This is because PhantomJSwas original made for simulating the engine of browsers (like initial HTML + CSS -> computing CSS -> rendering). But it does not mean to be interacted with as an end user's way (viewing, clicking, dragging). Therefore PhamtomJSis only partially supported with end-users interaction.

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

WHY DOES JS CLICK WORK?As for either click, they are all mean click. It is like a gun with 1 barreland 2 triggers. One from the viewport, one from JS. Since PhamtomJSgreat in simulating browser's engine, a JS click should work perfectly.

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

II. The event handler of "click" got to bind in the bad period of time.

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

For example, we got a <div>

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

  • -> We do some calculation

  • -> then we bind event of click to the <div>.

  • -> Plus with some bad coding of angular (e.g. not handling scope's cycle properly)

  • -> 我们做一些计算

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

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

We may end up with the same result. Click won't work, because WebdriverJS trying to click on the element when it has no click event handler.

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

WHY DOES JS CLICK WORK?Js click is like injecting js directly into the browser. Possible with 2 ways,

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

Fistis through devtools console (yes, WebdriverJS does communicate with devtools' console).

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

Secondis inject a <script>tag directly into HTML.

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

For each browser, the behavior will be different. But regardless, these methods are more complicating than clicking on the button. Click is using what already there (end-users click), js click is going through backdoor.

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

And for js click will appear to be an asynchronous task. This is related a with a kinda complex topic of 'browser asynchronous task and CPU task scheduling' (read it a while back can't find the article again). For short this will mostly result as js click will need to wait for a cycle of task scheduling of CPUand it will be ran a bit slower after the binding of the click event. (You could know this case when you found the element sometimes clickable, sometimes not. )

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

When exactly is this happening and what is the downside of this workaround (if any)?

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

=> As mention above, both mean for one purpose, but about using which entrance:

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

  • Click: is using what providing by default of browser.
  • JS click: is going through backdoor.
  • 单击:使用浏览器默认提供的内容。
  • JS点击:正在走后门。

=> For performance, it is hard to say because it relies on browsers. But generally:

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

  • Click: doesn't mean faster but only signed higher position in schedule list of CPU execution task.
  • JS click: doesn't mean slower but only it signed into the last position of schedule list of CPU task.
  • 单击:并不意味着更快,而只是在 CPU 执行任务的调度列表中标记更高的位置。
  • JS click: 不代表慢,只是签到了CPU任务调度列表的最后一个位置。

=> Downsides:

=> 缺点:

  • Click: doesn't seem to have any downside except you are using PhamtomJS.
  • JS click: very bad for health. You may accidentally click on something that doesn't there on the view. When you use this, make sure the element is there and available to view and click as the point of view of end-user.
  • Click: 除了您使用的是 PhamtomJS 之外,似乎没有任何缺点。
  • JS点击:对健康非常不利。您可能会不小心单击视图中不存在的内容。当您使用它时,请确保该元素在那里并且可以作为最终用户的观点进行查看和单击。

P.S. if you are looking for a solution.

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

  • Using PhantomJS? I will suggest using Chrome headless instead. Yes, you can set up Chrome headless on Ubuntu. Thing runs just like Chrome but it only does not have a view and less buggy like PhantomJS.
  • Not using PhamtomJS but still having problems? I will suggest using ExpectedCondition of Protractor with browser.wait()(check this for more information)
  • 使用 PhantomJS?我会建议使用 Chrome 无头代替。是的,您可以在 Ubuntu 上设置 Chrome 无头。Thing 像 Chrome 一样运行,但它没有像 PhantomJS 那样的视图和更少的错误。
  • 不使用 PhamtomJS 但仍有问题?我会建议使用量角器的 ExpectedCondition browser.wait()查看此了解更多信息

(I want to make it short, but ended up badly. Anything related with theory is complicated to explain...)

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

回答by Florent B.

The click executed by the driver tries to simulate the behavior of a real user as close as possible while the JavaScript HTMLElement.click()performs the default action for the clickevent, even if the element is not interactable.

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

The differences are:

区别在于:

  • The driver ensures that the element is visibleby scrolling it into the view and checks that the element is interactable.

    The driver will raise an error:

    • when the element on top at the coordinates of the click is not the targeted element or a descendant
    • when the element doesn't have a positive size or if it is fully transparent
    • when the element is a disabled input or button (attribute/property disabledis true)
    • when the element has the mouse pointer disabled (CSS pointer-eventsis none)


    A JavaScript HTMLElement.click()will always perform the default action or will at best silently fail if the element is a disabled.

  • The driver is expected to bring the element into focusif it is focusable.

    A JavaScript HTMLElement.click()won't.

  • The driver is expected to emit all the events(mousemove, mousedown, mouseup, click, ...) just like like a real user.

    A JavaScript HTMLElement.click()emits only the clickevent. The page might rely on these extra events and might behave differently if they are not emitted.

    These are the events emitted by the driver for a click with 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, ... }
    

    And this is the event emitted with a JavaScript injection:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
    
  • The eventemitted by a JavaScript .click()is not trustedand 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 eventemitted by a JavaScript .click()doesn't have the coordinates of the click.

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

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

    驱动程序将引发错误:

    • 当点击坐标顶部的元素不是目标元素或后代元素时
    • 当元素没有正大小或完全透明时
    • 当元素是禁用的输入或按​​钮时(属性/属性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。页面可能依赖于它们,并且行为可能不同。


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.


使用 JavaScript.click()来抓取一些数据可能没问题,但它不在测试环境中。它违背了测试的目的,因为它没有模拟用户的行为。因此,如果来自驱动程序的点击失败,那么真实用户很可能也无法在相同条件下执行相同的点击。



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

    Retry to click until it succeeds :

    browser.wait(function clickSuccessful() {
        return elem.click().then(() => true, (ex) => false);
    }, 5000);
    

    Add a delay matching the duration of the animation/transition :

    browser.sleep(250);
    


  • The targeted element ends-up covered by a floating elementonce scrolled into the view:

    The driver automatically scrolls the element into the view to make it visible. If the page contains a floating/sticky element (menu, ads, footer, notification, cookie policy..), the element may end-up covered and will no longer be visible/interactable.

    Example: https://twitter.com/?lang=en

    Workarounds:

    Set the size of the window to a larger one to avoid the scrolling or the floating element.

    Mover over the element with a negative Yoffset and then click it:

      browser.actions()
         .mouseMove(elem, {x: 0, y: -250})
         .click()
         .perform();
    

    Scroll the element to the center of the window before the click:

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

    Hide the floating element if it can't be avoided:

    browser.executeScript(function scrollCenter(elem) {
      elem.style.display = 'none';
    }, element);
    
  • 由于延迟或过渡效果,目标元素尚不可见/不可交互。

    一些例子 :

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