ab-interchange
Advanced tools
Comparing version 1.3.1 to 2.0.0
@@ -9,28 +9,19 @@ ;(function(name, definition) { | ||
} | ||
}('abInterchange', function() { | ||
}('interchange', function() { | ||
'use strict'; | ||
function extend(){ | ||
for (var i = 1, len = arguments.length; i < len; i++) { | ||
if (!arguments[i]) continue; | ||
var pluginName = 'interchange', | ||
attr = 'data-ab-interchange', | ||
attrSrc = 'data-ab-interchange-src'; | ||
for (var key in arguments[i]) { | ||
if (!arguments[i].hasOwnProperty(key)) continue; | ||
arguments[0][key] = arguments[i][key]; | ||
} | ||
} | ||
return arguments[0]; | ||
} | ||
var Plugin = function(el, options) { | ||
this.el = el; | ||
var Interchange = function(element, opt) { | ||
if (!(this instanceof Interchange)) return new Interchange(element, opt); | ||
var dataOptions = AB.isJson(this.el.getAttribute(attr)) ? JSON.parse(this.el.getAttribute(attr)) : {}; | ||
this.settings = AB.extend(Plugin.defaults, options, dataOptions); | ||
this.settings = extend({}, Interchange.defaults, opt); | ||
this.element = element; | ||
this.$element = $(element); | ||
this.rules = []; | ||
this.currentPath = ''; | ||
this.defaultPath = ''; | ||
this.mode = 'img'; | ||
this.rules = []; | ||
this.currentPath = ''; | ||
this.mode = this._defineMode(); | ||
@@ -40,12 +31,13 @@ this.preInit(); | ||
Interchange.defaults = { | ||
lazy : true, | ||
delay : 100, | ||
offscreen : 1.5 | ||
Plugin.defaults = { | ||
mode : 'background', | ||
lazy : true, | ||
delay : 100, | ||
offscreen : 1.5 | ||
}; | ||
Interchange.prototype = { | ||
Plugin.prototype = { | ||
preInit: function() { | ||
// no need for a plugin in case of 'picture' with good support | ||
if (this.$element.closest('picture').length && window.HTMLPictureElement) | ||
if (this.el.parentNode.matches('picture') && window.HTMLPictureElement) | ||
return this; | ||
@@ -59,3 +51,2 @@ | ||
._generateRules() | ||
._setDefault() | ||
._updatePath(); | ||
@@ -67,12 +58,7 @@ | ||
_defineMode: function() { | ||
// images | ||
if (this.element.nodeName === 'IMG') | ||
// in case of <img /> there is no doubt | ||
if (this.el.nodeName === 'IMG') | ||
return 'img'; | ||
// background images | ||
if (this.currentPath.match(/\.(gif|jpg|jpeg|tiff|png)([?#].*)?/i) || this.currentPath === 'empty.bg') | ||
return 'bg'; | ||
// HTML | ||
return 'ajax'; | ||
return this.settings.mode; | ||
}, | ||
@@ -82,10 +68,4 @@ | ||
var rulesList = [], | ||
rules; | ||
rules = this.el.getAttribute(attrSrc).match(/\[[^\]]+\]/g); | ||
if (this.settings.rules) { | ||
rules = this.settings.rules; | ||
} else { | ||
rules = this.$element.data('ab-interchange').match(/\[[^\]]+\]/g); | ||
} | ||
for (var i = 0, len = rules.length; i < len; i++) { | ||
@@ -107,20 +87,2 @@ var rule = rules[i].slice(1, -1).split(', '), | ||
_setDefault: function() { | ||
var path = '', | ||
rules = this.rules, | ||
rule; | ||
// Iterate through each rule | ||
for (var i = 0, len = rules.length; i < len; i++) { | ||
rule = rules[i]; | ||
// check if default value is provided | ||
if (rule.query === 'default' && this.defaultPath === '') { | ||
this.defaultPath = rule.path; | ||
} | ||
} | ||
return this; | ||
}, | ||
_updatePath: function() { | ||
@@ -130,3 +92,3 @@ var match = false, | ||
rules = this.rules, | ||
currentQuery = AB.mediaQuery.current, | ||
//currentQuery = AB.mediaQuery.current, | ||
rule; | ||
@@ -138,3 +100,3 @@ | ||
if (window.matchMedia(AB.mediaQuery.get(rule.query)).matches) { | ||
if (AB.mediaQuery.is(rule.query)) { | ||
path = rule.path; | ||
@@ -145,5 +107,5 @@ match = true; | ||
this.currentPath = (path === '') ? this.defaultPath : path; | ||
this.currentPath = path; | ||
this._replace(); | ||
this._replace(); | ||
return this; | ||
@@ -153,4 +115,3 @@ }, | ||
_onScroll: function() { | ||
if (this._inView()) | ||
this._replace(); | ||
if (this._inView()) this._replace(); | ||
@@ -180,11 +141,12 @@ return this; | ||
_inView: function() { | ||
var scrollTop = $(window).scrollTop(), | ||
var scrollTop = window.scrollY, | ||
windowHeight = window.innerHeight; | ||
return this.element.getBoundingClientRect().top + scrollTop <= scrollTop + windowHeight * this.settings.offscreen; | ||
return this.el.getBoundingClientRect().top + scrollTop <= scrollTop + windowHeight * this.settings.offscreen; | ||
}, | ||
_replace: function() { | ||
var that = this, | ||
path = that.currentPath, | ||
trigger = 'replaced.ab-interchange'; | ||
var that = this, | ||
path = that.currentPath, | ||
eventName = 'replaced.ab-interchange'; | ||
@@ -196,3 +158,7 @@ that.mode = that._defineMode(); | ||
if (that.mode === 'img') { | ||
that.$element.attr('src', path).load().trigger(trigger); | ||
that.el.src = path; | ||
var event = new CustomEvent(eventName); | ||
window.dispatchEvent(event); | ||
return that; | ||
@@ -202,8 +168,13 @@ } | ||
// background images | ||
if (that.mode === 'bg') { | ||
if (path === 'empty.bg') { | ||
that.$element.css({ 'background-image': 'none' }).trigger(trigger); | ||
} else { | ||
that.$element.css({ 'background-image': 'url('+path+')' }).trigger(trigger); | ||
} | ||
if (that.mode === 'background') { | ||
if (path) | ||
path = 'url('+path+')'; | ||
else | ||
path = 'none'; | ||
that.el.style.backgroundImage = path; | ||
var event = new CustomEvent(eventName); | ||
window.dispatchEvent(event); | ||
return that; | ||
@@ -213,10 +184,29 @@ } | ||
// HTML | ||
if (path === 'empty.ajax') { | ||
that.$element.empty(); | ||
} else { | ||
$.get(path, function(response) { | ||
that.$element.html(response).trigger(trigger); | ||
}); | ||
var request = new XMLHttpRequest(); | ||
if (!path) { | ||
that.el.innerHTML = ''; | ||
return that; | ||
} | ||
request.open('GET', path, true); | ||
request.onload = function() { | ||
if (this.status >= 200 && this.status < 400) { | ||
var resp = this.response; | ||
that.el.innerHTML = resp; | ||
var event = new CustomEvent(eventName); | ||
window.dispatchEvent(event); | ||
} else { | ||
that.el.innerHTML = ''; | ||
} | ||
}; | ||
request.onerror = function() { | ||
that.el.innerHTML = ''; | ||
}; | ||
request.send(); | ||
return that; | ||
@@ -227,12 +217,11 @@ } | ||
function abInterchange(options){ | ||
var elements = document.querySelectorAll('[data-ab-interchange]'); | ||
AB[pluginName] = function(options) { | ||
var elements = document.querySelectorAll('['+ attr +']'); | ||
for (var i = 0, len = elements.length; i < len; i++) { | ||
if (elements[i].abInterchange) continue; | ||
elements[i].abInterchange = new Interchange(elements[i], options); | ||
if (elements[i][pluginName]) continue; | ||
elements[i][pluginName] = new Plugin(elements[i], options); | ||
} | ||
} | ||
}; | ||
return abInterchange; | ||
return AB; | ||
})); |
{ | ||
"name": "ab-interchange", | ||
"version": "1.3.1", | ||
"version": "2.0.0", | ||
"description": "AB-interchange: While responsive image loading is not really an easy task even today, here is a solution to manage conditional (based on breakpoints) loading of img, background-image or even HTML content.", | ||
@@ -10,2 +10,6 @@ "main": "ab-interchange.js", | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/lordfpx/AB-interchange/issues" | ||
}, | ||
"homepage": "https://github.com/lordfpx/AB-interchange#readme", | ||
"scripts": { | ||
@@ -22,9 +26,12 @@ "test": "echo \"Error: no test specified\" && exit 1" | ||
"responsive", | ||
"AB-mediaQuery" | ||
"AB-mediaQuery", | ||
"another-brick", | ||
"AB" | ||
], | ||
"author": "Thierry Philippe", | ||
"author": "Thierry Philippe (@lordfpx)", | ||
"license": "ISC", | ||
"dependencies": { | ||
"ab-mediaquery": "^1.4.4" | ||
"ab-mediaquery": "^2.1.5", | ||
"another-brick": "^1.0.1" | ||
} | ||
} |
142
README.md
# AB-interchange | ||
Responsive image loading is not an easy task even nowadays. Here is a solution to manage conditional (based on breakpoints) loading of: | ||
- **img** (even **picture** on unsupported browsers) | ||
AB-interchange is a pure JavaScript file that makes possible to conditionnaly load: | ||
- **img** (it can be also used as **picture** polyfill on unsupported browsers) | ||
- **background-image** | ||
- **HTML content** | ||
- **HTML content** (Ajax) | ||
That plugin also has an lazy-loading option! | ||
That plugin also has an **lazy-loading** option! | ||
It's inspired by [Zurb Foundation](https://github.com/zurb/foundation-sites). | ||
- [NPM](https://www.npmjs.com/package/ab-interchange) | ||
Here is a demo page: [Codepen](http://codepen.io/lordfpx/pen/yJbwrK) | ||
``` | ||
> npm install ab-mediaquery | ||
``` | ||
or | ||
``` | ||
> yarn add ab-mediaquery | ||
``` | ||
NPM package: https://www.npmjs.com/package/ab-interchange | ||
The plugin is **CommonJS** and **AMD** compliant (UMD). | ||
> npm install ab-interchange | ||
It's used on French website [ENGIE](https://particuliers.engie.fr/). | ||
The plugin is **CommonJS** and **AMD** compliant. | ||
--- | ||
## Dependencies: | ||
- [jQuery](https://jquery.com/) | ||
- [AB-mediaQueries](https://www.npmjs.com/package/ab-mediaquery) | ||
- [AB (another-brick)](https://www.npmjs.com/package/ab-mediaquery) | ||
- [AB-mediaQueries v2](https://www.npmjs.com/package/ab-mediaquery) | ||
You can either loads those scripts or import them (with browserify or webpack for ex.): | ||
``` | ||
import AB from 'another-brick'; | ||
import abMediaQuery from 'ab-mediaquery'; | ||
``` | ||
--- | ||
## Compatibility | ||
Because of the usage of `matchMedia`, compatibility start from IE 10. To rise compatibility up to IE 9, you must add https://github.com/paulirish/matchMedia.js/ polyfill. | ||
Because of the usage of `matchMedia`, compatibility start from IE 10. To rise compatibility up to IE 9, you must add this [polyfill](https://github.com/paulirish/matchMedia.js/). | ||
--- | ||
## SETUP | ||
You will need [jQuery](https://jquery.com/). | ||
Follow [AB-mediaQuery](https://www.npmjs.com/package/ab-mediaquery) readme to configure it the way you like depending on your needs. For exemple: | ||
The other dependency is [AB-mediaQuery](https://www.npmjs.com/package/ab-mediaquery). | ||
``` | ||
abMediaQuery({ | ||
bp: { | ||
smallOnly: 'screen and (max-width: 767px)', | ||
mediumOnly: 'screen and (min-width: 768px) and (max-width: 1024px)', | ||
medium: 'screen and (min-width: 768px)', | ||
largeOnly: 'screen and (min-width: 1025px) and (max-width: 1280px)', | ||
large: 'screen and (min-width: 1025px)' | ||
} | ||
}); | ||
``` | ||
Follow AB-mediaQuery readme to configure it the way you like depending on your needs (or init it with `abMediaQuery()` for default configuration). | ||
Then you only need to initialize with `AB.interchange()` or with options: | ||
Then you only need to initialize with `abInterchange()` or with options: | ||
``` | ||
AB.interchange({ | ||
mode : 'img" | ||
lazy : true, // or false | ||
delay : 100 // debounce time on scroll event (only when lazy loading is true) | ||
offscreen : 1.5 // load items only when in the view + 0.5 by default | ||
}); | ||
``` | ||
``` | ||
abInterchange({ | ||
lazy : false, // or true | ||
delay : 100 // debounce time on scroll event (only when lazy loading is true) | ||
offscreen : 1.5 // load items only when in the view + 0.5 by default | ||
}); | ||
``` | ||
You can also use **data-ab-interchange** attribute to pass those otpions individually. | ||
**data-ab-interchange** attribute should contain a list of arrays with the path to the asset and the breakpoint name as defined in AB-mediaQuery. Beware to respect mobile first order! | ||
**data-ab-interchange-src** attribute is where you define different sources and breakpoints defined with AB-mediaQuery. | ||
It should contain a list of arrays with the path to the asset and the breakpoint name. Beware to respect mobile first order. Order is **VERY** important! | ||
Defaults breakpoints defined by AB-mediaQueries are: | ||
- small | ||
- smallOnly | ||
- medium | ||
- mediumOnly | ||
- large | ||
- largeOnly | ||
- huge | ||
- hugeOnly | ||
--- | ||
Plus set a default asset in case there is no media query matching: `[path/to/asset, default]`. | ||
If you don't want any default asset, you can must set the corresponding value to empty the content (**NOT for IMG**): | ||
- background-image: `[empty.bg, default]` | ||
- HTML content: `[empty.ajax, default]` | ||
## Examples | ||
It's clever to prepare a spinner animation as first img src (or default CSS styling when `src=""`) before init, especially when using **lazy-loading** option. | ||
### **picture** | ||
``` | ||
<picture> | ||
<source srcset="xxx" media="(min-width: 80em)"/> | ||
<source srcset="xxx" media="(min-width: 64em)"/> | ||
<source srcset="xxx" media="(min-width: 48em)"/> | ||
<source srcset="xxx"/> | ||
<img | ||
alt="" | ||
data-ab-interchange='{"lazy": false}" | ||
data-ab-interchange-src="[xxx, smallOnly], [xxx, medium]"/> | ||
</picture> | ||
``` | ||
## Examples | ||
### **img** | ||
``` | ||
<img | ||
src="spinner.gif" | ||
data-ab-interchange="[path/to/default/img, default], [path/to/small/img, small], [path/to/medium/img, medium]" | ||
> | ||
``` | ||
``` | ||
<img | ||
alt="" | ||
data-ab-interchange='{"lazy": false}" | ||
data-ab-interchange-src="[xxx, smallOnly], [xxx, medium]"/> | ||
``` | ||
@@ -83,17 +109,17 @@ | ||
To determine if it's a background-image changing, the script looks for image file extensions (`gif|jpg|jpeg|tiff|png`). | ||
``` | ||
<div | ||
data-ab-interchange='{"mode": "background", "lazy": true, "delay": 100, "offscreen": 1.5}" | ||
data-ab-interchange-src="[xxx, smallOnly], [xxx, medium]"> | ||
</div> | ||
``` | ||
``` | ||
<div data-ab-interchange="[empty.bg, default], [path/to/medium/img, medium]"></div> | ||
``` | ||
It this example, the will not be any background-image on small devices. | ||
### **and even XMLHttpRequest content (ajax)!** | ||
If the data-ab-interchange is neither an image format nor on an img tag, that will send and http request and put the response inside the element. | ||
``` | ||
<div data-ab-interchange="[path/to/default/http/request, default], [path/to/small/http/request, small], [path/to/medium/http/request, medium]"></div> | ||
``` | ||
``` | ||
<div | ||
data-ab-interchange="{"mode": "ajax", "lazy": false}" | ||
data-ab-interchange-src="[xxx, smallOnly], [xxx, mediumOnly]"> | ||
</div> | ||
``` |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
0
1
125
10919
2
161
+ Addedanother-brick@^1.0.1
+ Addedab-mediaquery@2.7.2(transitive)
+ Addedanother-brick@1.3.1(transitive)
- Removedab-mediaquery@1.4.4(transitive)
Updatedab-mediaquery@^2.1.5