How to detect when a webpage finishes loading before generating PDF with Puppeteer

I’m looking for assistance with creating PDFs from web pages using Puppeteer. My application is a single-page application that loads dynamic content like charts and graphs, which appear only after certain calculations.

The key issue I am facing is that I want to generate the PDF only after the entire page has completely loaded and rendered. Using fixed delays is not a reliable solution in this context.

Here’s the code I have at the moment:

const chromeBrowser = await puppeteer.launch({
    executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
    ignoreHTTPSErrors: true,
    headless: true,
    devtools: false,
    args: ['--no-sandbox', '--disable-setuid-sandbox']
});

const webPage = await chromeBrowser.newPage();

await webPage.goto(targetUrl, {
    waitUntil: 'networkidle2'
});

await webPage.type('#user_name', 'john');
await webPage.type('#user_password', 'secret');

await webPage.click('#submit_btn');
await webPage.waitFor(2000); // I would like to avoid this line

await webPage.pdf({
    path: pdfFileName,
    displayHeaderFooter: true,
    headerTemplate: '',
    footerTemplate: '',
    printBackground: true,
    format: 'A4'
});

The challenge is that waitForSelector isn’t applicable because the charts and graphs don’t have specific selectors. They become visible only after the data processing is complete.

What’s the best way to ensure all content, including dynamic elements, has fully loaded?

I’ve encountered this problem with scraping complex dashboards as well. Here’s a solution that proved effective for me: instead of relying on networkidle2, get more specific with your wait conditions. Start with page.waitForLoadState('domcontentloaded'), then use page.evaluate() to verify the presence of your content within the DOM. Specifically for charts, I monitor for canvas elements that exhibit actual dimensions or determine if the chart library has finished rendering. A helpful tip is to look for CSS classes that are applied when charts complete rendering, as most charting libraries typically add these to signify completion.

I had success with a custom waiting mechanism using page.waitForFunction() to check the actual rendered content. Most charting libraries modify the DOM or add specific attributes when they finish rendering - like changing element heights from zero to actual values or setting data attributes. You can wait for these changes directly. Another option is injecting a small script that sets a global flag when your calculations and rendering finish, then use page.waitForFunction(() => window.renderingComplete === true). This beats network-based waits and avoids arbitrary timeouts. Just figure out what specific DOM changes happen when your dynamic content loads.

try using page.waitForFunction() to check for specific conditions, like confirming your chart container has children or a global var that indicates rendering done. way more reliable than using timeouts!