A double click looks like the simplest edge case in Selenium's API: call one method instead of click(), and move on. It rarely stays that simple, the interaction depends on a genuine dblclick browser event, not two clicks fired back to back. Get that distinction wrong and a test can pass locally while never actually exercising the behavior it was meant to check.
This guide puts double click first, since it is the interaction most teams reach for and the one with the most ways to go wrong, then covers right click as the related interaction it is. Here is what we will cover:
THE SHORT ANSWER
How to double click in Selenium, at a glance:
• Use the Actions class: doubleClick() for a double click (contextClick() for the related right-click case). Both mirror the same way in Python through ActionChains.
• Wait for elementToBeClickable first. A double click on an element that is not yet interactable fails the same way a single click does.
• Do not replace doubleClick() with two calls to click(). Browsers only fire a native dblclick event when both clicks land inside a short time window, and two separate Selenium clicks will not reliably do that.
• A test that passes locally but fails in CI is usually a headless-mode or viewport difference, not a broken locator.
The examples, comparison, and troubleshooting section below cover each of these in depth.
It is tempting to reach for element.click(); element.click(); and assume the browser will treat it as a double click. It usually will not. A real double click is its own DOM event, dblclick, and the browser only fires it when it registers two clicks on the same element inside a short, fixed time window. Two Selenium clicks issued in a script often land outside that window, or register as two independent single clicks instead of one double click, so any UI logic listening specifically for dblclick (inline text editing, expanding a row, selecting a word) never fires.
Selenium's Actions class solves this by synthesizing the double click as a single, correctly timed browser event rather than two separate ones. That is the whole reason doubleClick() exists as a distinct method instead of a convenience wrapper around click().
A realistic double-click scenario is an expandable row: double-clicking a row in a data grid reveals its line-item detail without navigating to a new page. The selectors below (order-1024-row, order-details-expanded) are sample IDs for a generic grid, standing in for whatever grid or list your own app uses, not live elements on katalon.com. Below is a complete test: wait for the row to be interactable, double-click it, then assert the detail panel actually appeared. Stopping at the click itself hides the part that matters, confirming the double click did something.
import java.time.Duration;
import org.junit.jupiter.api.Assertions;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriver driver = new ChromeDriver();
driver.get("https://katalon.com");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement orderRow = wait.until(
ExpectedConditions.elementToBeClickable(By.id("order-1024-row"))
);
new Actions(driver).doubleClick(orderRow).perform();
WebElement detailsPanel = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.className("order-details-expanded"))
);
Assertions.assertTrue(detailsPanel.isDisplayed(), "Double-click did not expand the order row");
driver.quit();
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://katalon.com")
wait = WebDriverWait(driver, timeout=10)
order_row = wait.until(EC.element_to_be_clickable((By.ID, "order-1024-row")))
ActionChains(driver).double_click(order_row).perform()
details_panel = wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "order-details-expanded")))
assert details_panel.is_displayed(), "Double-click did not expand the order row"
driver.quit()
The Java example asserts with Assertions.assertTrue() from JUnit 5 rather than Java's built-in assert keyword, which needs the JVM's -ea flag to run at all and otherwise silently passes even on a real failure. For why the elementToBeClickable wait matters here specifically, see our guide to explicit wait in Selenium.
All three trigger through the same WebElement, but they fire different browser events and fail in different ways.
| Selenium Click Events, Compared Same WebElement, three different browser events, three different failure modes. |
|
|---|---|
| click() | Fires a single click event. The default for buttons, links, and anything a real user would click once. Fails with ElementClickInterceptedException if something else sits on top of the element. |
| doubleClick() | Fires a genuine dblclick event through the Actions class. Needed for UI patterns that specifically listen for it: expanding a row, selecting text, opening a file. Two calls to click() will not reliably trigger it. |
| contextClick() | Fires a contextmenu event through the Actions class. Only useful against a menu your application renders itself; the browser's native right-click menu is a separate OS-level surface Selenium cannot see or interact with. |
For the broader family, including Actions.click() and a JavaScript executor click, see our guide to the Selenium click command.
Right-click is the related interaction: same Actions pattern, useful for testing file managers, editors, or any UI that reveals a secondary menu instead of navigating away. As the table above notes, this only works if your app renders its own menu, not the browser's native one.
WebDriver driver = new ChromeDriver();
driver.get("https://katalon.com");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement fileIcon = wait.until(
ExpectedConditions.elementToBeClickable(By.id("attachment-icon"))
);
new Actions(driver).contextClick(fileIcon).perform();
WebElement menu = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.className("context-menu"))
);
Assertions.assertTrue(menu.isDisplayed(), "Right-click did not open the context menu");
driver = webdriver.Chrome()
driver.get("https://katalon.com")
wait = WebDriverWait(driver, timeout=10)
file_icon = wait.until(EC.element_to_be_clickable((By.ID, "attachment-icon")))
ActionChains(driver).context_click(file_icon).perform()
menu = wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "context-menu")))
assert menu.is_displayed(), "Right-click did not open the context menu"
attachment-icon and context-menu are sample selectors for illustration, the same wait-then-assert pattern applies whatever your app's actual menu markup looks like.
The syntax is rarely the problem. These are the failures that actually show up in real test runs.
Element not interactable, or the double click silently does nothing. If the element is present in the DOM but not yet visible or enabled, doubleClick() fails the same way click() does. Wait for elementToBeClickable, not just presence, before performing the action.
ElementClickInterceptedException from an overlay. A cookie banner, sticky header, or modal sitting on top of the target intercepts the interaction. Scroll the element into an uncovered part of the page, then retry the wait and the double click or right click.
StaleElementReferenceException right after a right-click. If opening a context menu re-renders part of the DOM, your reference to the original element can go stale. Re-locate the element (or the menu) after the interaction rather than reusing a handle captured before it.
Passes locally, fails in CI or headless mode. Headless browsers and CI runners often use a different default viewport size than your local machine, which changes what is actually visible or hoverable at a given point on the page. If a double click or right click depends on a hover state to reveal the target first, set an explicit window size in your CI configuration and confirm the element is genuinely visible before interacting with it, not just present.
When not to reach for a JavaScript executor. A JavaScript click bypasses Selenium's visibility and interactability checks, but it cannot synthesize a real dblclick or contextmenu event the way Actions can. Using it here does not just risk masking a real bug, it can fail to trigger the UI behavior you are testing at all. Reserve it for scrolling elements into view, and use Actions for the interaction itself.
Katalon is a low-code automation platform built on top of Selenium. For an interaction like a double click, that mostly means removing the boilerplate around it, not the interaction itself: instead of hand-writing an Actions chain plus wait logic every time, you reuse a saved test object for the target row and a built-in wait keyword, then inspect a screenshot or video of the exact moment the double click fired if it fails, rather than re-running the test to find out why.
To go deeper, the free Using Built-in Keywords in Katalon course covers test objects and reusable keywords in full.