How to Find and Interact with Elements Within Iframes Using Zendriver
Zendriver treats iframes as regular DOM nodes that expose a content_document property, allowing you to query and interact with nested elements using the same select_all, query_selector_all, and Element methods you use for the main page.
Working with iframes in browser automation traditionally requires complex context switching, but the cdpdriver/zendriver library eliminates this friction. By exposing iframe contents as accessible document trees, you can search across all frames automatically or target specific iframes directly while maintaining the same high-level API for clicks, evaluations, and text input.
How Zendriver Handles Iframe Documents
In zendriver, an iframe is simply a DOM node whose children live in a separate content document. The core classes (Tab and Element) expose this relationship transparently. When you query an iframe element, the library automatically resolves the underlying content_document before executing Chrome DevTools Protocol (CDP) commands, allowing seamless traversal between parent pages and nested frames.
Querying Elements Across Frames and Inside Iframes
Automatic Cross-Frame Search with select_all
The Tab.select_all method provides built-in support for searching across all frames via the include_frames parameter. When set to True, the method first fetches all <iframe> elements, executes the selector within each frame's document, and then searches the top-level document.
# zendriver/core/tab.py – select_all implementation
# https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/tab.py#L306-L334
async def select_all(..., include_frames: bool = False) -> List[Element]:
...
if include_frames:
frames = await self.query_selector_all("iframe")
for fr in frames:
items.extend(await fr.query_selector_all(selector))
items.extend(await self.query_selector_all(selector))
...
This approach is ideal when you know an element exists somewhere on the page but are unsure which frame contains it.
Targeting Specific Iframes with query_selector_all
For precise control, manually locate an iframe and query within it. The Tab.query_selector_all method detects when the target node is an iframe and swaps it for the content_document before issuing the CDP querySelectorAll command.
# zendriver/core/tab.py – query_selector_all handling of iframes
# https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/tab.py#L311-L340
if _node:
doc = _node
if _node.node_name == "IFRAME":
doc = _node.content_document
This automatic resolution means you can chain selectors naturally without manually switching contexts.
Traversing Iframe DOM Trees with Element.children
The Element class handles iframe traversal in its children property. When the element is an iframe, the property returns the children of the content_document instead of the iframe element itself.
# zendriver/core/element.py – special case for iframe children
# https://github.com/cdpdriver/zendriver/blob/main/zendriver/core/element.py#L67-L82
@property
def children(self) -> list[Element]:
if self._node.node_name == "IFRAME":
frame = self._node.content_document
...
This allows you to walk nested DOM structures transparently using standard tree navigation.
Practical Implementation Example
import asyncio
from zendriver import Browser
async def demo():
# launch a headful Chromium instance
async with Browser() as browser:
tab = await browser.new_tab()
# load a page that contains an <iframe>
await tab.get("https://example.com/page-with-iframe")
# 1️⃣ Find a button inside any iframe (search all frames automatically)
button = await tab.select_all("button.submit", include_frames=True)
# `button` is a list; pick the first match
if button:
await button[0].click() # works exactly like a normal element
# 2️⃣ Manually locate a specific iframe and then query inside it
iframe = await tab.query_selector('iframe[name="target"]')
# query inside the iframe's document
inside = await iframe.query_selector_all('input[name="email"]')
if inside:
await inside[0].apply("(el) => el.value = 'test@example.com'")
await inside[0].click() # maybe a submit button nearby
# 3️⃣ Walk the DOM tree of an iframe
children = iframe.children # returns Element objects for iframe's DOM
for child in children:
print(child.node.node_name, child.attrs.get("id"))
# Run the demo
asyncio.run(demo())
Key Source Files and Implementation Details
The iframe handling logic spans several files in the repository:
zendriver/core/tab.py: ImplementsTab.select_all,query_selector_all, and the logic that swaps an iframe node for itscontent_documentzendriver/core/element.py: Provides theElementwrapper; itschildrenproperty exposes the DOM inside an iframezendriver/core/util.py: Contains helper functions (e.g.,filter_recurse) used by element tree traversalzendriver/cdp/dom.py: Low-level CDP commands (querySelectorAll,getDocument, etc.) called by the high-level API
Summary
- Use
select_all(..., include_frames=True)to search across all frames in a single call - Query specific iframes using
query_selector_allon the iframe element, which automatically resolves thecontent_document - Access nested iframe children via the
Element.childrenproperty for transparent DOM walking - Interact with found elements using standard methods like
click()andapply()regardless of whether they reside inside iframes
Frequently Asked Questions
Does zendriver automatically search inside iframes by default?
No. By default, query_selector and query_selector_all only search the top-level document. You must explicitly set include_frames=True in select_all or manually target the iframe element first to search within it.
How do I access the DOM inside a specific iframe?
First, select the iframe element using await tab.query_selector('iframe[name="target"]'). Then call query_selector_all on that element. The library automatically detects the iframe node and uses its content_document for the query.
Can I click elements that are found inside iframes?
Yes. Once you obtain an Element reference, it does not matter whether the element is inside an iframe or the main document. You can call click(), apply(), evaluate(), and other methods exactly as you would for top-level elements.
What is the content_document property in zendriver?
The content_document represents the document root of an iframe's internal DOM as exposed by the Chrome DevTools Protocol. Zendriver handles the context switching internally when you query against an iframe element, allowing you to treat the nested document as a standard queryable node.
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 →