Handling iframes in Puppeteer requires understanding that each iframe is a separate HTML document with its own context. You need to get a reference to the iframe before interacting with its content.
Basic Iframe Access
Method 1: Using contentFrame()
// Get iframe by selector and access its frame
const iframeElement = await page.$('iframe#myFrame');
const frame = await iframeElement.contentFrame();
// Now you can interact with the iframe content
await frame.click('#button-inside-iframe');
await frame.type('#input-field', 'Hello World');
Method 2: Using page.frames()
// Get all frames on the page
const frames = await page.frames();
// Find specific frame by name or URL
const targetFrame = frames.find(frame =>
frame.name() === 'myFrameName' ||
frame.url().includes('iframe-content.html')
);
if (targetFrame) {
await targetFrame.click('#button');
}
Waiting for Iframes to Load
Always wait for iframes to fully load before interacting with them:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com/page-with-iframe');
// Wait for iframe to appear
await page.waitForSelector('iframe#myFrame');
// Get iframe reference
const iframeElement = await page.$('iframe#myFrame');
const frame = await iframeElement.contentFrame();
// Wait for content inside iframe to load
await frame.waitForSelector('#content-inside-iframe');
// Now safely interact with iframe content
const text = await frame.$eval('#title', el => el.textContent);
console.log('Iframe title:', text);
await browser.close();
})();
Working with Multiple Iframes
async function handleMultipleIframes(page) {
// Get all iframes on the page
const iframeElements = await page.$$('iframe');
for (let i = 0; i < iframeElements.length; i++) {
const frame = await iframeElements[i].contentFrame();
if (frame) {
// Check if frame contains specific content
const hasLoginForm = await frame.$('#login-form');
if (hasLoginForm) {
await frame.type('#username', 'user@example.com');
await frame.type('#password', 'password123');
await frame.click('#login-button');
break;
}
}
}
}
Extracting Data from Iframes
async function extractIframeData(page) {
const iframeElement = await page.$('iframe.data-frame');
const frame = await iframeElement.contentFrame();
// Extract data from iframe
const data = await frame.evaluate(() => {
const rows = Array.from(document.querySelectorAll('table tr'));
return rows.map(row => {
const cells = Array.from(row.querySelectorAll('td'));
return cells.map(cell => cell.textContent.trim());
});
});
return data;
}
Cross-Origin Iframe Handling
For iframes from different domains, you may need special handling:
async function handleCrossOriginIframe(page) {
try {
const iframeElement = await page.$('iframe[src*="external-domain.com"]');
const frame = await iframeElement.contentFrame();
// Note: Cross-origin iframes may have limited access
if (frame) {
// Only basic operations may work
await frame.waitForLoadState('networkidle');
const url = frame.url();
console.log('Iframe URL:', url);
}
} catch (error) {
console.log('Cross-origin iframe access restricted:', error.message);
}
}
Error Handling and Best Practices
async function robustIframeHandling(page) {
try {
// Wait for iframe with timeout
await page.waitForSelector('iframe', { timeout: 10000 });
const iframeElement = await page.$('iframe');
if (!iframeElement) {
throw new Error('Iframe not found');
}
const frame = await iframeElement.contentFrame();
if (!frame) {
throw new Error('Cannot access iframe content');
}
// Wait for iframe content to be ready
await frame.waitForFunction(
() => document.readyState === 'complete',
{ timeout: 15000 }
);
// Perform actions
await frame.click('#submit-button');
} catch (error) {
console.error('Iframe handling failed:', error.message);
// Fallback: try alternative approach
const frames = await page.frames();
const targetFrame = frames.find(f => f.url().includes('target-content'));
if (targetFrame) {
await targetFrame.click('#submit-button');
}
}
}
Common Iframe Patterns
Nested Iframes
// Handle iframes within iframes
const outerIframe = await page.$('iframe#outer');
const outerFrame = await outerIframe.contentFrame();
const innerIframe = await outerFrame.$('iframe#inner');
const innerFrame = await innerIframe.contentFrame();
await innerFrame.click('#deep-nested-button');
Dynamic Iframes
// Wait for dynamically created iframes
await page.waitForFunction(
() => document.querySelectorAll('iframe').length > 0,
{ timeout: 10000 }
);
const dynamicFrame = await page.frames().find(
frame => frame.url().includes('dynamic-content')
);
if (dynamicFrame) {
await dynamicFrame.waitForSelector('#dynamic-element');
}
Key Points to Remember
- Always wait for iframes to load completely before interaction
- Use
contentFrame()
for element-based access orpage.frames()
for frame collection - Cross-origin iframes may have access restrictions
- Implement proper error handling for robust iframe interactions
- Consider using
waitForFunction()
for complex loading scenarios