A modern lazy loading library for images.

Overview

Layzr.js

Layzr.js on NPM Layzr.js Downloads on NPM jsDelivr Hits Standard JavaScript Style

A modern lazy loading library for images.

Getting Started

Follow these steps:

  1. Install
  2. Setup Images
  3. Instantiate
  4. Review Options
  5. Review Events
  6. Review API
  7. Review Example Code
  • Examples progress like a coffee addiction: small -> medium -> large

Install

Layzr was developed with a modern JavaScript workflow in mind. To use it, it's recommended you have a build system in place that can transpile ES6, and bundle modules. For a minimal boilerplate that fulfills those requirements, check out outset.

Install Layzr, and add it to your package.json dependencies.

$ npm install layzr.js --save

Then import it into the file where you'll use it.

import Layzr from 'layzr.js'

Setup Images

Layzr intelligently chooses the best image source available based on an image's data attributes and browser feature detection.

  • In browsers that support srcset, if available, it will be used to determine the source.
  • In browsers that don't, the normal or retina source will be chosen based on the devicePixelRatio and availability.

Note that all attribute names are configurable via the options passed to Layzr. To indicate potential sources, add the following attributes to your images:

Name Required Optional
data-normal
data-retina
data-srcset

data-normal

Put the normal resolution source in the data-normal attribute.

<img data-normal="normal.jpg">

Note that Layzr selects elements using this attribute. Elements without it won't be tracked, and will never load.

data-retina

Add the retina/high resolution source in the data-retina attribute.

<img data-normal="normal.jpg" data-retina="retina.jpg">

data-srcset

Add the source set in the data-srcset attribute. For information on the proper syntax, read the official specification.

<img data-normal="normal.jpg" data-retina="retina.jpg" data-srcset="small.jpg 320w, medium.jpg 768w, large.jpg 1024w">

Instantiate

Create an instance, optionally passing in your options.

Be sure to assign your Layzr instance to a variable. Using your instance, you can:

  • start and stop the event listeners
  • add and remove event handlers
  • accommodate dynamically added elements
// using the default options

const instance = Layzr()

// using custom options

const instance = Layzr({
  // ...
})

Options are explained in the following section.

Options

Default options are shown below, and an explanation of each follows:

const instance = Layzr({
  normal: 'data-normal',
  retina: 'data-retina',
  srcset: 'data-srcset',
  threshold: 0
})

normal

Customize the attribute the normal resolution source is taken from.

const instance = Layzr({
  normal: 'data-normal'
})

retina

Customize the attribute the retina/high resolution source is taken from.

const instance = Layzr({
  retina: 'data-retina'
})

srcset

Customize the attribute the source set is taken from.

const instance = Layzr({
  srcset: 'data-srcset'
})

threshold

Adjust when images load, relative to the viewport. Positive values make images load sooner, negative values make images load later.

Threshold is a percentage of the viewport height, identical to the CSS vh unit.

const instance = Layzr({
  threshold: 0
})

Events

Layzr instances are extended with Knot.js, a browser-based event emitter. Use the event emitter syntax to add and remove handlers. Review the emitter syntax here.

Layzr emits the following events:

src:before

This event is emitted immediately before an image source is set. The image node is passed to the event handler.

instance.on('src:before', (element) => {
  // 'this' is your Layzr instance
  // 'element' is the image node
  // ...
})

Load event handlers should be attached using this event. See the example, and note the caveats associated with image load events before proceeding.

src:after

This event is emitted immediately after an image source is set. The image node is passed to the event handler.

instance.on('src:after', (element) => {
  // 'this' is your Layzr instance
  // 'element' is the image node
  // ...
})

Note that the image is not necessarily done loading when this event fires.

API

All API methods are chainable, including those from the emitter.

.handlers(flag)

Add or remove the scroll and resize event handlers.

instance
  .handlers(true)       // 'true' adds them
  .handlers(false)      // 'false' removes them

.check()

Manually check if elements are in the viewport.

This method is called while the window is scrolled or resized.

instance.check()

.update()

Update the elements Layzr is checking.

instance.update()

Dynamically added elements should be handled using this method. See the example.

Browser Support

Layzr depends on the following browser APIs:

It supports the following natively:

  • Chrome 24+
  • Firefox 23+
  • Safari 6.1+
  • Opera 15+
  • Edge 12+
  • IE 10+
  • iOS Safari 7.1+
  • Android Browser 4.4+

