Skip to main content
SeleniumDecoded

Click With a JavaScript Fallback

Try a real click first, then fall back to a JavaScript click only for the specific interception and visibility errors, with a log line so fallbacks are visible.

Medium Verified against Selenium 4.48.0

Problem

click() throws ElementClickInterceptedException because a sticky header, toast or cookie banner overlaps the target, or ElementNotInteractableException for a visually hidden but functional control. A JavaScript click works but bypasses the checks a user’s click would face, so making it the default hides real bugs.

Solution

Scroll to centre, try the real click, and only on those two exceptions fall back to element.click() in JavaScript, logging the fallback.

Real click, then JavaScript on interception
Selenium 3 & 4 Medium
public static void safeClick(WebDriver driver, WebElement el) {
JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("arguments[0].scrollIntoView({block: 'center'});", el);
try {
el.click();
} catch (ElementClickInterceptedException | ElementNotInteractableException e) {
System.out.println("JS click fallback for " + el + ": " + e.getClass().getSimpleName());
js.executeScript("arguments[0].click();", el);
}
}
from selenium.common.exceptions import (
ElementClickInterceptedException, ElementNotInteractableException)
def safe_click(driver, el):
driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", el)
try:
el.click()
except (ElementClickInterceptedException, ElementNotInteractableException) as e:
print(f"JS click fallback for {el}: {type(e).__name__}")
driver.execute_script("arguments[0].click();", el)
async function safeClick(driver, el) {
await driver.executeScript("arguments[0].scrollIntoView({block: 'center'});", el);
try {
await el.click();
} catch (e) {
if (!['ElementClickInterceptedError', 'ElementNotInteractableError'].includes(e.name)) throw e;
console.warn('JS click fallback:', e.name);
await driver.executeScript('arguments[0].click();', el);
}
}
public static void SafeClick(IWebDriver driver, IWebElement el)
{
var js = (IJavaScriptExecutor)driver;
js.ExecuteScript("arguments[0].scrollIntoView({block: 'center'});", el);
try
{
el.Click();
}
catch (Exception e) when (e is ElementClickInterceptedException || e is ElementNotInteractableException)
{
Console.WriteLine($"JS click fallback: {e.GetType().Name}");
js.ExecuteScript("arguments[0].click();", el);
}
}

Why It Works

The real click exercises the same hit-testing a user’s click does, so it catches layout bugs. The fallback dispatches a click event directly on the element, which fires the handlers regardless of overlap. Catching only the two overlap-related exceptions keeps genuine failures (NoSuchElement, stale references) visible.

Gotchas

  • A JavaScript click does not move the mouse, so hover-dependent handlers and mousedown/mouseup listeners do not fire. If the app needs those, dispatch them explicitly or fix the overlay instead.
  • Count the fallbacks in CI. A test that always needs the fallback has an overlay bug worth reporting or a wait that should have covered the overlay.
  • Prefer waiting for the overlay to disappear (see the spinner recipe) over falling back; use this recipe for the residual cases.

Learn the theory