Simple, no dependency, lazy loader
Switch branches/tags
Nothing to show
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
images Initial commit Dec 7, 2016
.gitattributes Added .gitattributes file Dec 7, 2016
LICENSE Initial commit Dec 7, 2016 Fixed really old browser support Apr 4, 2018
index.html Readme updates Mar 27, 2018
lazyload.js Fixed really old browser support Apr 4, 2018


v2.1.1 / 2018-04-01

Simple, tiny, no dependency, lazy loader. Demo

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.

It will also look for data-lazyload and swap that attribute for data-lazyloaded; again so you can hook up styles 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.

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):



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], [data-lazyload]', // maybe you just want images?
  rootMargin: '0px', // IntersectionObserver option
  threshold: 0, // IntersectionObserver option
  maxFrameCount: 10, // 60fps / 10 = 6 times a second

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.

Combating repaints

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;


If you find a bug in lazyload, please add it to the issue tracker or fork it, fix it and submit a pull request for it (👍).

Tabs are 2 spaces, functions are commented, variables are camel case and its preferred that its easier to read than outright file size being the smallest possible.


As of writing IntersectionObserver is supported in Edge, FireFox and Chrome: ([]

If thats not supported, Safari 11 for example, lazyLoad checks for document.querySelectorAll, ('addEventListener' in window), window.requestAnimationFrame and document.body.getBoundingClientRect.

If they're not supported then this script isn't going to do anything. For these browsers, really old ones, you might want to look at HTML4CSS and legacypicturefill instead.


  • ~7kb uncompressed
  • ~2kb minified
  • ~1kb minified and gzipped