Skip to main content
SeleniumDecoded

Mouse Actions

Master advanced mouse interactions including hover, right-click, double-click, and complex mouse movements.

Selenium 3 & 4 Medium

Beyond simple clicks, web applications often require hover menus, context menus, and other mouse-based interactions. The Actions API provides complete control over mouse behavior.

Basic Mouse Actions

Common Mouse Actions
Selenium 3 & 4 Stable
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.WebElement;
Actions actions = new Actions(driver);
WebElement element = driver.findElement(By.id("target"));
// Simple click
actions.click(element).perform();
// Double click
actions.doubleClick(element).perform();
// Right-click (context menu)
actions.contextClick(element).perform();
// Click and hold
actions.clickAndHold(element).perform();
// Release (after clickAndHold)
actions.release().perform();
// Move to element (hover)
actions.moveToElement(element).perform();
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
element = driver.find_element(By.ID, "target")
# Simple click
actions.click(element).perform()
# Double click
actions.double_click(element).perform()
# Right-click (context menu)
actions.context_click(element).perform()
# Click and hold
actions.click_and_hold(element).perform()
# Release (after click_and_hold)
actions.release().perform()
# Move to element (hover)
actions.move_to_element(element).perform()
const actions = driver.actions({ async: true });
const element = await driver.findElement(By.id('target'));
// Simple click
await actions.click(element).perform();
// Double click
await actions.doubleClick(element).perform();
// Right-click (context menu)
await actions.contextClick(element).perform();
// Click and hold
await actions.press().perform();
// Release
await actions.release().perform();
// Move to element (hover)
await actions.move({ origin: element }).perform();
using OpenQA.Selenium.Interactions;
Actions actions = new Actions(driver);
IWebElement element = driver.FindElement(By.Id("target"));
// Simple click
actions.Click(element).Perform();
// Double click
actions.DoubleClick(element).Perform();
// Right-click (context menu)
actions.ContextClick(element).Perform();
// Click and hold
actions.ClickAndHold(element).Perform();
// Release (after ClickAndHold)
actions.Release().Perform();
// Move to element (hover)
actions.MoveToElement(element).Perform();

Hover Menus

Many sites use hover-activated dropdown menus:

Interacting with Hover Menus
Selenium 3 & 4 Medium
// Hover over main menu item
WebElement menuItem = driver.findElement(By.id("products-menu"));
Actions actions = new Actions(driver);
actions.moveToElement(menuItem).perform();
// Wait for submenu to appear
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement submenu = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.className("dropdown-menu")
));
// Click submenu item
WebElement submenuItem = driver.findElement(By.linkText("Laptops"));
actions.moveToElement(submenuItem).click().perform();
// Multi-level hover menu
WebElement level1 = driver.findElement(By.id("electronics"));
WebElement level2 = driver.findElement(By.id("computers"));
WebElement level3 = driver.findElement(By.id("laptops"));
actions.moveToElement(level1)
.pause(Duration.ofMillis(500)) // Wait for animation
.moveToElement(level2)
.pause(Duration.ofMillis(500))
.moveToElement(level3)
.click()
.perform();
import time
# Hover over main menu item
menu_item = driver.find_element(By.ID, "products-menu")
actions = ActionChains(driver)
actions.move_to_element(menu_item).perform()
# Wait for submenu to appear
wait = WebDriverWait(driver, 10)
submenu = wait.until(EC.visibility_of_element_located(
(By.CLASS_NAME, "dropdown-menu")
))
# Click submenu item
submenu_item = driver.find_element(By.LINK_TEXT, "Laptops")
actions = ActionChains(driver)
actions.move_to_element(submenu_item).click().perform()
# Multi-level hover menu
level1 = driver.find_element(By.ID, "electronics")
level2 = driver.find_element(By.ID, "computers")
level3 = driver.find_element(By.ID, "laptops")
actions = ActionChains(driver)
actions.move_to_element(level1) .pause(0.5) .move_to_element(level2) .pause(0.5) .move_to_element(level3) .click() .perform()
// Hover over main menu item
const menuItem = await driver.findElement(By.id('products-menu'));
await driver.actions({ async: true })
.move({ origin: menuItem })
.perform();
// Wait for submenu to appear
const submenu = await driver.wait(
until.elementLocated(By.className('dropdown-menu')),
10000
);
// Click submenu item
const submenuItem = await driver.findElement(By.linkText('Laptops'));
await driver.actions({ async: true })
.move({ origin: submenuItem })
.click()
.perform();
// Multi-level hover menu
const level1 = await driver.findElement(By.id('electronics'));
const level2 = await driver.findElement(By.id('computers'));
const level3 = await driver.findElement(By.id('laptops'));
await driver.actions({ async: true })
.move({ origin: level1 })
.pause(500)
.move({ origin: level2 })
.pause(500)
.move({ origin: level3 })
.click()
.perform();
// Hover over main menu item
IWebElement menuItem = driver.FindElement(By.Id("products-menu"));
Actions actions = new Actions(driver);
actions.MoveToElement(menuItem).Perform();
// Wait for submenu to appear
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
IWebElement submenu = wait.Until(ExpectedConditions.ElementIsVisible(
By.ClassName("dropdown-menu")
));
// Click submenu item
IWebElement submenuItem = driver.FindElement(By.LinkText("Laptops"));
new Actions(driver).MoveToElement(submenuItem).Click().Perform();
// Multi-level hover menu
IWebElement level1 = driver.FindElement(By.Id("electronics"));
IWebElement level2 = driver.FindElement(By.Id("computers"));
IWebElement level3 = driver.FindElement(By.Id("laptops"));
new Actions(driver)
.MoveToElement(level1)
.Pause(TimeSpan.FromMilliseconds(500))
.MoveToElement(level2)
.Pause(TimeSpan.FromMilliseconds(500))
.MoveToElement(level3)
.Click()
.Perform();

Context Menu (Right-Click)

Right-Click Context Menu
Selenium 3 & 4 Medium
// Right-click to open context menu
WebElement target = driver.findElement(By.id("context-area"));
Actions actions = new Actions(driver);
actions.contextClick(target).perform();
// Wait for context menu
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.className("context-menu")
));
// Click menu option
WebElement copyOption = driver.findElement(
By.xpath("//div[@class='context-menu']//span[text()='Copy']")
);
copyOption.click();
// Handle browser's native context menu (limited control)
// Usually need to interact with custom context menus instead
# Right-click to open context menu
target = driver.find_element(By.ID, "context-area")
actions = ActionChains(driver)
actions.context_click(target).perform()
# Wait for context menu
wait = WebDriverWait(driver, 5)
wait.until(EC.visibility_of_element_located(
(By.CLASS_NAME, "context-menu")
))
# Click menu option
copy_option = driver.find_element(
By.XPATH, "//div[@class='context-menu']//span[text()='Copy']"
)
copy_option.click()
# Handle browser's native context menu (limited control)
# Usually need to interact with custom context menus instead
// Right-click to open context menu
const target = await driver.findElement(By.id('context-area'));
await driver.actions({ async: true })
.contextClick(target)
.perform();
// Wait for context menu
await driver.wait(
until.elementLocated(By.className('context-menu')),
5000
);
// Click menu option
const copyOption = await driver.findElement(
By.xpath("//div[@class='context-menu']//span[text()='Copy']")
);
await copyOption.click();
// Right-click to open context menu
IWebElement target = driver.FindElement(By.Id("context-area"));
Actions actions = new Actions(driver);
actions.ContextClick(target).Perform();
// Wait for context menu
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
wait.Until(ExpectedConditions.ElementIsVisible(
By.ClassName("context-menu")
));
// Click menu option
IWebElement copyOption = driver.FindElement(
By.XPath("//div[@class='context-menu']//span[text()='Copy']")
);
copyOption.Click();

Moving Mouse to Coordinates

