How to Manage Multiple Browser Tabs and Windows in Zendriver
Zendriver treats every browser view—whether a tab, separate window, iframe, or background script—as a Tab object, allowing you to open, switch, resize, and close multiple contexts programmatically using the Chrome DevTools Protocol (CDP).
Managing multiple browser tabs and windows in Zendriver requires understanding how the library abstracts CDP targets into high-level Python objects. Unlike traditional Selenium-based tools, Zendriver uses a stateful Tab class that maintains persistent connections to browser contexts, enabling sophisticated window management without losing DOM references or session state.
Understanding the Tab and Browser Architecture
In zendriver/core/tab.py, the Tab class inherits from the low-level Connection class, which handles the WebSocket communication with Chrome's DevTools Protocol. This design means every tab, window, or iframe is technically a Tab instance with its own CDP session.
The Browser class in zendriver/core/browser.py serves as the central registry. It maintains a mutable list called self.targets that tracks every active CDP target, including pages, service workers, and background scripts. When you open new tabs or windows, Zendriver adds corresponding Tab objects to this registry.
Opening New Tabs and Windows
Zendriver provides a unified interface for creating new browsing contexts through the Browser.get() method, which supports two mutually exclusive flags for controlling window behavior.
Opening a New Tab
To open a URL in a new tab within the existing browser window, pass new_tab=True to Browser.get():
import zendriver as zd
browser = await zd.start()
main_tab = await browser.get("https://example.com")
# Open a new tab in the same window
second_tab = await browser.get("https://python.org", new_tab=True)
Internally, this triggers cdp.target.create_target and returns a fresh Tab instance that Zendriver adds to browser.targets.
Opening a New Window
To spawn a separate OS window, use new_window=True instead:
# Open a new separate window
new_window_tab = await browser.get("https://github.com", new_window=True)
Despite being in a different window, the returned object is still a Tab instance with identical methods and properties.
Switching Between Tabs and Windows
Once you have multiple tabs open, you need to shift focus between them. Zendriver provides several mechanisms for context switching.
Activating a Specific Tab
The Tab.bring_to_front() method activates a specific target, sending the CDP Target.activateTarget command:
# Switch focus to the second tab
await second_tab.bring_to_front()
# Now perform actions on this active tab
await second_tab.wait_for("h1")
This is equivalent to clicking on a browser tab or window to focus it.
Enumerating Open Tabs
The Browser class provides the tabs property (defined in zendriver/core/browser.py lines 61-68) that returns a list of all page-type targets:
# Get all open tabs/windows
all_tabs = browser.tabs
for tab in all_tabs:
print(f"Tab URL: {await tab.url}")
This property filters self.targets to include only objects where type_ == "page", excluding service workers and background scripts.
Accessing the Main Tab
The main_tab property provides direct access to the initial tab created when the browser started:
original_tab = browser.main_tab
await original_tab.bring_to_front()
Managing Window State and Geometry
Beyond simple navigation, Zendriver allows precise control over window positioning, sizing, and state.
Querying Window Information
The Tab.get_window() method returns the underlying OS window identifier and current bounds:
window_id, bounds = await tab.get_window()
print(f"Window ID: {window_id}")
print(f"Position: ({bounds.left}, {bounds.top})")
print(f"Size: {bounds.width} x {bounds.height}")
This wraps the CDP Browser.getWindowBounds command.
Resizing and Repositioning
Use Tab.set_window_state() to modify window geometry:
# Move and resize
await tab.set_window_state(left=100, top=100, width=1280, height=720)
# Convenience methods
await tab.maximize()
await tab.minimize()
await tab.fullscreen()
These methods are implemented in zendriver/core/tab.py (lines 109-160) and handle the CDP Browser.setWindowBounds call.
Tiling Multiple Windows
For managing multiple windows simultaneously, the Browser.tile_windows() method automatically arranges all open windows in a grid:
# Arrange all windows evenly across the screen
await browser.tile_windows()
This method (found in zendriver/core/browser.py lines 86-145) calculates optimal positions based on screen size and calls set_window_size for each window.
Closing Tabs and Windows
To clean up specific contexts without shutting down the entire browser, use the Tab.close() method:
# Close a specific tab or window
await second_tab.close()
This sends the Target.closeTarget CDP command and removes the target from browser.targets. If the closed tab was the active one in a window, Chrome automatically switches focus to another tab in that window.
Complete Working Example
Here is a comprehensive example demonstrating the full lifecycle of multiple tab and window management:
import asyncio
import zendriver as zd
async def manage_multiple_contexts():
# Launch browser with UI visible
browser = await zd.start(headless=False)
try:
# 1. Open initial page in main tab
main = await browser.get("https://example.com")
# 2. Open new tab in same window
docs_tab = await browser.get("https://docs.python.org", new_tab=True)
# 3. Open separate window
github_tab = await browser.get("https://github.com", new_window=True)
# 4. List all open contexts
print(f"Total tabs: {len(browser.tabs)}")
for idx, tab in enumerate(browser.tabs):
url = await tab.url
print(f" [{idx}] {url}")
# 5. Switch to Python docs and resize
await docs_tab.bring_to_front()
await docs_tab.set_window_state(left=50, top=50, width=1000, height=800)
# 6. Tile all windows (arranges GitHub window too)
await browser.tile_windows()
# 7. Close specific tab
await docs_tab.close()
# 8. Return focus to main tab
await browser.main_tab.bring_to_front()
finally:
# Cleanup
await browser.stop()
if __name__ == "__main__":
asyncio.run(manage_multiple_contexts())
Summary
- Zendriver unifies tabs, windows, and iframes under the
Tabclass, which inherits fromConnectionto maintain persistent CDP sessions. - Create new contexts using
Browser.get()withnew_tab=Trueornew_window=True, both returningTabinstances tracked inbrowser.targets. - Switch focus between contexts by calling
await tab.bring_to_front(), which activates the specific CDP target. - Enumerate open pages via the
browser.tabsproperty (filtering for page-type targets) or access the original context throughbrowser.main_tab. - Control window geometry using
tab.set_window_state(),maximize(),minimize(), andbrowser.tile_windows()for automatic arrangement. - Clean up individual contexts with
await tab.close()without terminating the entire browser session.
Frequently Asked Questions
How do I switch between tabs in Zendriver?
Use the bring_to_front() method on the specific Tab instance you want to activate. This sends the CDP Target.activateTarget command to Chrome, bringing that tab or window into focus. For example: await my_tab.bring_to_front(). You can enumerate available tabs using browser.tabs to find the specific instance you need.
What is the difference between a tab and a window in Zendriver?
Technically, both are represented by the same Tab class and stored in the same browser.targets registry. The distinction lies in how they are created: use new_tab=True in Browser.get() to open a new tab within an existing window, or new_window=True to spawn a separate OS window. Both return a Tab instance with identical methods for navigation, resizing, and closing.
How can I resize browser windows programmatically?
Zendriver provides several methods on the Tab class for window management. Use await tab.set_window_state(left, top, width, height) for precise positioning, or use convenience methods like await tab.maximize(), await tab.minimize(), and await tab.fullscreen(). For arranging multiple windows automatically, use await browser.tile_windows(), which calculates a grid layout and applies it to all open windows.
Does Zendriver support headless mode with multiple windows?
Yes, Zendriver supports headless mode via the headless=True parameter in start(), and you can still create multiple tabs using new_tab=True. However, opening new separate windows with new_window=True in headless mode may behave differently depending on your Chrome version, as headless Chrome traditionally runs without a window manager. For full window management capabilities including resizing and tiling, run with headless=False to enable the graphical interface.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →