"""
Example of implementing hover functionality for elements.
This shows how to hover over elements to trigger hover states and tooltips.
"""
import asyncio
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
from dotenv import load_dotenv
from pydantic import BaseModel
load_dotenv()
from browser_use.agent.service import Agent, Controller
from browser_use.agent.views import ActionResult
from browser_use.browser import BrowserSession
from browser_use.llm import ChatOpenAI
# Initialize controller
controller = Controller()
class HoverAction(BaseModel):
"""Parameters for hover action"""
index: int | None = None
xpath: str | None = None
selector: str | None = None
@controller.registry.action(
'Hover over an element',
param_model=HoverAction, # Define this model with at least "index: int" field
)
async def hover_element(params: HoverAction, browser_session: BrowserSession):
"""
Hovers over the element specified by its index from the cached selector map or by XPath.
"""
try:
element_node = None
if params.xpath:
# Find element by XPath using CDP
cdp_session = await browser_session.get_or_create_cdp_session()
result = await cdp_session.cdp_client.send.Runtime.evaluate(
params={
'expression': f"""
(() => {{
const element = document.evaluate('{params.xpath}', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
if (element) {{
const rect = element.getBoundingClientRect();
return {{found: true, x: rect.x + rect.width/2, y: rect.y + rect.height/2}};
}}
return {{found: false}};
}})()
""",
'returnByValue': True,
},
session_id=cdp_session.session_id,
)
element_info = result.get('result', {}).get('value', {})
if not element_info.get('found'):
raise Exception(f'Failed to locate element with XPath {params.xpath}')
x, y = element_info['x'], element_info['y']
elif params.selector:
# Find element by CSS selector using CDP
cdp_session = await browser_session.get_or_create_cdp_session()
result = await cdp_session.cdp_client.send.Runtime.evaluate(
params={
'expression': f"""
(() => {{
const element = document.querySelector('{params.selector}');
if (element) {{
const rect = element.getBoundingClientRect();
return {{found: true, x: rect.x + rect.width/2, y: rect.y + rect.height/2}};
}}
return {{found: false}};
}})()
""",
'returnByValue': True,
},
session_id=cdp_session.session_id,
)
element_info = result.get('result', {}).get('value', {})
if not element_info.get('found'):
raise Exception(f'Failed to locate element with CSS Selector {params.selector}')
x, y = element_info['x'], element_info['y']
elif params.index is not None:
# Use index to locate the element
selector_map = await browser_session.get_selector_map()
if params.index not in selector_map:
raise Exception(f'Element index {params.index} does not exist - retry or use alternative actions')
element_node = selector_map[params.index]
# Get element position
if not element_node.absolute_position:
raise Exception(f'Element at index {params.index} has no position information')
x = element_node.absolute_position.x + element_node.absolute_position.width / 2
y = element_node.absolute_position.y + element_node.absolute_position.height / 2
else:
raise Exception('Either index, xpath, or selector must be provided')
# Perform hover using CDP mouse events
cdp_session = await browser_session.get_or_create_cdp_session()
# Move mouse to the element position
await cdp_session.cdp_client.send.Input.dispatchMouseEvent(
params={
'type': 'mouseMoved',
'x': x,
'y': y,
},
session_id=cdp_session.session_id,
)
# Wait a bit for hover state to trigger
await asyncio.sleep(0.1)
msg = (
f'🖱️ Hovered over element at index {params.index}'
if params.index is not None
else f'🖱️ Hovered over element with XPath {params.xpath}'
if params.xpath
else f'🖱️ Hovered over element with selector {params.selector}'
)
return ActionResult(extracted_content=msg, include_in_memory=True)
except Exception as e:
error_msg = f'❌ Failed to hover over element: {str(e)}'
return ActionResult(error=error_msg)
async def main():
"""Main function to run the example"""
browser_session = BrowserSession()
await browser_session.start()
llm = ChatOpenAI(model='gpt-4.1')
# Create the agent with hover capability
agent = Agent(
task="""
Go to a website with hover interactions, like https://www.w3schools.com/howto/howto_css_dropdown.asp
Try hovering over the dropdown menu to see the dropdown items appear.
Then describe what happens when you hover.
""",
llm=llm,
browser_session=browser_session,
controller=controller,
)
# Run the agent
await agent.run(max_steps=10)
# Cleanup
await browser_session.kill()
if __name__ == '__main__':
asyncio.run(main())