Coordinate-Based Mouse Movement
Selenium 3 & 4 Medium
Actions actions = new Actions(driver);
// Move to element with offset from its center
WebElement canvas = driver.findElement(By.id("drawing-canvas"));
actions.moveToElement(canvas, 50, 30) // 50px right, 30px down from center
.click()
.perform();
// Move by offset from current position
actions.moveByOffset(100, 0) // Move 100px right
.click()
.moveByOffset(0, 50) // Move 50px down
.click()
.perform();
// Draw a line on canvas
actions.moveToElement(canvas, -100, -100)
.clickAndHold()
.moveByOffset(200, 200)
.release()
.perform();
actions = ActionChains(driver)
# Move to element with offset from its center
canvas = driver.find_element(By.ID, "drawing-canvas")
actions.move_to_element_with_offset(canvas, 50, 30) .click() .perform()
# Move by offset from current position
actions = ActionChains(driver)
actions.move_by_offset(100, 0) .click() .move_by_offset(0, 50) .click() .perform()
# Draw a line on canvas
actions = ActionChains(driver)
actions.move_to_element_with_offset(canvas, -100, -100) .click_and_hold() .move_by_offset(200, 200) .release() .perform()
// Move to element with offset
const canvas = await driver.findElement(By.id('drawing-canvas'));
await driver.actions({ async: true })
.move({ origin: canvas, x: 50, y: 30 })
.click()
.perform();
// Move by offset from current position
await driver.actions({ async: true })
.move({ x: 100, y: 0 })
.click()
.move({ x: 0, y: 50 })
.click()
.perform();
// Draw a line on canvas
await driver.actions({ async: true })
.move({ origin: canvas, x: -100, y: -100 })
.press()
.move({ x: 200, y: 200 })
.release()
.perform();
Actions actions = new Actions(driver);
// Move to element with offset from its center
IWebElement canvas = driver.FindElement(By.Id("drawing-canvas"));
actions.MoveToElement(canvas, 50, 30) // 50px right, 30px down
.Click()
.Perform();
// Move by offset from current position
new Actions(driver)
.MoveByOffset(100, 0) // Move 100px right
.Click()
.MoveByOffset(0, 50) // Move 50px down
.Click()
.Perform();
// Draw a line on canvas
new Actions(driver)
.MoveToElement(canvas, -100, -100)
.ClickAndHold()
.MoveByOffset(200, 200)
.Release()
.Perform();

Scroll Actions

Selenium 4 added scroll support to the Actions API:

Scroll Actions (Selenium 4)
Selenium 4 Stable
import org.openqa.selenium.interactions.WheelInput;
// Scroll to element
WebElement footer = driver.findElement(By.id("footer"));
Actions actions = new Actions(driver);
actions.scrollToElement(footer).perform();
// Scroll by amount
actions.scrollByAmount(0, 500).perform(); // Scroll down 500px
actions.scrollByAmount(0, -300).perform(); // Scroll up 300px
// Scroll from element
actions.scrollFromOrigin(
WheelInput.ScrollOrigin.fromElement(footer),
0, 200 // dx, dy
).perform();
// Scroll with viewport origin
actions.scrollFromOrigin(
WheelInput.ScrollOrigin.fromViewport(),
0, 1000
).perform();
from selenium.webdriver.common.actions.wheel_input import ScrollOrigin
# Scroll to element
footer = driver.find_element(By.ID, "footer")
actions = ActionChains(driver)
actions.scroll_to_element(footer).perform()
# Scroll by amount
actions = ActionChains(driver)
actions.scroll_by_amount(0, 500).perform() # Scroll down 500px
actions.scroll_by_amount(0, -300).perform() # Scroll up 300px
# Scroll from element origin
scroll_origin = ScrollOrigin.from_element(footer)
actions = ActionChains(driver)
actions.scroll_from_origin(scroll_origin, 0, 200).perform()
# Scroll from viewport origin
scroll_origin = ScrollOrigin.from_viewport(100, 100)
actions = ActionChains(driver)
actions.scroll_from_origin(scroll_origin, 0, 1000).perform()
// Scroll to element
const footer = await driver.findElement(By.id('footer'));
await driver.actions({ async: true })
.scroll(0, 0, 0, 500, footer)
.perform();
// Note: JavaScript scroll API differs - consider using executeScript
await driver.executeScript(
"arguments[0].scrollIntoView(true);",
footer
);
// Scroll by amount using JavaScript
await driver.executeScript("window.scrollBy(0, 500);");
// Scroll to element
IWebElement footer = driver.FindElement(By.Id("footer"));
new Actions(driver).ScrollToElement(footer).Perform();
// Scroll by amount
new Actions(driver).ScrollByAmount(0, 500).Perform(); // Down 500px
new Actions(driver).ScrollByAmount(0, -300).Perform(); // Up 300px
// Scroll from element
new Actions(driver)
.ScrollFromOrigin(
new WheelInputDevice.ScrollOrigin(footer, 0, 0),
0, 200
)
.Perform();