To support older browsers, consider including polyfills/shims for the APIs listed above. There are no plans to include any in the library, in the interest of file size.

Colophon

License

MIT. © 2017 Michael Cavalea

Built With Love

Comments
  • Responsive images support

    Responsive images support

    Hi @callmecavs, are you planning to support responsive image attributes like srcset/sizes or the picture element? This would be a great addition in responsive websites.

    Thanks anyway for the great work you put into layzr.js!

    opened by zauni 20
  • Threshold not working as expected

    Threshold not working as expected

    1. The threshold doesn't work equally when scrolling up or down.
    2. Images that are already visible on the page on load don't show until scrolling starts

    I'm not sure how you intended people to set the threshold, but I found that if I want the image to load after part of the image is revealed, I have to set a ridiculously high negative integer (ie. -800).

    It works great as you scroll down, but if you are scrolling up (which can be achieved by scrolling down then refreshing the page) the image doesn't load until wayyyy later compared to scrolling down.

    In addition, I have images that are almost completely visible on page load, yet Layzr doesn't fire for them until I start scrolling.

    Ideally:

    1. any images that are already visible on the page (above the fold) should load when the script is initiated
    2. threshold should a) be more clearly defined (what values are we expected to use here?) and should work equally going up or down (ie. if it's 100px delay on the way down, it should only be 100px delay on the way up).
    opened by ndimatteo 17
  • ASYNC layzr.js

    ASYNC layzr.js

    Hi,

    Do you think it's possible to make layzr.js asynchronous?

    Something like :

    <script async src="js/layzr.min.js"></script>
    <script async src="js/instantiate.js"></script>
    

    It's not working for me and I think the error is with the initiation.

    document.addEventListener('DOMContentLoaded', event => {
    ...
    });
    

    What do you think?

    opened by ericvalois 11
  • Hidden images are loaded

    Hidden images are loaded

    Hey @callmecavs,

    It appears that images wrapped in an hidden container will still be loaded. Could you tell me in which context does it make sense? I think most people don't want to be happening, do they?

    You can see an example here: http://inquiry.n2clic.com/

    inquiry_n2clic_com

    If what I'm saying does make sense, perhaps you should check if the placeholder image is visible:

    //Where el is the DOM element you'd like to test for visibility
    function isHidden(el) {
        var style = window.getComputedStyle(el);
        return (style.display === 'none')
    }
    

    If it doesn't, please lemme know if there's a work around.

    Cheers

    roadmap 
    opened by siamkreative 11
  • DOMContentLoaded not firing when used in requirejs

    DOMContentLoaded not firing when used in requirejs

    document.addEventListener('DOMContentLoaded', this._create.bind(this), false); does not fire off when layzr is used with requirejs. Checking for readyState is complete seems to fix this:

        if (document.readyState === "complete") {
          this._create();
        } else {
          // call to create
          document.addEventListener('DOMContentLoaded', this._create.bind(this), false);
        }
    
    bug help wanted 
    opened by bmsterling 11
  • Thought about native respimg support?

    Thought about native respimg support?

    First: thanks for this, looks great!

    I was wondering, however, if you've considered supporting Responsive images through e.g. the srcset and sizes attributes? (For a nice explainer check: https://ericportis.com/posts/2014/srcset-sizes/)

    At the moment you 'force' retina img src by checking window.devicePixelRatio > 1. However, there's a lot of alternative use-cases for 'responsive' images and by forcing the src through JS you disable browser optimizations.

    I am not sure how this would work—if it would work at all (since I'm not sure we can set srcset and sizes dynamically)—but you might have some thoughts?

    wontfix 
    opened by davidhund 8
  • Suggestions

    Suggestions

    I like how this doesn't require any dependencies!! I also like how performant it is, along with the small file file size, great work! I have a few suggestions to improve performance, extend functionality, and (potentially) decrease file size.

    1. Remove elements from this._nodes after they have been revealed because you no longer need to check if they're in the viewport in update.

    2. Currently the _destroy method is not being used at all. This should run when this._nodes.length is 0. So you can remove the overhead of those events.

    3. Instead of defining this._nodes in the constructor I would move it to _create and just add an argument to _create(selector). If selector is undefined then just use the initial selector that was passed into the options. This way you can call Lazyr._create in some other function after the page loads and add other elements to this._nodes list. This would be useful if you ajaxed in images that weren't on the page when Lazyr was initially called. I would also update the name to be something else to be more semantic if you decide to implement it like this.

    4. In addition to #3 update document.querySelectorAll(this._optionsSelector) to be this._optionsContainer.querySelectorAll(selector || this._optionsSelector) so that you're only getting elements inside of the defined container.

    5. Remove this updateSelector because it's not being used and just adding weight to the file size.

    6. Remove getContainerHeight from _inViewport and just set this.containerHeight = this._optionsContainer.innerHeight || this._optionsContainer.offsetHeight inside of update outside of the loop. The reason for this is so that you don't have to get the container height for every node in this._nodes since it's highly unlikely that the container height will change before the update function is complete.

    7. Move the hidden attribute check into the update for loop. This way you don't have to waste time doing the calculations to check if it's visible if it's hidden.

      if(!node.hasAttribute(this._optionsAttrHidden) && this._inViewport(node)) {
      ...
      }
      

      Those are my 2 cents.

    roadmap 
    opened by tjbenton 7
  • Version number in min.js file

    Version number in min.js file

    I have no idea which version I installed last time. All I know is that bower found and downloaded version 1.2.2.

    It would be awesome if the version number could be inserted as a comment on the first line of the dist/layzr.min.js.

    Autocompeter does this using gulp-header.

    roadmap 
    opened by peterbe 7
  • Host it onto a CDN

    Host it onto a CDN

    As mentioned in the end of the readme, it would be great to have this hosted on a CDN. I suggest both CDNJS and JsDeliver:

    • https://github.com/cdnjs/cdnjs#adding-a-new-or-updating-an-existing-library
    • https://github.com/jsdelivr/jsdelivr#how-to-submit-or-update-projects

    Cheers

    roadmap 
    opened by siamkreative 7
  • Image loading firing prematurely // too fast

    Image loading firing prematurely // too fast

    I am using a Bootstrap Carousel trying to lazy load its images, and I noticed (in the network tab) that images are loaded very early. Now, I don't mind that much, as my main goal is the make initial rendering faster and cut down on http requests but I would like if I can control this a bit more.

    I am assuming something is wrongly calculated with the current scroll position / threshold? Maybe the fact that only 1 image is visible after the Carousel script is initialized?

    Here's the pen showing the problem/behavior: https://codepen.io/gkatsanos/pen/dWXNEj

    opened by gkatsanos 6
  • Data-Srcset deleting image references in Safari

    Data-Srcset deleting image references in Safari

    I'm having an issue with srcset in safari. Layzr loads and is executed but for images with data-srcset it wipes out all references to images and leaves the node blank.

    Browser Version : Safari Version 9.1.1 (11601.6.17), same happens on iOS Safari Latest version. Browser Console : Empty, when I console log the elements that the lazyr events are being fired on I get the following results

    https://www.dropbox.com/s/23r5e9nler8kspt/Screenshot%202016-06-10%2018.37.59.png?dl=0

    Relevant HTML/JS

    Source

    <figure class="content__image">
    <img data-normal="https://cloud.lovindublin.com/images/uploads/2016/06/_blogWideMobile/Screen-Shot-2016-06-09-at-12.29.45.jpg?mtime=20160609123013" data-srcset="https://cloud.lovindublin.com/images/uploads/2016/06/_blogWide/Screen-Shot-2016-06-09-at-12.29.45.jpg?mtime=20160609123013 870w" width="400" height="182" alt="Screen Shot 2016 06 09 At 12 29 45" />
    </figure>
    

    Expected outcome

    <figure class="content__image">
    <img src="https://cloud.lovindublin.com/images/uploads/2016/06/_blogWideMobile/Screen-Shot-2016-06-09-at-12.29.45.jpg?mtime=20160609123013" srcset="https://cloud.lovindublin.com/images/uploads/2016/06/_blogWide/Screen-Shot-2016-06-09-at-12.29.45.jpg?mtime=20160609123013 870w" width="400" height="182" alt="Screen Shot 2016 06 09 At 12 29 45" />
    </figure>
    

    Actual Outcome

    <figure class="content__image">
    <img width="400" height="182" alt="Screen Shot 2016 06 09 At 12 29 45" />
    </figure>
    

    https://www.dropbox.com/s/nxuquubek4a95d1/Screenshot%202016-06-10%2018.40.53.png?dl=0

    There are a few workarounds but none that are perfect.

    • Disable lazyr
    • Use data-normal on its own instead of data-srcset too
    • Use data-retina instead of data-srcset
    opened by seanogdev 6
  • Please update main readme for beginners

    Please update main readme for beginners

    Please place this in the front page readme, so beginners can spot it easy. This's useful piece of code to see when starting especially for beginner developer. Nice repo!

    @MarkOGDev per the Setup Images and Example Code sections of the docs, the simplest possible configuration would look as follows:

    <!DOCTYPE html>
    
    <html>
      <head></head>
      <body>
        <!-- setup images, only the data-normal attribute is required -->
        <img data-normal="normal.jpg">
    
        <!-- optionally include data-retina and data-srcset attributes -->
        <img data-normal="normal.jpg"
             data-retina="retina.jpg"
             data-srcset="small.jpg 320w, medium.jpg 768w, large.jpg 1024w">
    
        <!-- load Layzr -->
        <script src="layzr.min.js"></script>
    
        <!-- start up Layzr -->
        <script>
          const instance = Layzr();
          document.addEventListener('DOMContentLoaded', function(event) {
            instance
              .update()           // track initial elements
              .check()            // check initial elements
              .handlers(true)     // bind scroll and resize handlers
          })
        </script>
      </body>
    </html>
    

    please don't hesitate to reach out with any more questions!

    Originally posted by @callmecavs in https://github.com/callmecavs/layzr.js/issues/56#issuecomment-174575197

    opened by sengiv 0
  • Problem with image hosted on contentful

    Problem with image hosted on contentful

    Hello, I try to lazy-load my images witch are hosted on contentful, I installed layzr, but when I run my application, all I have is my default image displayed, Do you know why ? In my html, I can see that the date-image is the right url but the attribute src is not any idea how can I fix that ? Damien.

    opened by DamienCompere 0
  • Thoughts on adding support for native lazy loading?

    Thoughts on adding support for native lazy loading?

    Some browsers now support a loading attribute on images and iframes. A recent article on Smashing details how lazy loading scripts can be modified to first check for native support for lazy loading before triggering JavaScript based solutions.

    What are your thoughts on adding a parameter (something like prefer_native) that would check if native lazy loading is supported, and only run Layzr if it's not supported?

    https://www.smashingmagazine.com/2019/05/hybrid-lazy-loading-progressive-migration-native/

    opened by JacobDB 0
  • IntersectionObserver API Support

    IntersectionObserver API Support

    opened by keithws 1
  • Stop loading img in display:none

    Stop loading img in display:none

    Hi, dear

    This PR stops loading img which is not displayed ( display:none ). The function getBoundingClientRect, which is used in inViewport can't take correct position when the node is in invisible element, can it? So I added a check in inViewport to know whether it's displayed. Please tell me if I did anything wrong. Thanks.

    best regards,

    opened by h2owl 1
  • Make scrolling other elements trigger lazy-loading

    Make scrolling other elements trigger lazy-loading

    This PR fixes a problem I currently have. Simply, I have a div on my page which is overflow: auto. Inside this div, I have a bunch of images that I want to lazy-load with Layzr. The issue is that Layzr's handlers method only operates on window. This means that only scrolling the window itself will trigger lazy-loading, and scrolling any other element won't. I propose the addition of handlersFor which will add the scroll handler for the given node.

    Note: I looked into detecting resizing of a div (though, it's not a problem I have). It seems that this is a popular solution, and very efficient. However, it would introduce a dependency for an edge case which is probably rare. Having said that, maybe we should expose requestFrame (perhaps under a different name) as a hook for adding other triggers?

    opened by zaccharles 6
Releases(v2.2.2)
Owner
Michael Cavalea
Frontend. 🔥 JavaScript.
Michael Cavalea
A modern lazy loading library for images.

Layzr.js A modern lazy loading library for images. Demo Page Getting Started Follow these steps: Install Setup Images Instantiate Review Options Revie

Michael Cavalea 5.6k Dec 25, 2022
Fancytree - JavaScript tree view / tree grid plugin with support for keyboard, inline editing, filtering, checkboxes, drag'n'drop, and lazy loading

Fancytree Fancytree (sequel of DynaTree 1.x) is a JavaScript tree view / tree grid plugin with support for keyboard, inline editing, filtering, checkb

Martin Wendt 2.6k Jan 9, 2023
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
High performance and SEO friendly lazy loader for images (responsive and normal), iframes and more, that detects any visibility changes triggered through user interaction, CSS or JavaScript without configuration.

lazysizes lazysizes is a fast (jank-free), SEO-friendly and self-initializing lazyloader for images (including responsive images picture/srcset), ifra

Alexander Farkas 16.6k Jan 1, 2023
BttrLazyLoading is a Jquery plugin that allows your web application to defer image loading until images are scrolled to but not only

BttrLazyLoading.js BttrLazyLoading is a Jquery plugin that allows your web application to defer image loading until images are scrolled to but not onl

Julien Renaux 410 Dec 14, 2022
A simple way of loading inline es-modules on modern browser.

ES inline module A simple way of loading inline es-modules on modern browser. Usage Use inlineImport to dynamically import inline scripts. <script typ

稀土 40 Dec 22, 2022
JSPro is nothing but JavaScript Prototypes! The publisher is too lazy to write full name that's why it's just JSPro.

JSPro is nothing but JavaScript Prototypes! The publisher is too lazy to write full name that's why it's just JSPro. Whatever, it's a library of hundreds of awesome JavaScript Prototypes (you may know it as dot function) for lazy programmers. Just install the package with a little effort and leave the blames for the publisher.

Jafran Hasan 2 Mar 10, 2022
A lazy plugin for printing local network when starting NodeJS server. I made this so you don't have to.

lazy-net A simple, lightweight plugin for printing local network when starting Node.js or Express.js server. I made this so you don't have to. There a

Trần Quang Kha (始) 1 Feb 10, 2022
Lazy evaluation list with high tree-shaking affinity and easy customization.

Lazy evaluation list with high tree-shaking affinity and easy customization. Features ?? Lazy Evaluation: The collections are only enumerated to the m

Masanori Onoue 22 Dec 28, 2022
A small (~600B gzip), useful set of methods for lazy iteration of iterables.

@ricokahler/lazy · A small (~600B gzip*), useful set of methods for lazy iteration of iterables. Why this lazy lib? Do I even need a lazy lib? Install

Rico Kahler 11 Sep 10, 2022
Program that helps you to be more lazy :)

IFL (I F#cking lazy) IFL - is a program build with electron & react. Main goal of this project is to help you be more productive with adding new songs

null 3 Aug 31, 2022
Lazy minting of ERC721 NFTs using EIP712 standard for typed, structured data. ✨

ERC721 - Signature minting Lazy minting of ERC721 NFTs using EIP712 standard for typed, structured data. ✨ How it works Lazy minting or Signature mint

Sunrit Jana 21 Oct 20, 2022
Simple lazy responsive starter kit for CraftCMS 4 Projects.

Lazy Craft CMS 4 Boilerplate Simple lazy responsive starter kit for CraftCMS 4 Projects. Requirements RTFM Craft CMS 4 Requirements Configs Duplicate

null 4 Sep 2, 2022
Finally, a simple, lightweight (0.6kb), pure JavaScript image lazy loader that even works in IE* using the IntersectionObserver API

Simply Lazy A simple & lightweight (0.6kb), pure JavaScript image lazy loader that even works in IE* utilizing the built in IntersectionObserver API i

Max 5 Dec 26, 2022
Compare James Webb Space Telescope images to older images.

How much more powerful is the James Webb Space Telescope when compared to Hubble? Find out! More info Want to help out? CONTRIBUTING.md Blog post with

John Christensen 399 Jan 3, 2023
Simple jQuery plugin that will allow users to zoom in your images, perfect for product images and galleries.

Image Zoom (jQuery) Plugin Simple jQuery plugin that will allow users to zoom in your images, perfect for product images and galleries that is less th

Mario Duarte 8 Aug 3, 2022
Easiest 1-click way to install and use Stable Diffusion on your own computer. Provides a browser UI for generating images from text prompts and images. Just enter your text prompt, and see the generated image.

Stable Diffusion UI Easiest way to install and use Stable Diffusion on your own computer. No dependencies or technical knowledge required. 1-click ins

null 3.5k Dec 30, 2022