dinbror / blazy

Hey, be lazy! bLazy.JS is a lightweight pure JavaScript script for lazy loading and multi-serving images. It's working in all modern browsers including IE7+.
http://dinbror.dk/blazy
MIT License
2.63k stars 356 forks source link

[documentation] Discuss strategies for aggressive image lazy loading #101

Closed gajus closed 8 years ago

gajus commented 8 years ago

I am working on a large website (very large, HTML alone is 1MB). When I have added image lazy loading, initially it has been added after DOMContentLoaded event is fired. This caused the first image not to appear for a relatively long time.

The solution I have ended up with is initialising Blazy as the first thing in the document and then revalidating Blazy state every 100 ms. This makes the first images to load as soon as they are added to the DOM.

Here is how it looks:

const domReady = require('domready');

/**
 * The reason for using setTimeout instead of setInterval
 * is to not re-run revalidate more often than the body
 * of the function can be evaluated.
 */
const createIntervalIterator = (callback, interval) => {
  let currentTimeout;

  const control = {};

  const iterator = () => {
    const startTime = new Date().getTime();

    callback();

    const evaluationTime = new Date().getTime() - startTime;

    const delayNextIteration = interval - evaluationTime;

    currentTimeout = setTimeout(iterator, delayNextIteration);
  };

  iterator();

  return () => {
    clearTimeout(currentTimeout);
  };
};

/**
 * Detect LTE IE8.
 *
 * @see http://stackoverflow.com/a/24408672/368691
 */
const isIe = document.all && !document.addEventListener;

const iterationIntervalTime = 100;

// Before DOM is loaded.
const blazy = new Blazy({
  offset: 100,
  selector: 'img[data-src]',
});

/**
 * Revalidation ensures that all images are in Blazy index.
 */
const revalidate = () => {
    blazy.revalidate();
};

if (!isIe) {
  cancelInterval = createIntervalIterator(() => {
    revalidate();
  }, iterationIntervalTime);
}

domReady(() => {
  if (cancelInterval) {
    cancelInterval();
  }

  // Use 1:
  // LTE IE8 does not participate in the interval revalidation
  // before DOMContentLoaded is fired.
  // Use 2:
  // Revalidate after domReady in case any images have been added
  // to the DOM after the last `revalidate` invocation.
  revalidate();
});
dinbror commented 8 years ago

Hey @gajus

What about serving the first images above the fold as normal images?

gajus commented 8 years ago

What about serving the first images above the fold as normal images?

That assumes that server-side knows which images are above the fold. There is no way for server-side to know that.

dinbror commented 8 years ago

True. But you could still load the first X images as regular images.

What about loading some of the content async to make document load faster?

gajus commented 8 years ago

True. But you could still load the first X images as regular images.

That assumes that the first images in the document are the same images that are visible in the initial viewport.

What about loading some of the content async to make document load faster?

This consideration is out of scope for Blazy. Frontend enhancement of any kind must not depend on the backend implementation.

Regardless, I am happy with the solution we implemented (that I have shared in the original post). I have shared it as a gesture of goodwill in hopes that it might prove useful to others.

dinbror commented 8 years ago

Regarding your solution I would use setTimeout instead of setInterval to ensure that you only revalidates blazy once per interval.

dinbror commented 8 years ago

And thanks for sharing gajus

gajus commented 8 years ago

Regarding your solution I would use setTimeout instead of setInterval to ensure that you only revalidates blazy once per interval.

Where do you see setInterval in my code?

gajus commented 8 years ago

It even has a comment to explain why I am not using setInterval.

/**
 * The reason for using setTimeout instead of setInterval
 * is to not re-run revalidate more often than the body
 * of the function can be evaluated.
 */
dinbror commented 8 years ago

Ha. Great. I need sleep or vacation. Looking at it on my phone and was sure I saw setInterval but good you're not using it