Chaining Multiple Actions

Complex Action Chains
Selenium 3 & 4 Medium
WebElement source = driver.findElement(By.id("draggable"));
WebElement target = driver.findElement(By.id("droppable"));
// Complex interaction chain
Actions actions = new Actions(driver);
actions.moveToElement(source)
.pause(Duration.ofMillis(200))
.click()
.pause(Duration.ofMillis(100))
.clickAndHold()
.moveToElement(target)
.pause(Duration.ofMillis(200))
.release()
.perform();
// Chain with keyboard modifier
actions.keyDown(Keys.CONTROL)
.click(driver.findElement(By.id("item1")))
.click(driver.findElement(By.id("item2")))
.click(driver.findElement(By.id("item3")))
.keyUp(Keys.CONTROL)
.perform(); // Multi-select with Ctrl+Click
source = driver.find_element(By.ID, "draggable")
target = driver.find_element(By.ID, "droppable")
# Complex interaction chain
actions = ActionChains(driver)
actions.move_to_element(source) .pause(0.2) .click() .pause(0.1) .click_and_hold() .move_to_element(target) .pause(0.2) .release() .perform()
# Chain with keyboard modifier
actions = ActionChains(driver)
actions.key_down(Keys.CONTROL) .click(driver.find_element(By.ID, "item1")) .click(driver.find_element(By.ID, "item2")) .click(driver.find_element(By.ID, "item3")) .key_up(Keys.CONTROL) .perform() # Multi-select with Ctrl+Click
const source = await driver.findElement(By.id('draggable'));
const target = await driver.findElement(By.id('droppable'));
// Complex interaction chain
await driver.actions({ async: true })
.move({ origin: source })
.pause(200)
.click()
.pause(100)
.press()
.move({ origin: target })
.pause(200)
.release()
.perform();
// Chain with keyboard modifier
const item1 = await driver.findElement(By.id('item1'));
const item2 = await driver.findElement(By.id('item2'));
const item3 = await driver.findElement(By.id('item3'));
await driver.actions({ async: true })
.keyDown(Key.CONTROL)
.click(item1)
.click(item2)
.click(item3)
.keyUp(Key.CONTROL)
.perform(); // Multi-select with Ctrl+Click
IWebElement source = driver.FindElement(By.Id("draggable"));
IWebElement target = driver.FindElement(By.Id("droppable"));
// Complex interaction chain
new Actions(driver)
.MoveToElement(source)
.Pause(TimeSpan.FromMilliseconds(200))
.Click()
.Pause(TimeSpan.FromMilliseconds(100))
.ClickAndHold()
.MoveToElement(target)
.Pause(TimeSpan.FromMilliseconds(200))
.Release()
.Perform();
// Chain with keyboard modifier
new Actions(driver)
.KeyDown(Keys.Control)
.Click(driver.FindElement(By.Id("item1")))
.Click(driver.FindElement(By.Id("item2")))
.Click(driver.FindElement(By.Id("item3")))
.KeyUp(Keys.Control)
.Perform(); // Multi-select with Ctrl+Click

Best Practices

  1. Use explicit waits after hover actions for menus to appear
  2. Add pauses when elements have animations
  3. Reset action chains between different interactions
  4. Test on different browsers - mouse behavior can vary
  5. Prefer element-based moves over coordinate-based when possible

Next Steps