Lazy load

v2.0.0 / 2018-03-06

Simple, tiny, no dependency, lazy loader. Source code available on GitHub.

Doesn't preload, unload, mess with media queries, emit events, offer APIs etc.
Does periodically check all appropriate elements elements to see if they're in the viewport or not using a throttled requestAnimationFrame loop.

When an element is in the view port it swaps data-src/data-srcset on img, source and iframes to src/srcset. It also adds a load listener and removes the data- attribute on load to allow you to hook styles up to the two different states.

If data-srcset to srcset and picturefill is present, attempts to run picturefill on the element.

When it runs out of elements to watch, the loop ends.

v2 uses IntersectionObserver if available and if not, it uses requestAnimationFrame if available. If neither are available it does nothing.

Setting up

In your HTML:

<script src="/lazyload.js"></script>

In your JavaScript (on DOM ready):

lazyLoad();

Options

These options are the defaults, and can be overridden on init:

var options = {
  pageUpdatedEventName: 'page:updated', // how your app tells the rest of the app an update happened
  elements: 'img[data-src], img[data-srcset], source[data-srcset], iframe[data-src], video[data-src]', // maybe you just want images?
  rootMargin: '0px', // IntersectionObserver option
  threshold: 0, // IntersectionObserver option
  maxFrameCount: 10, // 60fps / 10 = 6 times a second
};
lazyLoad(options);
pageUpdatedEventName
string - an event name to listen for when the page has new content that might need to be lazy loaded. If set to `false` lazyLoad will only run on initial page load.
elements
string - string of items to look to lazy load.
rootMargin
string - IntersectionObserver rootMargin option.
threshold
integer/array - IntersectionObserver threshold option.
maxFrameCount
integer - as requestAnimationFrame runs as the monitor refreshes, this could be 60 times a second, to throttle this we can tell the script every NUM of frames.

Useful CSS

This demo page uses the following CSS:

picture,
img,
iframe {
  display: block;
  border: 0 none;
  opacity: 1;
  transition: opacity .25s;
  width: 500px;
  height: 281px;
  background: #f2f2f2;
  outline: 0 none;
}

iframe {
  width: 560px;
  height: 315px;
}

img[data-src]:not([src^="data:image"]),
img[data-srcset]:not([src^="data:image"]),
iframe[data-src] {
  opacity: 0;
}

img[data-srcset][src=""] {
  content: url("data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==");
}
      

Its well advisable to give the images/picture/iframes an intrinsic size to stop huge repaints: Either by giving a pixel size and using a transparent gif:

<img width="500" height="281" data-src="/images/greenflash_800.jpg" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
      

Or using the padding trick:

.img-container {
  position: relative;
  height: 0;
  padding-bottom: 56.25%; // for 16:9 images
}

