Selenium Actions Class
click() and sendKeys() cover most interactions, but real users also hover, right-click, drag, double-click and press key combinations. Selenium's Actions class simulates these through the W3C Actions API. This guide covers every common action with Selenium 4 code — plus the gotchas: offsets that changed in Selenium 4, modifier keys that don't stay pressed, and HTML5 drag-and-drop that the Actions class can't do.
Practice pages: Hovers, Context Menu and jQuery UI Droppable (inside an iframe).
Creating an Actions Object
import org.openqa.selenium.interactions.Actions;
Actions actions = new Actions(driver);
You build a chain of steps, then call perform() to send them to the browser. Nothing happens until perform().
Mouse Actions at a Glance
| Method | Does |
|---|---|
moveToElement(el) |
Hover over the element's center |
moveToElement(el, x, y) |
Hover at an offset — from the center in Selenium 4 (it was the top-left corner in Selenium 3) |
click() / click(el) |
Click at the current position / on an element |
doubleClick(el) |
Double-click |
contextClick(el) |
Right-click |
clickAndHold(el) / release() |
Press and release the mouse button |
dragAndDrop(source, target) |
Drag one element onto another |
dragAndDropBy(el, x, y) |
Drag by pixels — sliders |
scrollToElement(el), scrollByAmount(x, y) |
Scroll (Selenium 4.2+, Chromium browsers) |
pause(Duration) |
Wait between steps inside a chain |
Hover and Multi-Level Menus
WebElement products = driver.findElement(By.linkText("Products"));
WebElement laptops = driver.findElement(By.linkText("Laptops"));
actions.moveToElement(products) // open the first level
.moveToElement(laptops) // move into the submenu
.click()
.perform();
If a submenu appears with an animation, find it only after hovering, and wait for it:
actions.moveToElement(products).perform();
new WebDriverWait(driver, Duration.ofSeconds(5))
.until(ExpectedConditions.elementToBeClickable(By.linkText("Laptops")))
.click();
Hover tests are timing-sensitive: moving the mouse (or another action) can close the menu. Keep hover chains short and in one perform() where possible.
Right-Click and Double-Click
WebElement box = driver.findElement(By.id("hot-spot"));
actions.contextClick(box).perform();
// On The Internet's Context Menu page, right-clicking opens a JavaScript alert
Alert alert = new WebDriverWait(driver, Duration.ofSeconds(5)).until(ExpectedConditions.alertIsPresent());
Assert.assertEquals(alert.getText(), "You selected a context menu");
alert.accept();
actions.doubleClick(driver.findElement(By.id("editable-cell"))).perform();
Custom right-click menus are ordinary HTML — locate the menu item after the right-click and click it. The browser's native context menu (Back, Reload, Inspect) can't be automated by Selenium.
Drag and Drop
WebElement source = driver.findElement(By.id("draggable"));
WebElement target = driver.findElement(By.id("droppable"));
actions.dragAndDrop(source, target).perform();
// Step by step — when the page needs intermediate mouse moves
actions.clickAndHold(source)
.moveToElement(target)
.pause(Duration.ofMillis(200))
.release()
.perform();
Assert.assertEquals(target.getText(), "Dropped!");
// Sliders: drag by an offset
actions.dragAndDropBy(driver.findElement(By.cssSelector(".slider-handle")), 50, 0).perform();
On jQuery UI's demo, the elements sit inside an iframe — switch into it first (driver.switchTo().frame(0)).
When drag and drop doesn't work
Pages that use the HTML5 Drag and Drop API (draggable="true" with dragstart/drop events) often ignore Actions-based drags, because the browser's native drag events aren't fired by synthetic mouse moves. The Actions call "passes" but nothing moves. Common workarounds: a JavaScript helper that dispatches the drag events, or testing that feature with Playwright, whose dragTo() handles HTML5 drag and drop. Try the manual clickAndHold chain first — it works for jQuery UI and most library-based drags.
Keyboard Actions
| Method | Does |
|---|---|
sendKeys(text) / sendKeys(el, text) |
Type into the focused element / a specific element |
keyDown(Keys.X) |
Press and hold a key (Shift, Control, Alt, Command) |
keyUp(Keys.X) |
Release it |
WebElement field = driver.findElement(By.id("notes"));
Keys modifier = Platform.getCurrent().is(Platform.MAC) ? Keys.COMMAND : Keys.CONTROL;
// Select all and copy
actions.click(field)
.keyDown(modifier).sendKeys("a").sendKeys("c").keyUp(modifier)
.perform();
// Paste into another field
actions.click(driver.findElement(By.id("copy")))
.keyDown(modifier).sendKeys("v").keyUp(modifier)
.perform();
// Shift + typing = capitals; arrow keys and Enter
actions.keyDown(Keys.SHIFT).sendKeys("selenium").keyUp(Keys.SHIFT)
.sendKeys(Keys.ARROW_DOWN, Keys.ENTER)
.perform();
- Hold modifiers with
keyDown/keyUp.actions.sendKeys(Keys.CONTROL, "a")presses and releases Control before the "a", so it often types a plain "a" instead of selecting all. - Always release what you press — a Control left down affects the next steps.
- macOS uses Command, not Control, for shortcuts — detect the platform as above when tests run on both.
- For a single field,
field.sendKeys(Keys.chord(modifier, "a"))is a shorter alternative.
build() vs perform()
-
build()packages the chain into anActionobject without running it. -
perform()runs the chain — and in Selenium 4 it builds it for you.
actions.moveToElement(menu).click(item).perform(); // ⭐ the normal form
Action openMenu = actions.moveToElement(menu).click(item).build();
openMenu.perform(); // build once, run later — rarely needed
So build().perform() works but the build() is redundant; plain perform() is enough. build() is only useful when you want to store an action and run it later or several times.
Clicking Without click()
| Option | Code | When |
|---|---|---|
| Actions | actions.moveToElement(el).click().perform() |
Elements that need a real mouse move first |
| Keyboard | el.sendKeys(Keys.ENTER) |
Focusable buttons and links |
| Form submit | el.submit() |
Any element inside a form |
| JavaScript | js.executeScript("arguments[0].click();", el) |
Last resort |
The JavaScript click ignores whether a user could click (hidden, covered, disabled-looking), so it can hide real bugs. Fix the underlying problem — usually a wait or an overlay — before reaching for it. See Element Not Clickable at Point and JavaScriptExecutor.
See how each queued action travels from your test to the browser in the Selenium WebDriver Visualizer. Practice: Actions & JavaScriptExecutor Exercises.
From Real Projects
In my automation roles I created, modified and enhanced Selenium WebDriver scripts in Java, organized with POM classes and a business library, and ran them with TestNG on Chrome and Firefox. Exercises like these build exactly the muscle memory you need when a real application — for me, Canolog's sales, inventory and service screens — throws a new kind of element at you. Always call perform() at the end of an Actions chain.
FAQs
What is the Actions class used for?
Advanced user interactions — hover, right-click, double-click, drag and drop, click-and-hold, scrolling and key combinations — through the W3C Actions API.
How do you right-click in Selenium?
new Actions(driver).contextClick(element).perform().
What is the difference between build() and perform()?
build() creates an Action without running it; perform() runs the chain (and builds it automatically in Selenium 4).
Why doesn't my drag and drop work?
The page probably uses HTML5 native drag and drop, which ignores synthetic mouse events. Try the clickAndHold chain, then a JavaScript drag helper — or check the element is in an iframe you haven't switched to.
How do you press Ctrl+A or Ctrl+C?
keyDown(Keys.CONTROL).sendKeys("a").keyUp(Keys.CONTROL) — Command on macOS.
Did moveToElement offsets change in Selenium 4?
Yes — offsets are now measured from the element's center, not its top-left corner. Update old slider and canvas tests.