mirror of
https://github.com/browser-use/browser-use
synced 2026-05-06 17:52:15 +02:00
168 lines
5.0 KiB
Python
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())
|