.img-container img {
  position: absolute;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
      

Standard image, no src

  <img data-src="/images/greenflash_800.jpg">

Standard image, src=""

  <img src="" data-src="/images/greenflash_800.jpg">

Image on different domain

  <img data-src="http://slider.dev.area17.com/images/960/02.jpg" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">

Standard image with LQIP

  <img data-src="/images/greenflash_800.jpg" src="data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAABAAD/4QN8aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAxNCA3OS4xNTE0ODEsIDIwMTMvMDMvMTMtMTI6MDk6MTUgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6Y2QwNDkwYzktYTE1Yy00OTY2LTg2MWMtM2QwMWNjODk2MjhkIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkFDMjExNDdBQjRERDExRTY5M0NCRTVBNkJEODRGOTJCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkFDMjExNDc5QjRERDExRTY5M0NCRTVBNkJEODRGOTJCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjcyMzI0NTQ5LWFlMGEtNDlmZi1iNjBkLTdhYjZjNzUzOTUxMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpjZDA0OTBjOS1hMTVjLTQ5NjYtODYxYy0zZDAxY2M4OTYyOGQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAaGRknHCc+JSU+Qi8vL0JHPTs7PUdHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHARwnJzMmMz0mJj1HPTI9R0dHRERHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAA4AGQDASIAAhEBAxEB/8QBGwAAAwEBAQEBAQEBAQAAAAAAAQACAwQFBgcICQoLAQEBAQEBAQEBAQEBAQAAAAAAAQIDBAUGBwgJCgsQAAICAQMCAwQHBgMDBgIBNQEAAhEDIRIxBEFRIhNhcTKBkbFCoQXRwRTwUiNyM2LhgvFDNJKishXSUyRzwmMGg5Pi8qNEVGQlNUUWJnQ2VWWzhMPTdePzRpSkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2EQACAgAFAQYGAQMBAwUDBi8AARECIQMxQRJRYXGBkSITMvChsQTB0eHxQlIjYnIUkjOCQySisjRTRGNzwtKDk6NU4vIFFSUGFiY1ZEVVNnRls4TD03Xj80aUpIW0lcTU5PSltcXV5fVWZnaG/9oADAMBAAIRAxEAPwDipNOm1O10QgBoBsRaEWAik7XURaEWFMNq7Xo2J2KSwcpizT1GDmYqSGFIp2MWCGgzpW6VA6BBoQdQHQRcSagwGNrY9Ii1tZIg5drQi77EiCkQZCK7Xfau1klOYxczB7DFgxbIOMwczF7TBzMGySDk2q9GxVJINQ6B5hN0E3nJuDpDYeYTdBNzJqDamqcRNreySwaIY3oM1JILZLG9kzbIgosFBm5mbSQVSue9UDhGRsZFV0CxkbGRVclKGRr1FVhofUQciqsAScjJyKrcDJByMnIqtIT6iqrQf//Z">

Simple responsive image

  <img src="" data-srcset="/images/greenflash_800.jpg 800w, /images/greenflash_400.jpg 400w" sizes="500px">

Responsive image with LQIP

  <img data-srcset="/images/greenflash_800.jpg 800w, /images/greenflash_400.jpg 400w" sizes="500px" src="data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAABAAD/4QN8aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAxNCA3OS4xNTE0ODEsIDIwMTMvMDMvMTMtMTI6MDk6MTUgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6Y2QwNDkwYzktYTE1Yy00OTY2LTg2MWMtM2QwMWNjODk2MjhkIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkFDMjExNDdBQjRERDExRTY5M0NCRTVBNkJEODRGOTJCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkFDMjExNDc5QjRERDExRTY5M0NCRTVBNkJEODRGOTJCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjcyMzI0NTQ5LWFlMGEtNDlmZi1iNjBkLTdhYjZjNzUzOTUxMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpjZDA0OTBjOS1hMTVjLTQ5NjYtODYxYy0zZDAxY2M4OTYyOGQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAaGRknHCc+JSU+Qi8vL0JHPTs7PUdHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHARwnJzMmMz0mJj1HPTI9R0dHRERHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAA4AGQDASIAAhEBAxEB/8QBGwAAAwEBAQEBAQEBAQAAAAAAAQACAwQFBgcICQoLAQEBAQEBAQEBAQEBAQAAAAAAAQIDBAUGBwgJCgsQAAICAQMCAwQHBgMDBgIBNQEAAhEDIRIxBEFRIhNhcTKBkbFCoQXRwRTwUiNyM2LhgvFDNJKishXSUyRzwmMGg5Pi8qNEVGQlNUUWJnQ2VWWzhMPTdePzRpSkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2EQACAgAFAQYGAQMBAwUDBi8AARECIQMxQRJRYXGBkSITMvChsQTB0eHxQlIjYnIUkjOCQySisjRTRGNzwtKDk6NU4vIFFSUGFiY1ZEVVNnRls4TD03Xj80aUpIW0lcTU5PSltcXV5fVWZnaG/9oADAMBAAIRAxEAPwDipNOm1O10QgBoBsRaEWAik7XURaEWFMNq7Xo2J2KSwcpizT1GDmYqSGFIp2MWCGgzpW6VA6BBoQdQHQRcSagwGNrY9Ii1tZIg5drQi77EiCkQZCK7Xfau1klOYxczB7DFgxbIOMwczF7TBzMGySDk2q9GxVJINQ6B5hN0E3nJuDpDYeYTdBNzJqDamqcRNreySwaIY3oM1JILZLG9kzbIgosFBm5mbSQVSue9UDhGRsZFV0CxkbGRVclKGRr1FVhofUQciqsAScjJyKrcDJByMnIqtIT6iqrQf//Z">

Simple <picture>, no src

  <picture>
    <source data-srcset="/images/greenflash_400.jpg, /images/greenflash_800.jpg 2x" media="(min-width: 800px)">
    <source data-srcset="/images/greenflash_400.jpg, /images/greenflash_800.jpg 2x" media="(max-width: 799px)">
    <img>
  </picture>

Simple <picture>, src="/images/greenflash_100.jpg" (LQIP)

  <picture>
    <source data-srcset="/images/greenflash_400.jpg, /images/greenflash_800.jpg 2x" media="(min-width: 800px)">
    <source data-srcset="/images/greenflash_400.jpg, /images/greenflash_800.jpg 2x" media="(max-width: 799px)">
    <img src="/images/greenflash_100.jpg">
  </picture>

<picture> with inline LQIP

  <picture>
    <source data-srcset="/images/greenflash_400.jpg, /images/greenflash_800.jpg 2x" media="(min-width: 800px)">
    <source data-srcset="/images/greenflash_400.jpg, /images/greenflash_800.jpg 2x" media="(max-width: 799px)">
    <img src="data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAABAAD/4QN8aHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAxNCA3OS4xNTE0ODEsIDIwMTMvMDMvMTMtMTI6MDk6MTUgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6Y2QwNDkwYzktYTE1Yy00OTY2LTg2MWMtM2QwMWNjODk2MjhkIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkFDMjExNDdBQjRERDExRTY5M0NCRTVBNkJEODRGOTJCIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkFDMjExNDc5QjRERDExRTY5M0NCRTVBNkJEODRGOTJCIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDQyAoTWFjaW50b3NoKSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjcyMzI0NTQ5LWFlMGEtNDlmZi1iNjBkLTdhYjZjNzUzOTUxMCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpjZDA0OTBjOS1hMTVjLTQ5NjYtODYxYy0zZDAxY2M4OTYyOGQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAaGRknHCc+JSU+Qi8vL0JHPTs7PUdHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHARwnJzMmMz0mJj1HPTI9R0dHRERHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0dHR0f/wAARCAA4AGQDASIAAhEBAxEB/8QBGwAAAwEBAQEBAQEBAQAAAAAAAQACAwQFBgcICQoLAQEBAQEBAQEBAQEBAQAAAAAAAQIDBAUGBwgJCgsQAAICAQMCAwQHBgMDBgIBNQEAAhEDIRIxBEFRIhNhcTKBkbFCoQXRwRTwUiNyM2LhgvFDNJKishXSUyRzwmMGg5Pi8qNEVGQlNUUWJnQ2VWWzhMPTdePzRpSkhbSVxNTk9KW1xdXl9VZmdoaWprbG1ub2EQACAgAFAQYGAQMBAwUDBi8AARECIQMxQRJRYXGBkSITMvChsQTB0eHxQlIjYnIUkjOCQySisjRTRGNzwtKDk6NU4vIFFSUGFiY1ZEVVNnRls4TD03Xj80aUpIW0lcTU5PSltcXV5fVWZnaG/9oADAMBAAIRAxEAPwDipNOm1O10QgBoBsRaEWAik7XURaEWFMNq7Xo2J2KSwcpizT1GDmYqSGFIp2MWCGgzpW6VA6BBoQdQHQRcSagwGNrY9Ii1tZIg5drQi77EiCkQZCK7Xfau1klOYxczB7DFgxbIOMwczF7TBzMGySDk2q9GxVJINQ6B5hN0E3nJuDpDYeYTdBNzJqDamqcRNreySwaIY3oM1JILZLG9kzbIgosFBm5mbSQVSue9UDhGRsZFV0CxkbGRVclKGRr1FVhofUQciqsAScjJyKrcDJByMnIqtIT6iqrQf//Z">
  </picture>

iFrame

  <iframe data-src="https://www.youtube.com/embed/Wji-BZ0oCwg?list=PL148kCvXk8pDWYE9q9cNdux5JFmu80kN8" width="560" height="315" frameborder="0" allowfullscreen></iframe>

Dynamically added content