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 /量angular器中的示例:

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

问题:

为什么点击“通过JavaScript”的作品,当一个普通的WebDriver点击没有? 什么时候发生这种情况,这种解决方法(如果有的话)的缺点是什么?

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

与目前接受的答案相反,对于WebDriver在JavaScript中执行点击操作之间的区别,PhantomJS并没有特定的function。

区别

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

  • WebDriver: 当WebDriver执行点击操作时,会尽可能地模拟真实用户使用浏览器时发生的情况。 假设你有一个元素A,它是一个button,表示“Click me”,元素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不会允许您单击这些元素。 除了我提到的重叠情况,这也意味着不可见的元素不能被点击。 我在堆栈溢出问题中看到的一个常见情况是,尝试与已存在于DOM中的GUI元素进行交互,但只有在处理了其他元素时才可见。 下拉菜单有时会发生这种情况:您必须先点击button才能select菜单项,然后调出下拉菜单。 如果有人在菜单可见之前尝试点击菜单项,WebDriver会不知所措地说该元素不能被操纵。 如果这个人试图用JavaScript来做,它将会起作用,因为事件是直接传递给元素的,而不pipe可见性如何。

何时使用JavaScript进行点击?

如果您使用Selenium来testing应用程序 ,我对这个问题的回答是“几乎从不”。 总体而言,您的Seleniumtesting应该重现用户使用浏览器所做的事情。 以下拉菜单为例:testing应该先点击打开下拉菜单的button,然后点击菜单项。 如果因为button不可见而导致GUI出现问题,或者button无法显示菜单项或类似的东西,那么testing将失败,并且您将检测到该错误。 如果您使用JavaScript来点击,您将无法通过自动化testing来检测这些错误。

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

如果您使用Selenium来抓取网站 ,那么尝试重现用户行为就不那么重要了。 所以使用JavaScript绕过GUI不是一个问题。

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

差异是:

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

    司机会提出一个错误:

    • 当点击坐标顶部的元素不是目标元素或后代时
    • 当元素没有正面大小或完全透明时
    • 当元素是禁用的input或按​​钮(属性/属性disabledtrue
    • 当元素禁用了鼠标指针(CSS pointer-events none

    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

    请注意,某些驱动程序仍在生成不受信任的事件。 PhantomJS版本2.1就是这种情况。

  • JavaScript .click()发出的事件 不具有点击的坐标

    属性clientX, clientY, screenX, screenY, layerX, layerY被设置为0 。 页面可能依赖于他们,可能会有不同的performance。

使用JavaScript .click()来取消某些数据可能没有问题,但它不在testing环境中。 由于它不模拟用户的行为,因此它打败了testing的目的。 所以,如果驱动程序的点击失败,那么真正的用户很可能也无法在相同的条件下执行相同的点击。

当我们期望它成功时,驱动程序是否无法点击某个元素?

  • 目标元素由于延迟或过渡效果而不可见/可交互。

    一些例子 :

    https://developer.mozilla.org/fr/docs/Web (下拉导航菜单) http://materializecss.com/side-nav.html (下拉边栏)

    Workarrounds:

    添加一个服务员等待的可见性,最小的大小或稳定的位置:

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

    添加一个匹配animation/转换时间的延迟:

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

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

为什么点击“通过JavaScript”的作品,当一个普通的WebDriver点击没有?

有两种情况发生:

I. 如果你正在使用PhamtomJS

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

为什么JS点击工作? 至于任何一个点击,他们都是平均点击。 它就像一个枪pipe两个触发器 。 一个来自视口,一个来自JS。 由于PhamtomJS模拟浏览器引擎伟大的JS点击应该完美。

II。 “点击”的事件处理程序必须在不好的时间段内绑定。

比如我们有一个<div>

  • – >我们做一些计算

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

  • – >加上一些错误的angular度编码(例如,不正确处理示波器的循环)

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

为什么JS点击工作? Js点击就像直接注入到浏览器的JS。 可能有两种方式,

拳头是通过devtools控制台(是的,WebdriverJS与devtools的控制台通讯)。

其次是直接注入<script>标签到html中。

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

而对于js点击将显示为一个asyncronus任务。 这与一个“ 浏览器asynchronous任务和CPU任务调度 ”这个有点复杂的话题有关(一段时间读一遍又找不到文章)。 简而言之,这主要是因为js点击需要等待一个CPU任务调度周期,并且在点击事件绑定之后会稍微慢一些。 (当你发现元素有时可以点击的时候,你可能会知道这种情况,有时候不会。)

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

=>如上所述,这两个意思是为了一个目的,而是关于使用哪个入口:

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

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

  • 点击:并不意味着更快,而只是在CPU执行任务的计划列表中签名更高的位置。
  • JS点击:并不意味着慢,但只有它login到CPU任务计划列表的最后一个位置。

=>缺点:

  • 点击:除了使用PhamtomJS,似乎没有任何不利之处。
  • JS点击:对健康很不好。 您可能会意外地点击视图中不存在的内容。 当你使用这个,确保元素在那里,可以查看和点击作为最终用户的观点。

PS如果你正在寻找解决scheme。

  • 使用PhantomJS? 我会build议使用Chrome headless。 是的,你可以在Ubuntu上安装Chrome。 事情就像Chrome一样运行,但它只是没有像PhantomJS一样的视图和更less的错误。
  • 不使用PhamtomJS,但它仍然有问题? 我会build议使用ExpectCondition of Protractor和browser.wait() ( 查看更多信息 )

(我想把它简短一些,但结果很糟糕,任何与理论相关的东西都会使得解释变得复杂…)