4

I don't know why ActionChains move_to_element() is not working with chromedriver >74.

(But it works on chromedriver 74 and geckodriver.)

Even though I add these three line before ActionChains, it still failed to move to element.

WebDriverWait(driver, 60).until(EC.presence_of_element_located((By.XPATH, xxxxx)))
WebDriverWait(driver, 60).until(EC.visibility_of_element_located((By.XPATH, xxxxx))
drvier.execute_script("arguments[0].scrollIntoView();", element)

ActionChains(driver).move_to_element(element).click().perform()

And throw error as below:

selenium.common.exceptions.MoveTargetOutOfBoundsException: Message: move target out of bounds (Session info: chrome=79.0.3945.117)

I also try to use move_to_element_with_offset mentioned in Selenium MoveTargetOutOfBoundsException even after scrolling to element, it still not working:

ActionChains(driver).move_to_element_with_offset(element, 5, 5).click().perform()

Below is my setting of chromedriver. Is there any settings impact to ActionChains?

options = webdriver.ChromeOptions()
options.add_argument('--no-sandbox')
options.add_argument('log-level=3')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--no-proxy-server')
options.add_argument('--disable-extensions')
options.add_argument('--disable-infobars')
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(executable_path=chromedriver_path, chrome_options=options)
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
Yun
  • 1,032
  • 7
  • 20

2 Answers2

2

As your use is to invoke click() through ActionChains instead of presence_of_element_located() and visibility_of_element_located() you need to use the expected_conditions as element_to_be_clickable() as follows:

  • Usage with ActionChains:

    ActionChains(driver).move_to_element(WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "element_css")))).click().perform()
    
  • If you have to scrollIntoView() before invoking click() you need to induce WebDriverWait for the visibility_of_element_located() and you can use the following Locator Strategy:

    WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, xxxxx))
    drvier.execute_script("arguments[0].scrollIntoView();", WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, xxxxx)))
    ActionChains(driver).move_to_element(WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "element_css")))).click().perform()
    
  • Note : You have to add the following imports :

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    

Additional Considerations

Ensure that:

  • Selenium is upgraded to current levels Version 3.141.59.
  • ChromeDriver is updated to current ChromeDriver v79.0.3945.36 level.
  • Chrome is updated to current Chrome Version 79.0 level. (as per ChromeDriver v79.0 release notes)
  • Clean your Project Workspace through your IDE and Rebuild your project with required dependencies only.
  • If your base Web Client version is too old, then uninstall it through Revo Uninstaller and install a recent GA and released version of Web Client.
  • Take a System Reboot.
  • Execute your @Test as non-root user.
  • Always invoke driver.quit() within tearDown(){} method to close & destroy the WebDriver and Web Client instances gracefully.

Update

As per your comments that:

options.add_experimental_option('w3c', False)

worked for you, but as per the Release Notes of ChromeDriver 75.0.3770.8:

Resolved issue 2536: Make standards mode (goog:chromeOptions.w3c:true) the default [Pri-2]

ChromeDriver 75.0 solves this issue.

So the bottom line is, chromeOptions.w3c needs to be set as true by default. It will be against the best practices to turn off w3c in chromedriver to address the error. We have discussed this in length and breadth in the following discussions:

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • The exception is on the `move_to_element`, it has nothing to do with the click. – Guy Jan 20 '20 at 08:52
  • Thank you very much. I'm sorry to ask the fool question. I found that it caused by Chromedriver 75 runs in W3C standard compliant mode by default. And I found the solution with add "options.add_experimental_option('w3c', False)" , it works well with ActionChains. Release note: https://chromedriver.storage.googleapis.com/75.0.3770.8/notes.txt – Yun Jan 20 '20 at 09:18
  • @Yun Checkout the answer update and let me know if any questions. – undetected Selenium Jan 20 '20 at 09:35
  • I have updated chromedriver to 79 and tried to set w3c to true. It still failed to use ActionChains move_to_element(). – Yun Jan 20 '20 at 09:57
  • @Yun I tried my best with a canonical answer with reference to all the relevant discussions, call is yours now what works for you :) – undetected Selenium Jan 20 '20 at 09:59
  • Note. In my case (chrome driver 80) , adding the first JavaScript execute works because `move_to_element` moves to the center of the element instead of the top; while `click` requires the top to be visible. – user202729 Dec 09 '21 at 09:14
2

move_to_element uses internally move_to

def move_to_element(self, to_element):
    if self._driver.w3c: # default in chromedriver 79
        self.w3c_actions.pointer_action.move_to(to_element)

def move_to(self, element, x=None, y=None):
    #...
    el_rect = element.rect
    left_offset = el_rect['width'] / 2
    top_offset = el_rect['height'] / 2
    left = -left_offset + (x or 0)
    top = -top_offset + (y or 0)

    self.source.create_pointer_move(origin=element, x=int(left), y=int(top))

The mouse pointer is moved by offset based on the element position. You are locating the element and then scroll it into view using JavaScript, so the offset is calculated by the wrong coordinates.

Removing the JavaScript scroll should solve the problem.

Guy
  • 46,488
  • 10
  • 44
  • 88
  • Thank you. Neither of js scroll or not can be working. And I found that it can solve by setting w3c to False. – Yun Jan 21 '20 at 02:44