100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Testing

Actions Class: Mouse Hover and Drag-and-Drop

Learn how Selenium's Actions class composes mouse hover, click-and-hold, and multi-step drag gestures that simple click() and sendKeys() calls cannot express.

Advanced InteractionIntermediate9 min readJul 10, 2026
Analogies

Why the Actions Class Exists

Selenium's WebElement.click() and sendKeys() only cover the two most common interactions. Real UIs rely on richer mouse and keyboard gestures — hovering to reveal a dropdown, holding a mouse button to drag a card, or pressing Shift while clicking to multi-select — none of which a single method call can express. The Actions class (org.openqa.selenium.interactions.Actions) solves this by letting you compose a sequence of low-level input events (moveToElement, clickAndHold, keyDown, pause) into one chain, then fire them together with perform(), mirroring how a real user's hand moves across the screen over time.

🏏

Cricket analogy: Just as a fielder doesn't just 'catch the ball' but goes through a sequence — sighting it, positioning the body, cushioning the catch — Actions breaks a UI gesture into the same kind of ordered micro-steps like moveToElement then clickAndHold.

Simulating Mouse Hover

Many navigation menus only render their dropdown when the browser fires a genuine mouseover event on the parent element — clicking it does nothing because no click handler exists. new Actions(driver).moveToElement(menuElement).perform() moves the simulated cursor to the center of that element, which triggers the same mouseover/mouseenter listeners a real user's mouse would. Because the dropdown's items are often not present in the DOM (or not interactable) until that hover fires, you should call perform() first, then locate and act on the newly visible submenu item in a separate step, otherwise you'll hit a NoSuchElementException or ElementNotInteractableException.

🏏

Cricket analogy: A spinner drifting the ball in flight before it pitches changes what the batter sees just before contact; moveToElement changes what's rendered on screen just before your next findElement call, so ordering matters.

java
WebDriver driver = new ChromeDriver();
driver.get("https://example.com/shop");

WebElement menu = driver.findElement(By.id("categories-menu"));
Actions actions = new Actions(driver);

// Hover to reveal the dropdown, then act on the newly visible submenu item
actions.moveToElement(menu).pause(Duration.ofMillis(300)).perform();

WebElement submenuItem = driver.findElement(By.linkText("Laptops"));
submenuItem.click();

Drag and Drop

Selenium exposes a one-shot dragAndDrop(source, target) convenience method, but it only fires the native HTML5 drag events (dragstart, dragover, drop) and often fails against sortable lists or Kanban boards built with JavaScript libraries like SortableJS or react-beautiful-dnd, which listen for raw mousedown/mousemove/mouseup instead. For those, you build the gesture manually: clickAndHold(source), a moveToElement(target) or several moveByOffset() calls to simulate intermediate mouse movement (some libraries only start tracking after a minimum drag distance), a short pause, then release(). Adding pause() steps between moves is often the difference between a flaky drag that silently does nothing and one that reliably reorders the list.

🏏

Cricket analogy: A well-executed run-out relay throw isn't one motion but pick-up, transfer, and release in sequence, and rushing any step fumbles it; a JS-based drag-drop needs the same clickAndHold-move-pause-release sequence or the library never registers the gesture.

java
// Simple HTML5 drag-and-drop
WebElement source = driver.findElement(By.id("drag-item"));
WebElement target = driver.findElement(By.id("drop-zone"));
new Actions(driver).dragAndDrop(source, target).perform();

// Manual sequence for JS-based sortable lists (e.g. SortableJS, react-beautiful-dnd)
WebElement card = driver.findElement(By.id("task-card-3"));
WebElement dropZone = driver.findElement(By.id("in-progress-column"));

new Actions(driver)
    .clickAndHold(card)
    .moveByOffset(10, 0)          // small nudge to trigger drag-detection threshold
    .pause(Duration.ofMillis(200))
    .moveToElement(dropZone)
    .pause(Duration.ofMillis(200))
    .release()
    .build()
    .perform();

clickAndHold and moveToElement operate on WebElement references captured before the drag begins. If the drag reorders the DOM (common in Kanban boards), those references can become stale mid-chain and throw StaleElementReferenceException. Re-locate elements after any drag operation before interacting with them again, and never reuse the same WebElement across a drag and a subsequent assertion.

Building and Reusing Action Chains

Calling .build() compiles the queued events into a CompositeAction and .perform() executes it immediately; in modern Selenium (4.x) you can skip build() and call perform() directly, since it builds implicitly, but build() is still useful when you want to construct a chain once and store the resulting Action object to replay later. Each call to actions.moveToElement() etc. mutates and returns the same Actions instance, so chain calls fluently rather than reassigning; calling build() a second time on an Actions object that already had actions queued and performed can lead to unexpected re-execution of earlier steps if you don't instantiate a fresh Actions object per gesture.

🏏

Cricket analogy: A bowling machine can be pre-loaded with a specific line-and-length setting and then fired repeatedly for throwdowns without resetting it each time; Actions.build() similarly compiles a reusable Action object you can perform() more than once.

Always instantiate a fresh new Actions(driver) for each distinct gesture rather than reusing one Actions object across unrelated interactions in a test — reusing it can silently queue and replay stale actions alongside new ones, producing intermittent, hard-to-debug failures.

  • The Actions class composes low-level mouse and keyboard events (moveToElement, clickAndHold, keyDown) into one chain via build() and perform().
  • moveToElement() simulates a real mouseover, needed to reveal CSS/JS-driven dropdown menus before their items can be located and clicked.
  • dragAndDrop(source, target) only works for native HTML5 drag events; JS libraries like SortableJS need a manual clickAndHold-moveByOffset-pause-release sequence.
  • Intermediate moveByOffset() calls matter because many drag libraries only register a drag once a minimum movement threshold is crossed.
  • In Selenium 4.x, perform() implicitly calls build(), but build() remains useful for constructing a reusable Action object.
  • WebElement references can go stale mid-drag if the DOM reorders; re-locate elements after any drag-and-drop before further interaction.
  • Instantiate a fresh Actions object per gesture to avoid replaying stale queued actions from a previous interaction.

Practice what you learned

Was this page helpful?

Topics covered

#Testing#SeleniumStudyNotes#TestingQA#ActionsClassMouseHoverAndDragAndDrop#Actions#Class#Mouse#Hover#OOP#StudyNotes#SkillVeris