├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package.json └── test └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Watson Design Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | size.js 2 | === 3 | 4 | Small util to centralize and debounce window 'resize' events. 5 | 6 | - Avoid accessing a global object (window) 7 | - Avoid triggering [unnecessary repaint/reflow](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) 8 | - Avoid locking the UI by only dispatching one event 9 | 10 | 11 | ## Install 12 | 13 | ``` 14 | npm install watsondg/size -S 15 | ``` 16 | 17 | ## Usage 18 | 19 | ``` 20 | var size = require('size'); 21 | 22 | size.addListener(function(width, height) { 23 | console.log('resized', width, height); 24 | }); 25 | console.log(size.width); 26 | ``` 27 | 28 | ## Instance Methods 29 | 30 | ### addListener(handler[, context]) 31 | 32 | Bind a function to the resize event 33 | * `handler` - the function to call after a resize event 34 | * `context` - (OPTIONAL) - the context to bind the event callback to 35 | 36 | 37 | ### removeListener(handler[, context]) 38 | 39 | Unbind a function to the resize event 40 | * `handler` - the function to call after a resize event 41 | * `context` - (OPTIONAL) - the context to bind the event callback to 42 | 43 | ### bind(options) 44 | 45 | Enable the singleton by listen to the window `onresize` event. 46 | * `options` - a hash containing configurable options: 47 | - `debounceTime`: debounce delay for the window `onresize` event. Defaults is 150. 48 | 49 | ### unbind() 50 | 51 | Unbind the window `onresize` event. 52 | 53 | ## Instance Properties 54 | 55 | ### width 56 | ### height 57 | ### isLandscape 58 | ### hasBar (experimental) 59 | true on mobile if the browser bar is shown on iOS. 60 | 61 | ## License 62 | MIT. 63 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Emitter = require('tiny-emitter'); 4 | var debounce = require('debounce'); 5 | var EVENT_NAME = 'resize'; 6 | 7 | var emitter = new Emitter(); 8 | var debounceTime; 9 | var debounced; 10 | var isiOS = (/ip(hone|od|ad)/i).test(window.navigator.userAgent.toLowerCase()) && !window.MSStream; 11 | 12 | var size = module.exports = { 13 | width: 0, 14 | height: 0, 15 | hasBar: false, 16 | isLandscape: false, 17 | 18 | addListener: function(listener, context) { 19 | emitter.on(EVENT_NAME, listener, context); 20 | }, 21 | 22 | removeListener: function(listener, context) { 23 | if(listener) emitter.off(EVENT_NAME, listener, context); 24 | }, 25 | 26 | bind: function(opts) { 27 | opts = opts || {}; 28 | size.unbind(); 29 | debounceTime = opts.debounceTime || 150; 30 | debounced = debounce(onEvent, debounceTime); 31 | window.addEventListener(EVENT_NAME, debounced); 32 | }, 33 | 34 | unbind: function() { 35 | window.removeEventListener(EVENT_NAME, debounced); 36 | } 37 | }; 38 | 39 | function onEvent() { 40 | if (isiOS) { 41 | size.hasBar = size.width > size.height && size.height > window.innerHeight; 42 | } 43 | 44 | size.width = window.innerWidth; 45 | size.height = window.innerHeight; 46 | 47 | size.isLandscape = size.width > size.height; 48 | emitter.emit(EVENT_NAME, size.width, size.height); 49 | } 50 | 51 | onEvent(); 52 | size.bind(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "size", 3 | "version": "1.2.0", 4 | "description": "Small util to centralize and debounce window 'resize' events.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "browserify test/index.js | tap-closer | smokestack | faucet", 8 | "test-debug": "budo test/index.js --live" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+ssh://git@github.com/watsondg/size.git" 13 | }, 14 | "keywords": [ 15 | "size", 16 | "window", 17 | "resize", 18 | "innerwidth", 19 | "innerheight", 20 | "viewport", 21 | "screen" 22 | ], 23 | "author": "Florian Morel", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/watsondg/size/issues" 27 | }, 28 | "homepage": "https://github.com/watsondg/size#readme", 29 | "devDependencies": { 30 | "faucet": "0.0.1", 31 | "smokestack": "^3.4.1", 32 | "tap-closer": "^1.0.0", 33 | "tape": "^4.5.1" 34 | }, 35 | "dependencies": { 36 | "debounce": "^1.0.0", 37 | "tiny-emitter": "^1.0.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var size = require('../index.js'); 5 | var debounceTime = 150; 6 | 7 | size.bind({ 8 | debounceTime: debounceTime 9 | }); 10 | 11 | test('addListener test', function(assert) { 12 | var resizeHandler = function() { 13 | size.removeListener(resizeHandler); 14 | assert.pass('resize handler should be called.'); 15 | assert.end(); 16 | }; 17 | size.addListener(resizeHandler); 18 | 19 | triggerResize(); 20 | }); 21 | 22 | test('removeListener test', function(assert) { 23 | var resizeHandler = function() { 24 | assert.fail('resize handler shouldn\'t be called.'); 25 | }; 26 | size.addListener(resizeHandler); 27 | size.removeListener(resizeHandler); 28 | triggerResize(); 29 | 30 | setTimeout(function() { 31 | assert.pass('resize handler NOT called.'); 32 | assert.end(); 33 | }, debounceTime + 20); 34 | }); 35 | 36 | test('instance properties test', function(assert) { 37 | assert.plan(3); 38 | 39 | var resizeHandler = function() { 40 | size.removeListener(resizeHandler); 41 | assert.ok(size.width > 0, 'width should not be null'); 42 | assert.ok(size.height > 0, 'width should not be null'); 43 | assert.ok(size.isLandscape === size.width > size.height ? true : false, 'isLandscape should reflect window ratio.'); 44 | }; 45 | size.addListener(resizeHandler); 46 | triggerResize(); 47 | }); 48 | 49 | test('width/height test', function(assert) { 50 | var resizeHandler = function(width, height) { 51 | assert.ok(width == window.innerWidth, 'Passed width should be same as window width'); 52 | size.removeListener(resizeHandler); 53 | assert.end(); 54 | }; 55 | size.addListener(resizeHandler); 56 | 57 | triggerResize(); 58 | }); 59 | 60 | function triggerResize() { 61 | window.dispatchEvent(new Event('resize')); 62 | } 63 | 64 | test('unbind test', function(assert) { 65 | var resizeHandler = function() { 66 | assert.fail('resize handler shouldn\'t be called.'); 67 | }; 68 | 69 | size.unbind(); 70 | triggerResize(); 71 | 72 | setTimeout(function() { 73 | assert.pass('resize handler NOT called.'); 74 | assert.end(); 75 | }, debounceTime + 20); 76 | }); --------------------------------------------------------------------------------