Browse Source

Small cleanups, improved structure, reduce size

pull/50/head
Todd Motto 10 years ago
parent
commit
9921a9f10d
  1. 13
      .jshintrc
  2. 39
      README.md
  3. 167
      dist/echo.js
  4. 4
      dist/echo.min.js
  5. 4
      package.json
  6. 165
      src/echo.js

13
.jshintrc

@ -4,18 +4,19 @@
"esnext": true, "esnext": true,
"bitwise": true, "bitwise": true,
"camelcase": true, "camelcase": true,
"curly": true, "curly": false,
"eqeqeq": true, "eqeqeq": true,
"immed": true,
"indent": 2, "indent": 2,
"immed": true,
"latedef": true, "latedef": true,
"newcap": true, "newcap": true,
"noarg": true, "noarg": true,
"quotmark": "single", "quotmark": "single",
"regexp": true, "regexp": true,
"undef": true, "undef": true,
"unused": true, "unused": false,
"strict": true, "trailing": false,
"trailing": true, "globals" : {
"smarttabs": true "define": true
}
} }

39
README.md

@ -1,6 +1,6 @@
# Echo [![Build Status](https://travis-ci.org/toddmotto/echo.png)](https://travis-ci.org/toddmotto/echo) # Echo [![Build Status](https://travis-ci.org/toddmotto/echo.png)](https://travis-ci.org/toddmotto/echo)
Echo is a standalone JavaScript lazy-loading image tool. Echo is fast, less than 1KB and uses HTML5 data-* attributes. Check out a [demo](http://toddmotto.com/labs/echo). Echo works in IE8+. Echo is a standalone JavaScript lazy-loading image micro-library. Echo is fast, 2KB, and uses HTML5 data-* attributes for simple. Check out a [demo](http://toddmotto.com/labs/echo). Echo works in IE8+.
``` ```
bower install echojs bower install echojs
@ -15,19 +15,21 @@ Using Echo.js is simple, just add the image you wish to load to a `data-echo` a
<script src="dist/echo.js"></script> <script src="dist/echo.js"></script>
<script> <script>
Echo.init({ echo.init({
offset: 100, offset: 100,
throttle: 250, throttle: 250,
unload: false, unload: false,
callback: function(element, op){ console.log(element, "has been", op+'ed')} callback: function (element, op) {
console.log(element, 'has been', op + 'ed')
}
}); });
// Echo.render(); is also available for non-scroll callbacks // echo.render(); is also available for non-scroll callbacks
</script> </script>
</body> </body>
``` ```
## .init() API (options) ## .init() (options)
The `init()` API takes a few options The `init()` API takes a few options
@ -83,7 +85,7 @@ Type: `Function`
The callback will be passed the element that has been updated and what the update operation was (ie `load` or `unload`). This can be useful if you want to add a class like `loaded` to the element. Or do some logging. The callback will be passed the element that has been updated and what the update operation was (ie `load` or `unload`). This can be useful if you want to add a class like `loaded` to the element. Or do some logging.
```js ```js
Echo.init({ echo.init({
callback: function(element, op) { callback: function(element, op) {
if(op === 'load') { if(op === 'load') {
elemend.classList.add('loaded'); elemend.classList.add('loaded');
@ -94,16 +96,15 @@ Echo.init({
}); });
``` ```
## .render()
## .render() API
Echo's callback `render()` can be used to make Echo poll your images when you're not scrolling, for instance if you've got a filter layout that swaps images but does not scroll, you need to call the internal functions without scrolling. Use `render()` for this: Echo's callback `render()` can be used to make Echo poll your images when you're not scrolling, for instance if you've got a filter layout that swaps images but does not scroll, you need to call the internal functions without scrolling. Use `render()` for this:
```js ```js
Echo.render(); echo.render();
``` ```
Using `render()` is also throttled, which means you can bind it to a `window.onresize` event and it will be optimised for performance in the same way `window.onscroll` is. Using `render()` is also throttled, which means you can bind it to an `onresize` event and it will be optimised for performance in the same way `onscroll` is.
## Manual installation ## Manual installation
Drop your files into your required folders, make sure you're using the file(s) from the `dist` folder, which is the compiled production-ready code. Ensure you place the script before the closing `</body>` tag so the DOM tree is populated when the script runs. Drop your files into your required folders, make sure you're using the file(s) from the `dist` folder, which is the compiled production-ready code. Ensure you place the script before the closing `</body>` tag so the DOM tree is populated when the script runs.
@ -115,22 +116,8 @@ Add the image that needs to load when it's visible inside the viewport in a `dat
<img src="img/blank.gif" alt="Photo" data-echo="img/photo.jpg"> <img src="img/blank.gif" alt="Photo" data-echo="img/photo.jpg">
``` ```
## Scaffolding ## Contributing
Project files and folder structure. In lieu of a formal style guide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using Grunt.
```
├── dist/
│ ├── echo.js
│ └── echo.min.js
├── src/
│ └── echo.js
├── .editorconfig
├── .gitignore
├── .jshintrc
├── .travis.yml
├── Gruntfile.js
└── package.json
```
## License ## License
MIT license MIT license

167
dist/echo.js vendored

@ -1,102 +1,40 @@
/*! Echo v1.5.0 | (c) 2014 @toddmotto | MIT license | github.com/toddmotto/echo */ /*! echo.js v1.6.0 | (c) 2014 @toddmotto | MIT license | github.com/toddmotto/echo */
window.Echo = (function (global, document, undefined) { (function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory;
} else {
root.echo = factory(root);
}
})(this, function (root) {
'use strict'; 'use strict';
/** var exports = {};
* callback - initialized to a no-op so that no validations on it's presence need to be made
* @type {Function}
*/
var callback = function(){};
/** var callback = function () {};
* offset, throttle, poll, unload vars
*/
var offset, throttle, poll, unload;
/** var offset, poll, throttle, unload;
* _inView
* @private var inView = function (element, view) {
* @param {Element} element Image element
* @returns {Boolean} Is element in viewport
*/
var _inView = function (element, view) {
var box = element.getBoundingClientRect(); var box = element.getBoundingClientRect();
return (box.right >= view.l && box.bottom >= view.t && box.left <= view.r && box.top <= view.b); return (box.right >= view.l && box.bottom >= view.t && box.left <= view.r && box.top <= view.b);
}; };
/** var debounce = function () {
* _pollImages Loop through the images if present
* or remove all event listeners
* @private
*/
var _pollImages = function () {
var src,
i,
elem,
view,
nodes = document.querySelectorAll('img[data-echo]'),
length = nodes.length;
view = {
l: 0 - offset.l,
t: 0 - offset.t,
b: (window.innerHeight || document.documentElement.clientHeight) + offset.b,
r: (window.innerWidth || document.documentElement.clientWidth) + offset.r
};
for(i=0; i<length; i++) {
elem = nodes[i];
if(_inView(elem, view)) {
if(unload) {
elem.setAttribute('data-echo-placeholder', elem.src);
}
elem.src = elem.getAttribute('data-echo');
if(!unload) {
elem.removeAttribute('data-echo');
}
callback(elem, 'load');
} else if(unload && !!(src = elem.getAttribute('data-echo-placeholder'))) {
elem.src = src;
elem.removeAttribute('data-echo-placeholder');
callback(elem, 'unload');
}
}
if(!length) {
detach();
}
};
/**
* _throttle Sensible event firing
* @private
*/
var _throttle = function () {
clearTimeout(poll); clearTimeout(poll);
poll = setTimeout(_pollImages, throttle); poll = setTimeout(exports.render, throttle);
}; };
/** exports.init = function (opts) {
* init Module init function
* @param {Object} [opts] Passed in Object with options
* @param {Number|String} [opts.throttle]
* @param {Number|String} [opts.offset]
* @param {Number|String} [opts.offsetBottom]
* @param {Number|String} [opts.offsetTop]
* @param {Number|String} [opts.offsetLeft]
* @param {Number|String} [opts.offsetRight]
* @param {Boolean} [opts.unload]
* @param {Function} [opts.callback]
*/
var init = function (opts) {
opts = opts || {}; opts = opts || {};
var offsetAll = opts.offset || 0; var offsetAll = opts.offset || 0;
var offsetVertical = opts.offsetVertical || offsetAll; var offsetVertical = opts.offsetVertical || offsetAll;
var offsetHorizontal = opts.offsetHorizontal || offsetAll; var offsetHorizontal = opts.offsetHorizontal || offsetAll;
var optionToInt = function (opt, fallback) {
function optionToInt(opt, fallback) {
return parseInt(opt || fallback, 10); return parseInt(opt || fallback, 10);
} };
offset = { offset = {
t: optionToInt(opts.offsetTop, offsetVertical), t: optionToInt(opts.offsetTop, offsetVertical),
b: optionToInt(opts.offsetBottom, offsetVertical), b: optionToInt(opts.offsetBottom, offsetVertical),
@ -106,40 +44,57 @@ window.Echo = (function (global, document, undefined) {
throttle = optionToInt(opts.throttle, 250); throttle = optionToInt(opts.throttle, 250);
unload = !!opts.unload; unload = !!opts.unload;
callback = opts.callback || callback; callback = opts.callback || callback;
exports.render();
_pollImages();
if (document.addEventListener) { if (document.addEventListener) {
global.addEventListener('scroll', _throttle, false); root.addEventListener('scroll', debounce, false);
global.addEventListener('load', _throttle, false); root.addEventListener('load', debounce, false);
} else { } else {
global.attachEvent('onscroll', _throttle); root.attachEvent('onscroll', debounce);
global.attachEvent('onload', _throttle); root.attachEvent('onload', debounce);
} }
};
exports.render = function () {
var nodes = document.querySelectorAll('img[data-echo]');
var length = nodes.length;
var src, elem;
var view = {
l: 0 - offset.l,
t: 0 - offset.t,
b: (root.innerHeight || document.documentElement.clientHeight) + offset.b,
r: (root.innerWidth || document.documentElement.clientWidth) + offset.r
};
for (var i = 0; i < length; i++) {
elem = nodes[i];
if (inView(elem, view)) {
if (unload) {
elem.setAttribute('data-echo-placeholder', elem.src);
}
elem.src = elem.getAttribute('data-echo');
if (!unload) {
elem.removeAttribute('data-echo');
}
callback(elem, 'load');
} else if (unload && !!(src = elem.getAttribute('data-echo-placeholder'))) {
elem.src = src;
elem.removeAttribute('data-echo-placeholder');
callback(elem, 'unload');
}
}
if (!length) {
exports.detach();
}
}; };
/** exports.detach = function () {
* detach remove listeners
*/
var detach = function() {
if (document.removeEventListener) { if (document.removeEventListener) {
global.removeEventListener('scroll', _throttle); root.removeEventListener('scroll', debounce);
} else { } else {
global.detachEvent('onscroll', _throttle); root.detachEvent('onscroll', debounce);
} }
clearTimeout(poll); clearTimeout(poll);
}; };
/** return exports;
* return Public methods
* @returns {Object}
*/
return {
init: init,
detach: detach,
render: _pollImages
};
})(this, document); });

4
dist/echo.min.js vendored

@ -1,2 +1,2 @@
/*! Echo v1.5.0 | (c) 2014 @toddmotto | MIT license | github.com/toddmotto/echo */ /*! echo.js v1.6.0 | (c) 2014 @toddmotto | MIT license | github.com/toddmotto/echo */
window.Echo=function(a,b){"use strict";var c,d,e,f,g=function(){},h=function(a,b){var c=a.getBoundingClientRect();return c.right>=b.l&&c.bottom>=b.t&&c.left<=b.r&&c.top<=b.b},i=function(){var a,d,e,i,j=b.querySelectorAll("img[data-echo]"),k=j.length;for(i={l:0-c.l,t:0-c.t,b:(window.innerHeight||b.documentElement.clientHeight)+c.b,r:(window.innerWidth||b.documentElement.clientWidth)+c.r},d=0;k>d;d++)e=j[d],h(e,i)?(f&&e.setAttribute("data-echo-placeholder",e.src),e.src=e.getAttribute("data-echo"),f||e.removeAttribute("data-echo"),g(e,"load")):f&&(a=e.getAttribute("data-echo-placeholder"))&&(e.src=a,e.removeAttribute("data-echo-placeholder"),g(e,"unload"));k||l()},j=function(){clearTimeout(e),e=setTimeout(i,d)},k=function(e){function h(a,b){return parseInt(a||b,10)}e=e||{};var k=e.offset||0,l=e.offsetVertical||k,m=e.offsetHorizontal||k;c={t:h(e.offsetTop,l),b:h(e.offsetBottom,l),l:h(e.offsetLeft,m),r:h(e.offsetRight,m)},d=h(e.throttle,250),f=!!e.unload,g=e.callback||g,i(),b.addEventListener?(a.addEventListener("scroll",j,!1),a.addEventListener("load",j,!1)):(a.attachEvent("onscroll",j),a.attachEvent("onload",j))},l=function(){b.removeEventListener?a.removeEventListener("scroll",j):a.detachEvent("onscroll",j),clearTimeout(e)};return{init:k,detach:l,render:i}}(this,document); !function(a,b){"function"==typeof define&&define.amd?define(b):"object"==typeof exports?module.exports=b:a.echo=b(a)}(this,function(a){"use strict";var b,c,d,e,f={},g=function(){},h=function(a,b){var c=a.getBoundingClientRect();return c.right>=b.l&&c.bottom>=b.t&&c.left<=b.r&&c.top<=b.b},i=function(){clearTimeout(c),c=setTimeout(f.render,d)};return f.init=function(c){c=c||{};var h=c.offset||0,j=c.offsetVertical||h,k=c.offsetHorizontal||h,l=function(a,b){return parseInt(a||b,10)};b={t:l(c.offsetTop,j),b:l(c.offsetBottom,j),l:l(c.offsetLeft,k),r:l(c.offsetRight,k)},d=l(c.throttle,250),e=!!c.unload,g=c.callback||g,f.render(),document.addEventListener?(a.addEventListener("scroll",i,!1),a.addEventListener("load",i,!1)):(a.attachEvent("onscroll",i),a.attachEvent("onload",i))},f.render=function(){for(var c,d,i=document.querySelectorAll("img[data-echo]"),j=i.length,k={l:0-b.l,t:0-b.t,b:(a.innerHeight||document.documentElement.clientHeight)+b.b,r:(a.innerWidth||document.documentElement.clientWidth)+b.r},l=0;j>l;l++)d=i[l],h(d,k)?(e&&d.setAttribute("data-echo-placeholder",d.src),d.src=d.getAttribute("data-echo"),e||d.removeAttribute("data-echo"),g(d,"load")):e&&(c=d.getAttribute("data-echo-placeholder"))&&(d.src=c,d.removeAttribute("data-echo-placeholder"),g(d,"unload"));j||f.detach()},f.detach=function(){document.removeEventListener?a.removeEventListener("scroll",i):a.detachEvent("onscroll",i),clearTimeout(c)},f});

4
package.json

@ -1,6 +1,6 @@
{ {
"name": "Echo", "name": "echo.js",
"version": "1.5.0", "version": "1.6.0",
"homepage": "https://github.com/toddmotto/echo", "homepage": "https://github.com/toddmotto/echo",
"author": "Todd Motto", "author": "Todd Motto",
"description": "Lazy-loading with data-* attributes, offset and throttle options", "description": "Lazy-loading with data-* attributes, offset and throttle options",

165
src/echo.js

@ -1,101 +1,39 @@
window.Echo = (function (global, document, undefined) { (function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory;
} else {
root.echo = factory(root);
}
})(this, function (root) {
'use strict'; 'use strict';
/** var exports = {};
* callback - initialized to a no-op so that no validations on it's presence need to be made
* @type {Function}
*/
var callback = function(){};
/** var callback = function () {};
* offset, throttle, poll, unload vars
*/
var offset, throttle, poll, unload;
/** var offset, poll, throttle, unload;
* _inView
* @private var inView = function (element, view) {
* @param {Element} element Image element
* @returns {Boolean} Is element in viewport
*/
var _inView = function (element, view) {
var box = element.getBoundingClientRect(); var box = element.getBoundingClientRect();
return (box.right >= view.l && box.bottom >= view.t && box.left <= view.r && box.top <= view.b); return (box.right >= view.l && box.bottom >= view.t && box.left <= view.r && box.top <= view.b);
}; };
/** var debounce = function () {
* _pollImages Loop through the images if present
* or remove all event listeners
* @private
*/
var _pollImages = function () {
var src,
i,
elem,
view,
nodes = document.querySelectorAll('img[data-echo]'),
length = nodes.length;
view = {
l: 0 - offset.l,
t: 0 - offset.t,
b: (window.innerHeight || document.documentElement.clientHeight) + offset.b,
r: (window.innerWidth || document.documentElement.clientWidth) + offset.r
};
for(i=0; i<length; i++) {
elem = nodes[i];
if(_inView(elem, view)) {
if(unload) {
elem.setAttribute('data-echo-placeholder', elem.src);
}
elem.src = elem.getAttribute('data-echo');
if(!unload) {
elem.removeAttribute('data-echo');
}
callback(elem, 'load');
} else if(unload && !!(src = elem.getAttribute('data-echo-placeholder'))) {
elem.src = src;
elem.removeAttribute('data-echo-placeholder');
callback(elem, 'unload');
}
}
if(!length) {
detach();
}
};
/**
* _throttle Sensible event firing
* @private
*/
var _throttle = function () {
clearTimeout(poll); clearTimeout(poll);
poll = setTimeout(_pollImages, throttle); poll = setTimeout(exports.render, throttle);
}; };
/** exports.init = function (opts) {
* init Module init function
* @param {Object} [opts] Passed in Object with options
* @param {Number|String} [opts.throttle]
* @param {Number|String} [opts.offset]
* @param {Number|String} [opts.offsetBottom]
* @param {Number|String} [opts.offsetTop]
* @param {Number|String} [opts.offsetLeft]
* @param {Number|String} [opts.offsetRight]
* @param {Boolean} [opts.unload]
* @param {Function} [opts.callback]
*/
var init = function (opts) {
opts = opts || {}; opts = opts || {};
var offsetAll = opts.offset || 0; var offsetAll = opts.offset || 0;
var offsetVertical = opts.offsetVertical || offsetAll; var offsetVertical = opts.offsetVertical || offsetAll;
var offsetHorizontal = opts.offsetHorizontal || offsetAll; var offsetHorizontal = opts.offsetHorizontal || offsetAll;
var optionToInt = function (opt, fallback) {
function optionToInt(opt, fallback) {
return parseInt(opt || fallback, 10); return parseInt(opt || fallback, 10);
} };
offset = { offset = {
t: optionToInt(opts.offsetTop, offsetVertical), t: optionToInt(opts.offsetTop, offsetVertical),
b: optionToInt(opts.offsetBottom, offsetVertical), b: optionToInt(opts.offsetBottom, offsetVertical),
@ -105,40 +43,57 @@ window.Echo = (function (global, document, undefined) {
throttle = optionToInt(opts.throttle, 250); throttle = optionToInt(opts.throttle, 250);
unload = !!opts.unload; unload = !!opts.unload;
callback = opts.callback || callback; callback = opts.callback || callback;
exports.render();
_pollImages();
if (document.addEventListener) { if (document.addEventListener) {
global.addEventListener('scroll', _throttle, false); root.addEventListener('scroll', debounce, false);
global.addEventListener('load', _throttle, false); root.addEventListener('load', debounce, false);
} else { } else {
global.attachEvent('onscroll', _throttle); root.attachEvent('onscroll', debounce);
global.attachEvent('onload', _throttle); root.attachEvent('onload', debounce);
} }
};
exports.render = function () {
var nodes = document.querySelectorAll('img[data-echo]');
var length = nodes.length;
var src, elem;
var view = {
l: 0 - offset.l,
t: 0 - offset.t,
b: (root.innerHeight || document.documentElement.clientHeight) + offset.b,
r: (root.innerWidth || document.documentElement.clientWidth) + offset.r
};
for (var i = 0; i < length; i++) {
elem = nodes[i];
if (inView(elem, view)) {
if (unload) {
elem.setAttribute('data-echo-placeholder', elem.src);
}
elem.src = elem.getAttribute('data-echo');
if (!unload) {
elem.removeAttribute('data-echo');
}
callback(elem, 'load');
} else if (unload && !!(src = elem.getAttribute('data-echo-placeholder'))) {
elem.src = src;
elem.removeAttribute('data-echo-placeholder');
callback(elem, 'unload');
}
}
if (!length) {
exports.detach();
}
}; };
/** exports.detach = function () {
* detach remove listeners
*/
var detach = function() {
if (document.removeEventListener) { if (document.removeEventListener) {
global.removeEventListener('scroll', _throttle); root.removeEventListener('scroll', debounce);
} else { } else {
global.detachEvent('onscroll', _throttle); root.detachEvent('onscroll', debounce);
} }
clearTimeout(poll); clearTimeout(poll);
}; };
/** return exports;
* return Public methods
* @returns {Object}
*/
return {
init: init,
detach: detach,
render: _pollImages
};
})(this, document); });

Loading…
Cancel
Save