Raza
Raza

Reputation: 3403

Puppeteer - How to fill form that is inside an iframe?

I have to fill out a form that is inside an iframe, here the sample page. I cannot access by simply using page.focus() and page.type(). I tried to get the form iframe by using const formFrame = page.mainFrame().childFrames()[0], which works but I cannot really interact with the form iframe.

Upvotes: 46

Views: 53490

Answers (5)

dogdog
dogdog

Reputation: 133

For nested iframes, try these codes to find element lazy-loaded iframe:

// wait for iframe network queries
await page.waitForNetworkIdle()
console.log("iframe 准备好了")
let theFrame = ""
for (let i = 0; i < 50; i++) {
    // wait for iframe to load , max 50 seconds
    await new Promise(resolve => setTimeout(resolve, 1000))
    let frames = page.frames()
    for (let frame of frames) {
        if (!!theFrame) break
        // wait for lazy content to load
        await frame.content().then((res)=>{
            if (res.toString().includes("以证明您是真人")) {
                theFrame = frame
            }
        })
    }
    if (!!theFrame) break
}
console.log("theFrame:", theFrame)
theFrame.click('button[aria-label="音频"]')

Upvotes: 0

Tarun Lalwani
Tarun Lalwani

Reputation: 146630

Instead of figuring out how to get inside the iFrame and type, I would simplify the problem by navigating to the IFrame URL directly

https://warranty.goodmanmfg.com/registration/NewRegistration/NewRegistration.aspx?Sender=Goodman

Make your script directly go to the above URL and try automating, it should work

Edit-1: Using frames

Since the simple approach didn't work for you, we do it with the frames itself

Below is a simple script which should help you get started

const puppeteer = require('puppeteer');
(async () => {
    const browser = await puppeteer.launch({ headless: false });
    const page = await browser.newPage();
    await page.goto('http://www.goodmanmfg.com/product-registration', { timeout: 80000 });
    var frames = await page.frames();
    var myframe = frames.find(
        f =>
            f.url().indexOf("NewRegistration") > -1);
    const serialNumber = await myframe.$("#MainContent_SerNumText");
    await serialNumber.type("12345");

    await page.screenshot({ path: 'example.png' });

    await browser.close();
})();

The output is

Type serial

Upvotes: 41

mykhailoklym94
mykhailoklym94

Reputation: 588

If you can't select/find iFrame read this:

I had an issue with finding stripe elements. The reason for that is the following:

You can't access an with different origin using JavaScript, it would be a huge security flaw if you could do it. For the same-origin policy browsers block scripts trying to access a frame with a different origin. See more detailed answer here

Therefore when I tried to use puppeteer's methods:Page.frames() and Page.mainFrame(). ElementHandle.contentFrame() I did not return any iframe to me. The problem is that it was happening silently and I couldn't figure out why it couldn't find anything.

Adding these arguments to launch options solved the issue: '--disable-web-security', '--disable-features=IsolateOrigins,site-per-process'

Upvotes: 26

Sahil Paudel
Sahil Paudel

Reputation: 321

Though you have figured out but I think I have better solution. Hope it helps.

async doFillForm() {
    return await this.page.evaluate(() => {
       let iframe = document.getElementById('frame_id_where_form_is _present');
       let doc = iframe.contentDocument;
       doc.querySelector('#username').value='Bob';
       doc.querySelector('#password').value='pass123';
    });
}

Upvotes: 4

Raza
Raza

Reputation: 3403

I figured it out myself. Here's the code.

console.log('waiting for iframe with form to be ready.');
await page.waitForSelector('iframe');
console.log('iframe is ready. Loading iframe content');

const elementHandle = await page.$(
    'iframe[src="https://example.com"]',
);
const frame = await elementHandle.contentFrame();

console.log('filling form in iframe');
await frame.type('#Name', 'Bob', { delay: 100 });

Upvotes: 64

Related Questions