Search code examples
javascriptnode.jsscreenshotpuppeteergoogle-chrome-headless

How to wait for all images to load from page.evaluate function in puppeteer when the DOM is populated using a client side function


I am trying to make the code execution wait for all images to load before puppeteer takes a screenshot. My DOM gets populated when initData() function is called, which is defined in the client side js file. Delay or timeout is an option but I am sure there must be a more efficient way of doing it.

    (async (dataObj) => {
             const url = dataObj.url;
             const payload = dataObj.payload;
             const browser = await puppeteer.launch({ headless: false,devtools:false});
             const page = await browser.newPage();
             await page.goto(url,{'waitUntil': 'networkidle0'});

             await page.evaluate((payload) => {
               initData(payload);
                //initData is a client side function that populates the DOM, need to wait 
                //here till the images are loaded. 
               },payload)

             await page.setViewport({ width: 1280, height: 720 })
             await page.screenshot({ path: 'test.png' });
             await browser.close();
    })(dataObj)

Thanks in advance.


Solution

  • As mentioned in another answer, image elements have a complete property. You can write a function that returns true when all images in the document have been fetched:

    function imagesHaveLoaded() { return Array.from(document.images).every((i) => i.complete); }
    

    And you can wait for that function like so:

    await page.waitForFunction(imagesHaveLoaded);
    

    Putting the two together with your original code and adding a timeout so it doesn’t wait indefinitely, we get:

    function imagesHaveLoaded() {
        return Array.from(document.images).every((i) => i.complete);
    }
    
    (async (dataObj) => {
             const url = dataObj.url;
             const payload = dataObj.payload;
             const browser = await puppeteer.launch({ headless: false, devtools: false});
             const page = await browser.newPage();
             await page.goto(url, { waitUntil: 'networkidle0' });
    
             await page.evaluate((payload) => {
               initData(payload);
             }, payload);
    
             await page.waitForFunction(imagesHaveLoaded, { timeout: YOUR_DESIRED_TIMEOUT });
    
             await page.setViewport({ width: 1280, height: 720 })
             await page.screenshot({ path: 'test.png' });
             await browser.close();
    })(dataObj)