Yes, you can automatically scroll the browser so that any element with which we interact falls into the center. I have a working example below written and tested in ruby using selenium-webdriver-2.41.0 and Firefox 28.
Full disclosure. You may need to edit parts of your code a bit for this to work properly. Explanations follow.
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
Recommended Use:
Enable automatic centering at the beginning of any code segments where the items are usually off-screen, and then turn it off.
NOTE. This code does not seem to work with chain actions. Example:
driver.action.move_to(element).click.perform
The scroll fix doesn't seem to update the click position. In the above example, he will click on the element’s preliminary scroll element, generating an incorrect click.
Why move_to ?
I chose move_to because most mouse-based actions use it, and at this point the behavior of the “scroll in view” of Selenium occurs. This particular patch should not work for any mouse interactions that won't call move_to at some level, and I don't expect it to work with any keyboard interactions, but a similar approach should work theoretically if you complete the correct one functions.
Why sleep ?
I'm actually not sure why the sleep command is needed after scrolling through executeScript . With my specific installation, I can remove the sleep command, and it still works. Similar examples from other developers through net net sleep commands with delays from 0.1 to 3 seconds. As a wild guess, I would say that this is done for reasons of cross-compatibility.
What if I don’t want monkey patch?
The ideal solution would be, as you suggested, to change the behavior of selenium “scroll to point of view”, but I believe that this behavior is controlled by code outside the selenium-webdriver gem. I traced the code to Bridge before the trail went cold.
To reject a monkey monkey, the scroll_to_center method works just fine as a standalone method with multiple permutations, where driver is your instance of Selenium::WebDriver::Driver :
driver.manage.window.position instead of @bridge.getWindowPositiondriver.manage.window.size instead of @bridge.getWindowSizedriver.execute_script instead of @bridge.executeScript