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: Implements Tab.select_all, query_selector_all, and the logic that swaps an iframe node for its content_document
  • zendriver/core/element.py: Provides the Element wrapper; its children property exposes the DOM inside an iframe
  • zendriver/core/util.py: Contains helper functions (e.g., filter_recurse) used by element tree traversal
  • zendriver/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_all on the iframe element, which automatically resolves the content_document
  • Access nested iframe children via the Element.children property for transparent DOM walking
  • Interact with found elements using standard methods like click() and apply() 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →