问题 硒滚动元素进入(中心)视图


当一个元素与selenium不在视图中并且试图与它进行交互时,selenium通常会隐式地将元素滚动到视图中。这很棒,但令人讨厌的是它通常会将元素放入视图中。我的意思是,如果元素在窗口下方,它将向下滚动,直到元素刚好接近窗口边缘。

通常这很好,但是当在带有边框的网站上工作时,这将导致许多这类错误

Selenium::WebDriver::Error::UnknownError:
       unknown error: Element is not clickable at point (438, 747). Other element would receive the click: <body>...</body>

因为通常网页的边框位于其上方,但无论如何都会尝试单击该元素。无论如何处理这个?也许是在视线外时自动将元素移动到屏幕中心?我正在考虑通过红宝石修补猴子。


9987
2017-11-23 20:34


起源



答案:


这应该可以将元素滚动到视图中心:

WebElement element = driver.findElement(By.xxx("xxxx"));

String scrollElementIntoMiddle = "var viewPortHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);"
                                            + "var elementTop = arguments[0].getBoundingClientRect().top;"
                                            + "window.scrollBy(0, elementTop-(viewPortHeight/2));";

((JavascriptExecutor) driver).executeScript(scrollElementIntoMiddle, element);

12
2017-07-12 06:18



谢谢。这个比scrollIntoView好 - bugCracker


答案:


这应该可以将元素滚动到视图中心:

WebElement element = driver.findElement(By.xxx("xxxx"));

String scrollElementIntoMiddle = "var viewPortHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);"
                                            + "var elementTop = arguments[0].getBoundingClientRect().top;"
                                            + "window.scrollBy(0, elementTop-(viewPortHeight/2));";

((JavascriptExecutor) driver).executeScript(scrollElementIntoMiddle, element);

12
2017-07-12 06:18



谢谢。这个比scrollIntoView好 - bugCracker


是的,可以自动滚动浏览器,使我们交互的任何元素都在窗口中居中。我在下面有一个工作示例,使用selenium-webdriver-2.41.0和Firefox 28在ruby中编写和测试。

完全披露:您可能需要编辑部分代码  让它正常工作。解释如下。

Selenium::WebDriver::Mouse.class_eval do
  # Since automatic centering of elements can be time-expensive, we disable
  # this behavior by default and allow it to be enabled as-needed.
  self.class_variable_set(:@@keep_elements_centered, false)

  def self.keep_elements_centered=(enable)
    self.class_variable_set(:@@keep_elements_centered, enable)
  end

  def self.keep_elements_centered
    self.class_variable_get(:@@keep_elements_centered)
  end

  # Uses javascript to attempt to scroll the desired element as close to the
  # center of the window as possible. Does nothing if the element is already
  # more-or-less centered.
  def scroll_to_center(element)
    element_scrolled_center_x = element.location_once_scrolled_into_view.x + element.size.width / 2
    element_scrolled_center_y = element.location_once_scrolled_into_view.y + element.size.height / 2

    window_pos = @bridge.getWindowPosition
    window_size = @bridge.getWindowSize
    window_center_x = window_pos[:x] + window_size[:width] / 2
    window_center_y = window_pos[:y] + window_size[:height] / 2

    scroll_x = element_scrolled_center_x - window_center_x
    scroll_y = element_scrolled_center_y - window_center_y

    return if scroll_x.abs < window_size[:width] / 4 && scroll_y.abs < window_size[:height] / 4

    @bridge.executeScript("window.scrollBy(#{scroll_x}, #{scroll_y})", "");
    sleep(0.5)
  end

  # Create a new reference to the existing function so we can re-use it.
  alias_method :base_move_to, :move_to

  # After Selenium does its own mouse motion and scrolling, do ours.
  def move_to(element, right_by = nil, down_by = nil)
    base_move_to(element, right_by, down_by)
    scroll_to_center(element) if self.class.keep_elements_centered
  end
end

推荐用法:

在元素通常在屏幕外的任何代码段的开头启用自动居中,然后禁用它。

注意:此代码似乎不适用于链式操作。例:

driver.action.move_to(element).click.perform

滚动修复似乎没有更新 click 位置。在上面的示例中,它会单击元素的预滚动位置,从而产生误点击。

为什么 move_to

我选择 move_to 因为大多数基于鼠标的操作都使用它,并且在此步骤中会发生Selenium现有的“滚动到视图”行为。此特定补丁不适用于任何不调用的鼠标交互 move_to 在某种程度上,我也不希望它适用于任何键盘交互,但理论上,如果你包装正确的函数,类似的方法应该有效。

为什么 sleep

我真的不确定为什么 sleep 滚动后需要命令 executeScript。通过我的特定设置,我可以删除 sleep 命令它仍然有效。 类似的例子 从 其他开发者 横跨'网包括 sleep 延迟范围为0.1到3秒的命令。作为一个疯狂的猜测,我会说这是出于交叉兼容的原因。

如果我不想修补猴子怎么办?

正如你所建议的那样,理想的解决方案是改变Selenium的“滚动视图”行为,但我相信这种行为是由selenium-webdriver gem之外的代码控制的。我一直跟踪代码 Bridge 在小道变冷之前。

对于猴子补丁厌恶, scroll_to_center 方法可以作为一个具有少量替换的独立方法工作,其中 driver是你的 Selenium::WebDriver::Driver 例如:

  • driver.manage.window.position 代替 @bridge.getWindowPosition
  • driver.manage.window.size 代替 @bridge.getWindowSize
  • driver.execute_script 代替 @bridge.executeScript

3
2018-03-18 21:19





你可以在这里通过javascript使用显式滚动动作。在这种情况下,您会找到元素(如果我正确理解您的问题,此部分已经工作),然后将窗口滚动到指定位置,然后与元素交互。

在java中将是:

WebElement element = driver.findElement(By.id("tabs")).findElement(By.className("youarehere"));
Point p = element.getLocation();
((JavascriptExecutor) driver).executeScript("window.scroll(" + p.getX() + "," + (p.getY() + 200) + ");");

0
2017-11-23 22:00





以下代码将滚动,直到元素在视图中,

WebElement element = driver.findElement(By.id("id_of_element"));
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", element);
Thread.sleep(500); 

//do anything you want with the element

0
2017-11-24 04:19



这是一个很好的建议,但是我必须为我接触到的每个元素做这个。我正在寻找一种在整个会话中应用的方法 - mango
这并不总是有效,因为“进入视图”仍然可以在横幅下,这意味着它仍然无法点击。 scrollElementIntoMiddle工作得更好。 - KuleRucket