Interacting with web elements is a core part of automating browsers with Selenium. However, you may occasionally run into the frustrating “element not interactable” exception when trying to click, send keys, or otherwise interact with an element on a page.
In this comprehensive guide, you’ll learn what causes this error and the various ways to fix it. We’ll cover specific solutions like waits and actions chains as well as general troubleshooting techniques you can follow to debug “element not interactable” issues.
Also read: Fixing “ImportError: No module named ‘selenium’” in Python
Understanding the Problem
The “element not interactable” error means Selenium attempted to interact with an element, but something on the page prevented that interaction from working properly.
Some common causes include:
- The element is hidden by another overlaying element
- The element exists in the DOM but is not visible in the viewport
- There is a timing issue and the page has not finished loading and rendering the element
Without further troubleshooting, this generic error message gives us few clues about what exactly went wrong.
To fix it, we’ll need to employ waits and retries, debug the page layout and DOM, try alternative selectors and interaction methods, and leverage Selenium’s logs and screenshots.
Prerequisite Setup
Before we dig into solutions, let’s quickly cover the baseline dependencies and code to start debugging:
pip3 install selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.common.exceptions import ElementNotInteractableException
driver = webdriver.Chrome()
driver.get("https://www.google.com")
try:
button = driver.find_element(By.ID, "my-button")
button.click()
except ElementNotInteractableException:
print("Got ElementNotInteractableException!")
This sets up Selenium, opens a webpage, attempts to click a button, and prints an error message if the “element not interactable” exception occurs.
With that foundation in place, let’s walk through how to fix it.
Solution 1: Use Explicit and Implicit Waits
One of the most common sources of “element not interactable” errors is timing issues. The page may still be loading elements when Selenium attempts to interact with them.
Implicit waits tell Selenium to wait up to a certain amount of time when finding elements:
driver.implicitly_wait(10) # seconds
This will retry finding elements for up to 10 seconds before throwing an error.
Explicit waits go further and repeatedly check if an element meets a given condition, like being visible or enabled:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import time
# Setup Chrome options
chrome_options = Options()
# Setup WebDriver with ChromeDriverManager
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
# Navigate to the website
driver.get("https://python.org")
try:
# Wait for the button to be clickable
button = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable((By.ID, "my-button"))
)
# Click the button once it's clickable
button.click()
except:
# Handle cases where the button is not found or not clickable within the timeout
print("The button with ID 'my-button' was not found or was not clickable within 10 seconds.")
# It's good practice to close the browser when done
driver.quit()
This waits up to 10 seconds for the button to become clickable before interacting with it.
/usr/bin/python3 /Users/preet/test.py
The button with ID 'my-button' was not found or was not clickable within 10 seconds.
Adding waits gives the page time to finish rendering elements before Selenium tries clicking or sending keys.
Solution 2: Use ActionChains to Move to the Element
Another issue could be that the element is buried or obscured by other elements on the page.
Selenium’s ActionChains allow you to virtually “move” the mouse to the element before interacting:
from selenium import webdriver
from selenium.webdriver import ActionChains
driver = webdriver.Chrome()
driver.get("https://python.org")
button = driver.find_element_by_id("my-button")
actions = ActionChains(driver)
actions.move_to_element(button)
actions.click(button)
actions.perform()
This smoothly scrolls the element into view and hovers over it before clicking.
Also read: Important Python Selenium Functions You Must Know
Solution 3: Switch to the Frame Containing the Element
Web applications often embed iframes and frames to load distinct sections of pages. If your target element lives inside an iframe, you’ll need to explicitly switch driver context into that frame before interacting:
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://python.org")
# Locate the iframe using the updated method
iframe = driver.find_element(By.TAG_NAME, "iframe")
# Switch to the iframe
driver.switch_to.frame(iframe)
# Locate the button by its ID and click it
button = driver.find_element(By.ID, "my-button")
button.click()
# Switch back to the main content when done with the iframe
driver.switch_to.default_content()
This common cause of “element not interactable” errors can be easy to debug once you know to check for iframes!
Solution 4: Scroll Element into View Before Interacting
Another visibility issue could be elements loaded outside the current viewpoint that require scrolling to access:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
driver = webdriver.Chrome() # Ensure the ChromeDriver executable is in your PATH
driver.get("https://www.google.com") # Navigate to Google
try:
# Wait for the button to be present in the DOM and visible
# Note: Google.com may not have a button with ID "my-button".
# This is just an example and you might need to adjust the ID based on your actual requirements.
button = WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "my-button"))
)
# Scroll the button into view
driver.execute_script("arguments[0].scrollIntoView(true);", button)
# Click the button
button.click()
except TimeoutException:
print("The button was not found on the page within the timeout period.")
Here we use JavaScript to manually scroll the DOM element into view before clicking it.
Solution 5: Try Alternative Selector Strategies
“Element not interactable” errors may indicate your selector isn’t pinpointing the desired element accurately.
Try alternative locator strategies like CSS selector, XPath, link text, partial link text, tag name etc. to hone in on the element:
from selenium.webdriver.common.by import By
button = driver.find_element(By.CSS_SELECTOR, "#content .submit-button")
button = driver.find_element(By.XPATH, "//button[text()='Submit']")
Experiment with different selection approaches if simpler lookups like ID and class name fail.
Solution 6: Debug Page Layout Issues in DevTools
At this point, it’s wise to open browser DevTools to inspect page layout and diagnose why your element remains “not interactable.”
Key things to validate:
- Is the element visible and enabled from a user perspective?
- Does the DOM markup around the element seem valid?
- Are there unusual styles, overlays or opacity settings impacting visibility?
Comparison shots before and after interacting may reveal relevant style and layout changes.
Solution 7: Check Selenium Logs for Errors
Enable driver logging and scan logs following the “element not interactable” failure:
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
# Specify the path to your ChromeDriver and the log path
service = Service(executable_path="path/to/chromedriver", log_path="logs/service.log")
driver = webdriver.Chrome(service=service)
Logs may reveal additional errors and hints on root cause – invaluable debugging context!
Solution 8: Use Selenium Screenshots to Inspect State
Save browser screenshots immediately after the exception to visually inspect page state:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
driver = webdriver.Chrome() # Make sure the ChromeDriver executable is in your PATH
driver.get("https://www.google.com") # Navigate to a website
try:
# Attempt to find an element that does not exist
driver.find_element(By.ID, "nonexistent-element-id")
except NoSuchElementException:
# If the element is not found, save a screenshot
driver.save_screenshot('after_error.png')
# Remember to close the driver after your tests
driver.quit()
Compare this to a working screenshot to identify differences in layout, rendering etc.
Solution 9: Switching Browser Driver Implementations
If you run into an “element not interactable” issue across browser drivers (ChromeDriver, GeckoDriver etc.), try switching driver implementations:
driver = webdriver.Firefox() # or Edge(), Safari() etc
This can rule out driver-specific quirks that may impact reliability.
Solution 10: Test Case to Isolate Failure
As a last resort, strip down the test case to the bare minimum steps to reproduce the failure:
- Navigate to a blank HTML page
- Insert minimal viable element markup
- Attempt interaction
You may discover simplifying assumptions around page load status, element visibility, DOM stability etc.
This reduction often reveals underlying issues not evident in complex test flows.
Summary
Debugging the common “element not interactable” error may seem difficult at first glance. However, as shown in this guide’s solutions, applying the right mix of waits, actions, locators and troubleshooting techniques can uncover the root cause. Factor in timing, visibility and context issues as you investigate, and leverage the full suite of Selenium actions to interact with stubborn elements. With these tips, you’ll be able to tame even the most temperamental “not interactable” elements standing in the way of test automation success!