Files
browser-use/examples/custom-functions/hover_element.py
Nick Sweeting a1f1330aa6 linter fixes
2025-08-14 14:32:32 -07:00

168 lines
5.0 KiB
Python

"""
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())