Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Largest Contentful Paint image was lazily loaded #146

Open
rdallaire opened this issue Aug 21, 2022 · 3 comments
Open

Largest Contentful Paint image was lazily loaded #146

rdallaire opened this issue Aug 21, 2022 · 3 comments

Comments

@rdallaire
Copy link

rdallaire commented Aug 21, 2022

I'm trying to get perfect lighthouse scores.

When having an image in the Largest Contentful Paint (LCP) you get this warning when running Google Lighthouse Tests

⚠️ Largest Contentful Paint image was lazily loaded

My example
https://rossdallaire.com/notes/tv-interfaces-can-be-hard-to-find-active-selection/

https://pagespeed.web.dev/report?url=https%3A%2F%2Frossdallaire.com%2Fnotes%2Ftv-interfaces-can-be-hard-to-find-active-selection%2F&form_factor=mobile

When I look at the Industrial Empathy post that has an LCP image it has loading="eager" on the first image.
https://www.industrialempathy.com/posts/viral-software-deadlines/

I'm wondering if I did something incorrectly or it looks like there needs to be an updated way to detect LCP image and load it differently.


I haven't tried anything but I'm assuming it has something to do in img-dim.js
https://github.com/google/eleventy-high-performance-blog/blob/main/_11ty/img-dim.js#L91

May explore further if no one else takes a look.

@cramforce
Copy link
Collaborator

Good catch. I have a pretty aggressive optimization for my own site that renders pages in puppeteer and stores the information as to whether they appear above the fold.

const fs = require("fs-extra");
const puppeteer = require("puppeteer");

const updateHeroImages = async (content, outputPath) => {
  if (
    !outputPath.endsWith(".html") ||
    isAmp(content) ||
    // Must have an image element to be interesting.
    !/<img/.test(content)
  ) {
    return content;
  }
  const images = {};
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.emulate(puppeteer.devices["Pixel 2"]);
    await page.setContent(content, {
      waitUntil: "domcontentloaded",
    });
    for (let i of await page.$$("img")) {
      const img = await page.evaluate((i) => {
        const { top, left, bottom, right } = i.getBoundingClientRect();
        return {
          src: i.getAttribute("src"),
          rect: {
            top: Math.round(top),
            left: Math.round(left),
            bottom: Math.round(bottom),
            right: Math.round(right),
          },
        };
      }, i);
      if (!images[img.src]) {
        images[img.src] = {
          isInFirstMobileViewport: img.rect.top < 830,
          rect: img.rect,
        };
      }
    }
  } finally {
    await browser.close();
  }
  if (Object.keys(images).length) {
    const filename = `_data/images.json`;
    const allImages = JSON.parse(fs.readFileSync(filename));
    allImages[outputPath] = images;
    fs.writeFileSync(filename, JSON.stringify(allImages, null, "  "));
  }
  return content;
};

module.exports = {
  initArguments: {},
  configFunction: async (eleventyConfig, pluginOptions = {}) => {
    if (!process.env.UPDATE_HERO_IMAGES) {
      return;
    }
    eleventyConfig.addTransform("updateHeroImages", updateHeroImages);
  },
};

function isAmp(content) {
  return /\<html[^>]* amp/i.test(content);
}

I didn't put this into the open-source code because it is too slow to run on every render and I wanted to avoid complicating the template too much. See how it is guarded with UPDATE_HERO_IMAGES. I run this in a Github Action as a cron a couple times a day to gather the data.

Maybe the right solution is to simply default to eager for the first N images by default.

@rdallaire
Copy link
Author

rdallaire commented Aug 22, 2022

Ok great! I knew this would be fairly complex or heavy to do properly. I was also thinking about first N images by default even though it's not the perfect solution.

@indcoder
Copy link
Contributor

indcoder commented Oct 11, 2022 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants