Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

layzr.js

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

layzr.js - npm Package Compare versions

Comparing version 1.4.3 to 2.0.0

.babelrc

6

dist/layzr.min.js
/*!
* Layzr.js 1.4.2 - A small, fast, modern, and dependency-free library for lazy loading.
* Copyright (c) 2015 Michael Cavalea - http://callmecavs.github.io/layzr.js/
* Layzr.js 2.0.0 - A small, fast, and modern library for lazy loading images.
* Copyright (c) 2016 Michael Cavalea - http://callmecavs.github.io/layzr.js/
* License: MIT
*/
!function(t,i){"function"==typeof define&&define.amd?define([],i):"object"==typeof exports?module.exports=i():t.Layzr=i()}(this,function(){"use strict";function t(t){this._lastScroll=0,this._ticking=!1,t=t||{},this._optionsContainer=document.querySelector(t.container)||window,this._optionsSelector=t.selector||"[data-layzr]",this._optionsAttr=t.attr||"data-layzr",this._optionsAttrRetina=t.retinaAttr||"data-layzr-retina",this._optionsAttrBg=t.bgAttr||"data-layzr-bg",this._optionsAttrHidden=t.hiddenAttr||"data-layzr-hidden",this._optionsThreshold=t.threshold||0,this._optionsCallback=t.callback||null,this._retina=window.devicePixelRatio>1,this._srcAttr=this._retina?this._optionsAttrRetina:this._optionsAttr,this._nodes=document.querySelectorAll(this._optionsSelector),this._handlerBind=this._requestScroll.bind(this),this._create()}return t.prototype._requestScroll=function(){this._lastScroll=this._optionsContainer===window?window.pageYOffset:this._optionsContainer.scrollTop+this._getOffset(this._optionsContainer),this._requestTick()},t.prototype._requestTick=function(){this._ticking||(requestAnimationFrame(this.update.bind(this)),this._ticking=!0)},t.prototype._getOffset=function(t){return t.getBoundingClientRect().top+window.pageYOffset},t.prototype._getContainerHeight=function(){return this._optionsContainer.innerHeight||this._optionsContainer.offsetHeight},t.prototype._create=function(){this._handlerBind(),this._optionsContainer.addEventListener("scroll",this._handlerBind,!1),this._optionsContainer.addEventListener("resize",this._handlerBind,!1)},t.prototype._destroy=function(){this._optionsContainer.removeEventListener("scroll",this._handlerBind,!1),this._optionsContainer.removeEventListener("resize",this._handlerBind,!1)},t.prototype._inViewport=function(t){var i=this._lastScroll,e=i+this._getContainerHeight(),o=this._getOffset(t),n=o+this._getContainerHeight(),s=this._optionsThreshold/100*window.innerHeight;return n>=i-s&&e+s>=o&&!t.hasAttribute(this._optionsAttrHidden)},t.prototype._reveal=function(t){var i=t.getAttribute(this._srcAttr)||t.getAttribute(this._optionsAttr);t.hasAttribute(this._optionsAttrBg)?t.style.backgroundImage="url("+i+")":t.setAttribute("src",i),"function"==typeof this._optionsCallback&&this._optionsCallback.call(t),t.removeAttribute(this._optionsAttr),t.removeAttribute(this._optionsAttrRetina),t.removeAttribute(this._optionsAttrBg),t.removeAttribute(this._optionsAttrHidden)},t.prototype.updateSelector=function(){this._nodes=document.querySelectorAll(this._optionsSelector)},t.prototype.update=function(){for(var t=this._nodes.length,i=0;t>i;i++){var e=this._nodes[i];e.hasAttribute(this._optionsAttr)&&this._inViewport(e)&&this._reveal(e)}this._ticking=!1},t});
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;t="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,t.Layzr=e()}}(function(){var e;return function t(e,n,r){function o(u,f){if(!n[u]){if(!e[u]){var s="function"==typeof require&&require;if(!f&&s)return s(u,!0);if(i)return i(u,!0);var c=new Error("Cannot find module '"+u+"'");throw c.code="MODULE_NOT_FOUND",c}var a=n[u]={exports:{}};e[u][0].call(a.exports,function(t){var n=e[u][1][t];return o(n?n:t)},a,a.exports,t,e,n,r)}return n[u].exports}for(var i="function"==typeof require&&require,u=0;u<r.length;u++)o(r[u]);return o}({1:[function(t,n,r){(function(o){!function(t){if("object"==typeof r&&"undefined"!=typeof n)n.exports=t();else if("function"==typeof e&&e.amd)e([],t);else{var i;i="undefined"!=typeof window?window:"undefined"!=typeof o?o:"undefined"!=typeof self?self:this,i.Knot=t()}}(function(){return function e(n,r,o){function i(f,s){if(!r[f]){if(!n[f]){var c="function"==typeof t&&t;if(!s&&c)return c(f,!0);if(u)return u(f,!0);var a=new Error("Cannot find module '"+f+"'");throw a.code="MODULE_NOT_FOUND",a}var d=r[f]={exports:{}};n[f][0].call(d.exports,function(e){var t=n[f][1][e];return i(t?t:e)},d,d.exports,e,n,r,o)}return r[f].exports}for(var u="function"==typeof t&&t,f=0;f<o.length;f++)i(o[f]);return i}({1:[function(e,t,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n["default"]=function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return e.events={},e.on=function(t,n){return e.events[t]=e.events[t]||[],e.events[t].push(n),e},e.once=function(t,n){return n._once=!0,e.on(t,n),e},e.off=function(t,n){return 2===arguments.length?e.events[t].splice(e.events[t].indexOf(n),1):delete e.events[t],e},e.emit=function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),o=1;n>o;o++)r[o-1]=arguments[o];var i=e.events[t]&&e.events[t].slice();return i&&i.forEach(function(n){n._once&&e.off(t,n),n.apply(e,r)}),e},e},t.exports=n["default"]},{}]},{},[1])(1)})}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],2:[function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(n,"__esModule",{value:!0});var o=e("knot.js"),i=r(o);n["default"]=function(){function e(){a=window.scrollY||window.pageYOffset,t()}function t(){d||(requestAnimationFrame(function(){return f()}),d=!0)}function n(e){return e.getBoundingClientRect().top+a}function r(e){var t=a,r=t+v,o=n(e),i=o+e.offsetHeight,u=100*p.threshold/v;return i>=t-u&&r+u>=o}function o(e){if(w.emit("src:before",e),m&&e.hasAttribute(p.srcset))e.setAttribute("srcset",e.getAttribute(p.srcset));else{var t=h>1&&e.getAttribute(p.retina);e.setAttribute("src",t||e.getAttribute(p.normal))}w.emit("src:after",e),[p.normal,p.retina,p.srcset].forEach(function(t){return e.removeAttribute(t)}),s()}function u(t){var n=t?"addEventListener":"removeEventListener";return["scroll","resize"].forEach(function(t){return window[n](t,e)}),this}function f(){return v=window.innerHeight,l.forEach(function(e){return r(e)&&o(e)}),d=!1,this}function s(){return l=Array.prototype.slice.call(document.querySelectorAll("["+p.normal+"]")),this}var c=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],a=void 0,d=void 0,l=void 0,v=void 0,p={normal:c.normal||"data-normal",retina:c.retina||"data-retina",srcset:c.srcset||"data-srcset",threshold:c.threshold||0},m=document.body.classList.contains("srcset")||"srcset"in document.createElement("img"),h=window.devicePixelRatio||window.screen.deviceXDPI/window.screen.logicalXDPI,w=(0,i["default"])({handlers:u,check:f,update:s});return w},t.exports=n["default"]},{"knot.js":1}]},{},[2])(2)});
{
"name": "layzr.js",
"version": "1.4.3",
"description": "A small, fast, modern, and dependency-free library for lazy loading.",
"version": "2.0.0",
"description": "A small, fast, and modern library for lazy loading images.",
"homepage": "http://callmecavs.github.io/layzr.js/",
"main": "dist/layzr.js",
"main": "dist/layzr.min.js",

@@ -26,13 +26,23 @@ "author": "Michael Cavalea",

"dependencies": {
"knot.js": "^1.0.1"
},
"devDependencies": {
"gulp": "^3.8.10",
"gulp-connect": "^2.2.0",
"gulp-header": "^1.2.2",
"gulp-load-plugins": "^0.8.0",
"gulp-plumber": "^1.0.0",
"gulp-rename": "^1.2.2",
"gulp-uglify": "^1.0.2",
"gulp-umd": "^0.1.3",
"node-notifier": "^4.1.2"
"babel-core": "^6.4.0",
"babel-plugin-add-module-exports": "^0.1.2",
"babel-preset-es2015": "^6.3.13",
"babelify": "^7.2.0",
"browser-sync": "^2.11.0",
"browserify": "^13.0.0",
"gulp": "^3.9.0",
"gulp-header": "^1.7.1",
"gulp-sourcemaps": "^1.6.0",
"gulp-uglify": "^1.5.1",
"lodash.assign": "^4.0.0",
"node-notifier": "^4.4.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.7.0"
}
}
# Layzr.js
[![Layzr.js on NPM](https://img.shields.io/npm/v/layzr.js.svg)](https://www.npmjs.com/package/layzr.js) [![Layzr.js on Bower](https://img.shields.io/bower/v/layzr.js.svg)](http://bower.io/search/?q=layzr.js) [![Layzr.js on Gitter](https://img.shields.io/badge/gitter-join%20chat-brightgreen.svg)](https://gitter.im/callmecavs/layzr.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Layzr.js on NPM](https://img.shields.io/npm/v/layzr.js.svg)](https://www.npmjs.com/package/layzr.js) [![Layzr.js on Gitter](https://img.shields.io/badge/gitter-join%20chat-green.svg)](https://gitter.im/callmecavs/layzr.js)
A small, fast, modern, and dependency-free library for lazy loading.
A small, fast, and modern library for lazy loading images.
Lazy loading boosts page speed by deferring the loading of images until they're in (or near) the viewport. This library makes it completely painless - maximizing speed by keeping options to a minimum.
* [Demo Page](http://callmecavs.github.io/layzr.js/)
## Usage
## Getting Started
Follow these steps:
1. [Install](https://github.com/callmecavs/layzr.js#install)
2. [Image Setup](https://github.com/callmecavs/layzr.js#image-setup)
3. [Instance Creation](https://github.com/callmecavs/layzr.js#instance-creation)
1. [Install](#install)
2. [Setup Images](#setup-images)
3. [Instantiate](#instantiate)
4. [Review Options](#options)
5. [Review Events](#events)
6. [Review API](#api)
7. **[Review Example Code](https://github.com/callmecavs/layzr.js/tree/master/examples)**
* Examples progress like a coffee addiction: small -> medium -> large
### Install
## Install
Load the script.
Layzr was developed with a modern JavaScript workflow in mind. Though not required, it's convenient to have a build system in place that can transpile ES6, and bundle modules. For a minimal boilerplate that does so, try [outset](https://github.com/callmecavs/outset).
[Download it](https://github.com/callmecavs/layzr.js/archive/master.zip), install it with [NPM](https://www.npmjs.com/package/layzr.js), or install it with [Bower](http://bower.io/search/?q=layzr.js).
Choose an installation option based on your workflow:
```html
<script src="layzr.js"></script>
* [npm](#npm)
* [CDN](#cdn)
* [Download](#download)
Refer to the [releases](https://github.com/callmecavs/layzr.js/releases) page for version specific information.
### npm
Install Layzr, and add it to your `package.json` dependencies.
```
$ npm install layzr.js --save
```
#### CDN
Then `import` it into the file where you'll use it.
The script is also available via CDN.
```es6
import Layzr from 'layzr.js'
```
In the examples below, replace `{version}` with your desired version. Refer to the [releases](https://github.com/callmecavs/layzr.js/releases) page for version info.
### CDN
##### [cdnjs](https://cdnjs.com/libraries/layzr.js)
Copy and paste one of the following `<script>` tags. Note the version number in the `src` attributes.
#### [jsDelivr](http://www.jsdelivr.com/projects/layzr.js)
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/layzr.js/{version}/layzr.min.js"></script>
<script src="https://cdn.jsdelivr.net/layzr.js/2.0.0/layzr.min.js"></script>
```
##### [jsDelivr](http://www.jsdelivr.com/#!layzr.js)
#### [cdnjs](https://cdnjs.com/libraries/layzr.js)
```html
<script src="https://cdn.jsdelivr.net/layzr.js/{version}/layzr.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/layzr.js/2.0.0/layzr.min.js"></script>
```
### Image Setup
### Download
For each `img` and/or `iframe` you want to lazy load, put the `src` in the `data-layzr` attribute.
[Download](https://github.com/callmecavs/layzr.js/archive/master.zip) the latest version, and load the script in the `dist` folder.
```html
<img data-layzr="image/source">
<iframe data-layzr="media/source"></iframe>
<script src="layzr.min.js"></script>
```
This is the only _required_ attribute. Advanced, _optional_ configuration follows:
## Setup Images
#### (Optional) Placeholders
Layzr intelligently chooses the best image source available **based on an image's data attributes and browser feature detection**.
Include a placeholder, via the `src` attribute.
* In browsers that [support `srcset`](http://caniuse.com/#search=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](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) and availability.
Images without a placeholder - _before_ they're loaded - may impact layout (no width/height), or appear broken.
Note that all attribute names are configureable via the [options](#options) passed to Layzr. To indicate potential sources, add the following attributes to your images:
| Name | Required | Optional |
| :----------------------------- | :------: | :------: |
| [`data-normal`](#data-normal) | ✓ | |
| [`data-retina`](#data-retina) | | ✓ |
| [`data-srcset`](#data-srcset) | | ✓ |
### data-normal
Put the _normal resolution_ source in the `data-normal` attribute.
```html
<img src="optional/placeholder" data-layzr="image/source">
<img data-normal="normal.jpg">
```
#### (Optional) Retina Support
Note that Layzr **selects elements using this attribute**. Elements without it won't be tracked, and will never load.
Include a retina (high-resolution) version of the image in the `data-layzr-retina` attribute. This source will only be loaded if the [devicePixelRatio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio) is greater than 1.
### data-retina
Ensure the proper CSS is in place to display both regular and retina images correctly. This library handles the loading, but not the displaying, of elements.
Add the _retina/high resolution_ source in the `data-retina` attribute.
```html
<img data-layzr="image/source" data-layzr-retina="optional/retina/source">
<img data-normal="normal.jpg" data-retina="retina.jpg">
```
#### (Optional) Background Images
### data-srcset
Include the `data-layzr-bg` attribute to load the source as a background image.
Add the _source set_ in the `data-srcset` attribute. For information on the proper syntax, read the official [specification](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img).
The `data-layzr-bg` attribute should be valueless - the image source is still taken from the `data-layzr` and `data-layzr-retina` attributes.
```html
<img data-layzr="image/source" data-layzr-bg>
<img data-normal="normal.jpg" data-retina="retina.jpg" data-srcset="small.jpg 320w, medium.jpg 768w, large.jpg 1024w">
```
#### (Optional) Hidden Images
## Instantiate
Include the `data-layzr-hidden` attribute to prevent an image from loading.
Create an instance, optionally passing in your [options](#options).
Removing this attribute _will not load the image_ - the user will still need to scroll, and the element will still need to be in the viewport.
Be sure to **assign your Layzr instance to a variable**. Using your instance, you can:
```html
<img data-layzr="image/source" data-layzr-hidden>
```
* start and stop the event listeners
* add and remove event handlers
* accommodate dynamically added elements
### Instance Creation
```es6
// using the default options
Create a new instance, and that's it!
const instance = Layzr()
```javascript
var layzr = new Layzr();
// using custom options
const instance = Layzr({
// ...
})
```
Documentation for all options follows:
Options are explained in the following section.
## Options
Defaults shown below:
Default options are shown below, and an explanation of each follows:
```javascript
var layzr = new Layzr({
container: null,
selector: '[data-layzr]',
attr: 'data-layzr',
retinaAttr: 'data-layzr-retina',
bgAttr: 'data-layzr-bg',
hiddenAttr: 'data-layzr-hidden',
threshold: 0,
callback: null
});
```es6
const instance = Layzr({
normal: 'data-normal',
retina: 'data-retina',
srcset: 'data-srcset',
threshold: 0
})
```
Explanation of each follows:
### normal
### container
Customize the attribute the normal resolution source is taken from.
Customize the container that holds the elements to lazy load - using CSS selector syntax. This option may be useful when building single page applications.
```es6
const instance = Layzr({
normal: 'data-normal'
})
```
Note that `window` is assumed to be the container if this option is set to `null`.
### retina
```javascript
var layzr = new Layzr({
container: null
});
Customize the attribute the retina/high resolution source is taken from.
```es6
const instance = Layzr({
retina: 'data-retina'
})
```
### selector
### srcset
Customize the selector used to find elements to lazy load - using CSS selector syntax.
Customize the attribute the source set is taken from.
```javascript
var layzr = new Layzr({
selector: '[data-layzr]'
});
```es6
const instance = Layzr({
srcset: 'data-srcset'
})
```
### attr / retinaAttr
### threshold
Customize the data attributes that image sources are taken from.
Adjust when images load, relative to the viewport. _Positive values make images load sooner, negative values make images load later_.
```javascript
var layzr = new Layzr({
attr: 'data-layzr',
retinaAttr: 'data-layzr-retina'
});
Threshold is a percentage of the viewport height, identical to the CSS `vh` unit.
```es6
const instance = Layzr({
threshold: 0
})
```
### bgAttr
## Events
Customize the data attribute that loads images as a background.
Layzr instances are extended with [Knot.js](https://github.com/callmecavs/knot.js), a browser-based event emitter. Use the event emitter syntax to add and remove handlers. Review the emitter syntax [here](https://github.com/callmecavs/knot.js#api).
```javascript
var layzr = new Layzr({
bgAttr: 'data-layzr-bg'
});
Layzr emits the following events:
* [src:before](#srcbefore)
* [src:after](#srcafter)
### src:before
This event is emitted immediately _before an image source is set_. The image node is passed to the event handler.
```es6
instance.on('src:before', (element) => {
// 'this' is your Layzr instance
// 'element' is the image node
// ...
})
```
### hiddenAttr
Load event handlers should be attached using this event. See the [example](https://github.com/callmecavs/layzr.js/blob/v2.0.0/examples/large.js), and note the [caveats](https://api.jquery.com/load-event/) associated with image load events before proceeding.
Customize the data attribute that prevents images from loading.
### src:after
```javascript
var layzr = new Layzr({
hiddenAttr: 'data-layzr-hidden'
});
This event is emitted immediately _after an image source is set_. The image node is passed to the event handler.
```es6
instance.on('src:after', (element) => {
// 'this' is your Layzr instance
// 'element' is the image node
// ...
})
```
### threshold
Note that the image is not necessarily done loading when this event fires.
Adjust when images load, relative to the viewport. Positive values make elements load _sooner_.
## API
Threshold is a percentage of the viewport height - think of it as similar to the CSS `vh` unit.
All API methods are **chainable**, including those from the emitter.
```javascript
// load images when they're half the viewport height away from the bottom of the viewport
### .handlers(flag)
var layzr = new Layzr({
threshold: 50
});
Add or remove the `scroll` and `resize` event handlers.
```es6
instance
.handlers(true) // 'true' adds them
.handlers(false) // 'false' removes them
```
### callback
### .check()
Invoke a callback function each time an image is loaded.
Manually check if elements are in the viewport.
The image _may not be fully loaded before the function is called_. Detecting image load is inconsistent at best in modern browsers.
This method is called while the `window` is scrolled or resized.
```javascript
// in the callback function, "this" refers to the image node
```es6
instance.check()
```
var layzr = new Layzr({
callback: function() {
this.classList.add('class');
}
});
### .update()
Update the elements Layzr is checking.
```es6
instance.update()
```
**Dynamically added elements** should be handled using this method. See the [example](https://github.com/callmecavs/layzr.js/blob/v2.0.0/examples/large.js).
## Browser Support
Layzr natively supports **IE10+**.
Layzr depends on the following browser APIs:
To add support for older browsers, consider including [Paul Irish's polyfill](https://gist.github.com/paulirish/1579671) for [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).
* [classList](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList)
* ES5 array methods: [forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach), [slice](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)
* [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
There are currently no plans to include the polyfill in the library, in the interest of file size.
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](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills) for the APIs listed above. There are **no plans to include any in the library**, in the interest of file size.
## Colophon

@@ -220,15 +288,7 @@

* Stock Photos: [Unsplash](https://unsplash.com/)
* Heading Links: [heading-links.js](https://github.com/callmecavs/heading-links.js)
* Google Analytics Isogram: [isogrammer](http://isogrammer.com/)
* Inspiration: [Headroom.js](http://wicky.nillia.ms/headroom.js/), [jQuery Unveil](http://luis-almeida.github.io/unveil/)
* Education: [Paul Lewis](http://www.html5rocks.com/en/tutorials/speed/animations/)
## License
MIT. © 2015 Michael Cavalea
MIT. © 2016 Michael Cavalea
## Roadmap
- [x] Add to CDN?
[![Built With Love](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com)

@@ -1,157 +0,121 @@

'use strict';
import knot from 'knot.js'
// CONSTRUCTOR
export default (options = {}) => {
// private
function Layzr(options) {
// debounce
this._lastScroll = 0;
this._ticking = false;
let prevLoc
let ticking
let nodes
let windowHeight
// options
options = options || {};
this._optionsContainer = document.querySelector(options.container) || window;
this._optionsSelector = options.selector || '[data-layzr]';
this._optionsAttr = options.attr || 'data-layzr';
this._optionsAttrRetina = options.retinaAttr || 'data-layzr-retina';
this._optionsAttrBg = options.bgAttr || 'data-layzr-bg';
this._optionsAttrHidden = options.hiddenAttr || 'data-layzr-hidden';
this._optionsThreshold = options.threshold || 0;
this._optionsCallback = options.callback || null;
const settings = {
normal: options.normal || 'data-normal',
retina: options.retina || 'data-retina',
srcset: options.srcset || 'data-srcset',
threshold: options.threshold || 0
}
// properties
this._retina = window.devicePixelRatio > 1;
this._srcAttr = this._retina ? this._optionsAttrRetina : this._optionsAttr;
// feature detection
// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/img/srcset.js
// nodelist
this._nodes = document.querySelectorAll(this._optionsSelector);
const srcset = document.body.classList.contains('srcset') || 'srcset' in document.createElement('img')
// scroll and resize handler
this._handlerBind = this._requestScroll.bind(this);
// device pixel ratio
// not supported in IE10 - https://msdn.microsoft.com/en-us/library/dn265030(v=vs.85).aspx
// call to create
this._create();
}
const dpr = window.devicePixelRatio || window.screen.deviceXDPI / window.screen.logicalXDPI
// DEBOUNCE HELPERS
// adapted from: http://www.html5rocks.com/en/tutorials/speed/animations/
// instance
Layzr.prototype._requestScroll = function() {
if(this._optionsContainer === window) {
this._lastScroll = window.pageYOffset;
}
else {
this._lastScroll = this._optionsContainer.scrollTop + this._getOffset(this._optionsContainer);
}
const instance = knot({
handlers: handlers,
check: check,
update: update
})
this._requestTick();
};
return instance
Layzr.prototype._requestTick = function() {
if(!this._ticking) {
requestAnimationFrame(this.update.bind(this));
this._ticking = true;
// debounce helpers
function requestScroll() {
prevLoc = window.scrollY || window.pageYOffset
requestFrame()
}
};
// OFFSET HELPER
// remember, getBoundingClientRect is relative to the viewport
function requestFrame() {
if(!ticking) {
requestAnimationFrame(() => check())
ticking = true
}
}
Layzr.prototype._getOffset = function(node) {
return node.getBoundingClientRect().top + window.pageYOffset;
};
// offset helper
// HEIGHT HELPER
function getOffset(node) {
return node.getBoundingClientRect().top + prevLoc
}
Layzr.prototype._getContainerHeight = function() {
return this._optionsContainer.innerHeight
|| this._optionsContainer.offsetHeight;
}
// in viewport helper
// LAYZR METHODS
function inViewport(node) {
const viewTop = prevLoc
const viewBot = viewTop + windowHeight
Layzr.prototype._create = function() {
// fire scroll event once
this._handlerBind();
const nodeTop = getOffset(node)
const nodeBot = nodeTop + node.offsetHeight
// bind scroll and resize event
this._optionsContainer.addEventListener('scroll', this._handlerBind, false);
this._optionsContainer.addEventListener('resize', this._handlerBind, false);
};
const offset = (settings.threshold * 100) / windowHeight
Layzr.prototype._destroy = function() {
// unbind scroll and resize event
this._optionsContainer.removeEventListener('scroll', this._handlerBind, false);
this._optionsContainer.removeEventListener('resize', this._handlerBind, false);
};
return nodeBot >= viewTop - offset
&& nodeTop <= viewBot + offset
}
Layzr.prototype._inViewport = function(node) {
// get viewport top and bottom offset
var viewportTop = this._lastScroll;
var viewportBottom = viewportTop + this._getContainerHeight();
// source helper
// get node top and bottom offset
var nodeTop = this._getOffset(node);
var nodeBottom = nodeTop + this._getContainerHeight();
function setSource(node) {
instance.emit('src:before', node)
// calculate threshold, convert percentage to pixel value
var threshold = (this._optionsThreshold / 100) * window.innerHeight;
// prefer srcset, fallback to pixel density
if(srcset && node.hasAttribute(settings.srcset)) {
node.setAttribute('srcset', node.getAttribute(settings.srcset))
}
else {
const retina = dpr > 1 && node.getAttribute(settings.retina)
node.setAttribute('src', retina || node.getAttribute(settings.normal))
}
// return if node in viewport
return nodeBottom >= viewportTop - threshold
&& nodeTop <= viewportBottom + threshold
&& !node.hasAttribute(this._optionsAttrHidden);
};
instance.emit('src:after', node)
Layzr.prototype._reveal = function(node) {
// get node source
var source = node.getAttribute(this._srcAttr) || node.getAttribute(this._optionsAttr);
;[settings.normal, settings.retina, settings.srcset].forEach(attr => node.removeAttribute(attr))
// set node src or bg image
if(node.hasAttribute(this._optionsAttrBg)) {
node.style.backgroundImage = 'url(' + source + ')';
update()
}
else {
node.setAttribute('src', source);
}
// call the callback
if(typeof this._optionsCallback === 'function') {
// "this" will be the node in the callback
this._optionsCallback.call(node);
}
// API
// remove node data attributes
node.removeAttribute(this._optionsAttr);
node.removeAttribute(this._optionsAttrRetina);
node.removeAttribute(this._optionsAttrBg);
node.removeAttribute(this._optionsAttrHidden);
};
function handlers(flag) {
const action = flag
? 'addEventListener'
: 'removeEventListener'
Layzr.prototype.updateSelector = function() {
// update cached list of nodes matching selector
this._nodes = document.querySelectorAll(this._optionsSelector);
};
;['scroll', 'resize'].forEach(event => window[action](event, requestScroll))
return this
}
Layzr.prototype.update = function() {
// cache nodelist length
var nodesLength = this._nodes.length;
function check() {
windowHeight = window.innerHeight
// loop through nodes
for(var i = 0; i < nodesLength; i++) {
// cache node
var node = this._nodes[i];
nodes.forEach(node => inViewport(node) && setSource(node))
// check if node has mandatory attribute
if(node.hasAttribute(this._optionsAttr)) {
// check if node in viewport
if(this._inViewport(node)) {
// reveal node
this._reveal(node);
}
}
ticking = false
return this
}
// allow for more animation frames
this._ticking = false;
};
function update() {
nodes = Array.prototype.slice.call(document.querySelectorAll(`[${ settings.normal }]`))
return this
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc