【问题标题】:selenium scroll element into (center of) view硒滚动元素进入(中心)视图
【发布时间】:2013-12-08 16:08:02
【问题描述】:

当一个元素在 selenium 的视野之外并且试图与之交互时,selenium 通常会首先隐式地将元素滚动到视野中。这很好,只是令人讨厌的是它通常将元素放入视图中。我的意思是,如果元素在窗口下方,它会向下滚动到足够多的时候,直到元素刚好与窗口边缘接壤。

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

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

因为通常网页的边框在它上面,但无论如何都会尝试点击该元素。有没有办法处理这个?也许在看不见的时候自动将元素移动到屏幕的中心?我正在考虑通过 ruby​​ 进行猴子修补。

【问题讨论】:

    标签: ruby selenium-webdriver watir-webdriver


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢。这个比 scrollIntoView 好
    • 这对我规避一些似乎覆盖了我试图移动到的元素的页眉和页脚问题特别有帮助。在 C# 中为我工作。
    【解决方案2】:

    是的,可以自动滚动浏览器,使我们与之交互的任何元素都在窗口中居中。我在下面有一个工作示例,使用 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

    我实际上不确定为什么在通过executeScript 滚动后需要sleep 命令。通过我的特定设置,我可以删除 sleep 命令并且它仍然有效。 Similar examples 来自 other developers 的网络包括 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】:

      下面的代码会一直滚动,直到元素出现在视图中,

      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
      

      【讨论】:

      • 这是一个很好的建议,但是我必须为我接触到的每个元素都这样做。我正在寻找一种适用于整个会话的方法
      • 这并不总是有效,因为“进入视野”仍然可以在横幅下,这意味着它仍然不可点击。 scrollElementIntoMiddle 效果更好。
      【解决方案4】:

      您可以在此处通过 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) + ");");
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-11
        • 2012-04-08
        • 1970-01-01
        • 1970-01-01
        • 2018-02-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多