I’m trying to use Puppeteer to take a full-page screenshot of a website. But I want to make sure all the images are fully loaded before taking the screenshot. I’ve tried some code but it’s not working as expected. Here’s what I’ve got so far:
const puppeteer = require('puppeteer');
async function captureScreenshot() {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.setViewport({width: 1200, height: 800});
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
await page.waitForTimeout(2000);
await page.evaluate(() => {
const imgElements = document.querySelectorAll('img');
function waitForImgLoad(img) {
return new Promise((resolve) => {
if (img.complete) resolve();
img.onload = img.onerror = resolve;
});
}
return Promise.all(Array.from(imgElements).map(waitForImgLoad));
});
await page.screenshot({path: 'fullpage.png', fullPage: true});
await browser.close();
}
captureScreenshot();
Can someone help me figure out why this isn’t working? How can I make sure all images are loaded before taking the screenshot?
I’ve encountered similar issues with Puppeteer and full-page screenshots. Your approach is on the right track, but there are a few tweaks that can make it more reliable.
Instead of using waitForTimeout, try waitForNetworkIdle. This ensures all network requests have finished. Also, consider increasing the timeout for page.goto to allow for slower-loading pages.
Here’s a modified version that’s worked well for me:
await page.goto('https://example.com', {waitUntil: 'networkidle0', timeout: 60000});
await page.evaluate(() => new Promise(resolve => {
const images = document.querySelectorAll('img');
if (images.length === 0) resolve();
let loadedImages = 0;
images.forEach(img => {
if (img.complete) loadedImages++;
else img.onload = img.onerror = () => { loadedImages++; if (loadedImages === images.length) resolve(); };
});
if (loadedImages === images.length) resolve();
}));
This should ensure all images are loaded before taking the screenshot. Remember to adjust viewport size and consider using device emulation for more accurate results.
I’ve been in your shoes, Pete. Dealing with image loading in Puppeteer can be tricky. Here’s what worked for me:
Instead of relying on timeouts or network idle, I found success using the MutationObserver API. This approach monitors the DOM for changes, ensuring we catch dynamically loaded images too.
Here’s a snippet that’s been reliable:
await page.goto('https://example.com', {waitUntil: 'domcontentloaded'});
await page.evaluate(() => {
return new Promise((resolve) => {
const observer = new MutationObserver(() => {
const images = document.querySelectorAll('img');
const loaded = Array.from(images).every(img => img.complete);
if (loaded) {
observer.disconnect();
resolve();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true
});
});
});
await page.screenshot({path: 'fullpage.png', fullPage: true});
This method has consistently captured full-page screenshots with all images loaded for me. Give it a shot and see if it solves your issue.
hey pete, i’ve dealt with this before. try using page.waitForSelector(‘img’) to wait for the first image to load, then use networkidle0 in your goto options. also, increase the timeout. something like:
await page.goto(‘https://example.com’, {waitUntil: ‘networkidle0’, timeout: 60000});
await page.waitForSelector(‘img’);
that should help catch those pesky slow-loading images. good luck!