Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 521
Naitik Adani Avatar asked Aug 02 '18 10:08

Naitik Adani


People also ask

How do you make a puppeteer wait?

You can use Puppeteer's page. waitForNavigation() method here to explicitly wait for this event to happen and then continue your script. The accepted notation in Puppeteer's case is by using the Promise. all() method to wait for the click to happen and the navigation to happen before continuing.

How do you evaluate a page in a puppeteer?

evaluate() method. Evaluates a function in the page's context and returns the result. If the function passed to page. evaluteHandle returns a Promise, the function will wait for the promise to resolve and return its value.


1 Answers

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)
like image 158
Aankhen Avatar answered Oct 12 '22 10:10

Aankhen