├── tests ├── tests.css ├── tests-jquery.html ├── tests-javascript.html ├── tests-jquery.js └── tests-javascript.js ├── bower.json ├── package.json ├── LICENSE ├── README.md ├── detect-element-resize.js └── jquery.resize.js /tests/tests.css: -------------------------------------------------------------------------------- 1 | #container { 2 | /*display: none;*/ 3 | } 4 | 5 | #resizable-element { 6 | /*display: inline-block;*/ 7 | float: left; 8 | } 9 | 10 | #content { 11 | color: red; 12 | width: 20px; 13 | height: 15px; 14 | } -------------------------------------------------------------------------------- /tests/tests-jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | javascript-detect-element-resize Tests 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/tests-javascript.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | javascript-detect-element-resize Tests 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-detect-element-resize", 3 | "version": "0.5.3", 4 | "main": "detect-element-resize.js", 5 | "repository": { 6 | "type": "git", 7 | "url": "git@github.com:sdecima/javascript-detect-element-resize.git" 8 | }, 9 | "homepage": "https://github.com/sdecima/javascript-detect-element-resize", 10 | "authors": [ 11 | "Sebastián Décima (https://github.com/sdecima/)" 12 | ], 13 | "description": "A Cross-Browser, Event-based, Element Resize Detection", 14 | "keywords": [ 15 | "resize", 16 | "events", 17 | "javascript", 18 | "ecmascript" 19 | ], 20 | "license": "MIT", 21 | "ignore": [ 22 | "**/.*" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-detect-element-resize", 3 | "version": "0.5.3", 4 | "description": "A Cross-Browser, Event-based, Element Resize Detection", 5 | "main": "detect-element-resize.js", 6 | "directories": { 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/sdecima/javascript-detect-element-resize" 15 | }, 16 | "keywords": [ 17 | "resize", 18 | "events" 19 | ], 20 | "author": "Sebastián Décima (https://github.com/sdecima/)", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/sdecima/javascript-detect-element-resize/issues" 24 | }, 25 | "homepage": "https://github.com/sdecima/javascript-detect-element-resize", 26 | "devDependencies": { 27 | "browserify": "^6.3.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Sebastián Décima 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /tests/tests-jquery.js: -------------------------------------------------------------------------------- 1 | var container, element, content; 2 | 3 | QUnit.module('main', { 4 | setup: function() { 5 | var fixture = '
'; 6 | $("#qunit-fixture").append(fixture); 7 | 8 | container = document.getElementById('container'); 9 | element = document.getElementById('resizable-element'); 10 | content = document.getElementById('content'); 11 | 12 | $('#container').hide(); 13 | $(element).resize(detectCallback); 14 | $('#container').show(); 15 | shouldDetect = true; 16 | detected = false; 17 | }, 18 | teardown: function() { 19 | $('#styleTest').remove(); 20 | try { 21 | $(element).removeResize(detectCallback); 22 | } catch(e) {} 23 | } 24 | }); 25 | 26 | var newWidth = 0, newHeight = 0, shouldDetect = true, detected = false; 27 | var detectCallback = function() { 28 | detected = true; 29 | }; 30 | 31 | var validateEvent = function(assert) { 32 | setTimeout(function() { 33 | if(shouldDetect) { 34 | assert.ok(shouldDetect === true && detected === true, 'resize event fired OK'); 35 | } 36 | assert.ok($(content).width() == newWidth, 'Resize OK'); 37 | 38 | QUnit.start(); 39 | }, 2000); 40 | }; 41 | 42 | QUnit.asyncTest( "JS addResizeListener css resize test", function( assert ) { 43 | expect( 2 ); 44 | 45 | newWidth = 100; 46 | 47 | var myCss = ''; 48 | $('head').append(myCss); 49 | 50 | validateEvent(assert); 51 | }); 52 | 53 | QUnit.asyncTest( "JS addResizeListener script resize test", function( assert ) { 54 | expect( 2 ); 55 | 56 | newWidth = 30; 57 | 58 | $(content).width(newWidth); 59 | 60 | validateEvent(assert); 61 | }); 62 | 63 | QUnit.asyncTest( "JS addResizeListener script reattach element test", function( assert ) { 64 | expect( 2 ); 65 | 66 | var elem = $(content).detach(); 67 | 68 | setTimeout(function() { 69 | $(container).append("div").append(elem); 70 | //elem.appendTo(container); 71 | newWidth = 68; 72 | $(content).width(newWidth); 73 | }, 500); 74 | 75 | validateEvent(assert); 76 | }); 77 | 78 | QUnit.asyncTest( "JS removeResizeListener test", function( assert ) { 79 | expect( 1 ); 80 | 81 | newWidth = 0; 82 | shouldDetect = false; 83 | 84 | $(element).removeResize(detectCallback); 85 | 86 | $(content).width(newWidth); 87 | $(content).height(0); 88 | 89 | validateEvent(assert); 90 | }); -------------------------------------------------------------------------------- /tests/tests-javascript.js: -------------------------------------------------------------------------------- 1 | var container, element, content; 2 | 3 | QUnit.module('main', { 4 | setup: function() { 5 | var fixture = '
'; 6 | $("#qunit-fixture").append(fixture); 7 | 8 | container = document.getElementById('container'); 9 | element = document.getElementById('resizable-element'); 10 | content = document.getElementById('content'); 11 | 12 | $('#container').hide(); 13 | addResizeListener(element, detectCallback); 14 | $('#container').show(); 15 | shouldDetect = true; 16 | detected = false; 17 | }, 18 | teardown: function() { 19 | $('#styleTest').remove(); 20 | try { 21 | removeResizeListener(element, detectCallback); 22 | } catch(e) {} 23 | } 24 | }); 25 | 26 | var newWidth = 0, newHeight = 0, shouldDetect = true, detected = false; 27 | var detectCallback = function() { 28 | detected = true; 29 | }; 30 | 31 | var validateEvent = function(assert) { 32 | setTimeout(function() { 33 | if(shouldDetect) { 34 | assert.ok(shouldDetect === true && detected === true, 'resize event fired OK'); 35 | } 36 | assert.ok($(content).width() == newWidth, 'Resize OK'); 37 | 38 | QUnit.start(); 39 | }, 2000); 40 | }; 41 | 42 | QUnit.asyncTest( "JS addResizeListener css resize test", function( assert ) { 43 | expect( 2 ); 44 | 45 | newWidth = 100; 46 | 47 | var myCss = ''; 48 | $('head').append(myCss); 49 | 50 | validateEvent(assert); 51 | }); 52 | 53 | QUnit.asyncTest( "JS addResizeListener script resize test", function( assert ) { 54 | expect( 2 ); 55 | 56 | newWidth = 30; 57 | 58 | $(content).width(newWidth); 59 | 60 | validateEvent(assert); 61 | }); 62 | 63 | QUnit.asyncTest( "JS addResizeListener script reattach element test", function( assert ) { 64 | expect( 2 ); 65 | 66 | var elem = $(content).detach(); 67 | 68 | setTimeout(function() { 69 | $(container).append("div").append(elem); 70 | //elem.appendTo(container); 71 | newWidth = 68; 72 | $(content).width(newWidth); 73 | }, 500); 74 | 75 | validateEvent(assert); 76 | }); 77 | 78 | QUnit.asyncTest( "JS removeResizeListener test", function( assert ) { 79 | expect( 1 ); 80 | 81 | newWidth = 0; 82 | shouldDetect = false; 83 | 84 | removeResizeListener(element, detectCallback); 85 | 86 | $(content).width(newWidth); 87 | $(content).height(0); 88 | 89 | validateEvent(assert); 90 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | javascript-detect-element-resize 2 | ================================ 3 | 4 | A Cross-Browser, Event-based, Element Resize Detection. 5 | 6 | In short, this implementation does NOT use an internal timer to detect size changes (as most implementations I found do). 7 | It uses `scroll` events on most browsers, and the [`onresize` event][5] on IE10 and below. 8 | 9 | The method used not only detects javascript generated resize changes but also changes made from CSS pseudo classes e.g. :hover, CSS animations, etc. 10 | 11 | About the libraries 12 | =================== 13 | I was searching for a library that allowed me to detect when an DOM element changes size, and all solutions I found had two problems: 14 | 15 | 1. only available as jQuery libraries (so no standalone Javascript) 16 | 2. all had terrible performance (because all of them use timers to intermittently poll the size of the elements to detect a change). 17 | 18 | Then I came across this [great post][1] on [Back Alley Coder][3] about using ~~[overflow and underflow events][2]~~ [`scroll` events][2] to do event-based element resize detection; and it works great without consuming resources at all (just like any other browser originated event). 19 | 20 | The libraries on this repository are just a ready-to-use implementation of the above, one pure javascript and the other a jQuery plugin version (just for convenience). 21 | 22 | Libraries 23 | ========= 24 | 25 | Pure Javascript library usage 26 | ----------------------------- 27 | 28 | ```html 29 | 30 | 38 | ``` 39 | 40 | jQuery plugin library usage 41 | --------------------------- 42 | ```html 43 | 44 | 45 | 53 | ``` 54 | 55 | Compatibility 56 | ------------- 57 | Works great on: 58 | 59 | - Chrome 60 | - Firefox 61 | - IE 11 and below (tested on 11, 10, 9, 8 and 7) 62 | 63 | Known Issues: 64 | 65 | - On IE 10 and below: If you detach the element and re-attach it, you will need to add the resize listener again. 66 | 67 | Doesn't work on: 68 | 69 | - ??? 70 | 71 | Please [let me know](https://github.com/sdecima/javascript-detect-element-resize/issues) if you test these libraries on any other browser, of if you run into issues with any of the above browsers. 72 | 73 | TODO 74 | ==== 75 | 76 | - Fix detach/re-attach issue on IE 10 and below (IE 9 and below doesn't support CSS animations so we can use those as in the rest of the browsers). 77 | - Create minified version of the libraries. 78 | - Add support for standard jQuery bind method on 'resize' event. 79 | 80 | Release Notes 81 | ============= 82 | v0.5.3 83 | ------ 84 | 85 | - Fix for when the element is inside a display:none, and for when it is detached and reattached (changed @thomassuckow and @jerjou fixes to properly use CSS animations) 86 | - Adding /tests/ with some general QUnit tests to help test on multiple browsers 87 | 88 | v0.5.2 89 | ------ 90 | 91 | - Adding a bower.json file (thanks @adamjcook) 92 | - Fix style being appended to head multiple times (thanks @thomassuckow and @progman32) 93 | - Work around a chrome bug that would show scrollbars in some cases (thanks @thomassuckow) 94 | 95 | v0.5.1 96 | ------ 97 | 98 | - Fix for resize event on IE 99 | 100 | v0.5 101 | ---- 102 | 103 | - It is now fully compatible with IE11. 104 | - Rework of the libraries using the new scroll-event-based code of [Back Alley Coder][1]. For the pure javascript version I pretty much used the original code from [Back Alley Coder][1] and only had to add code to dynamically insert the styling for the resize-triggers. 105 | 106 | v0.4.1 107 | ---- 108 | 109 | - Fix for jQuery 'resize' method overlapping. 110 | 111 | v0.4 112 | ---- 113 | 114 | - Adds better cross-browser support, it now uses MutationObservers only on IE11. 115 | 116 | v0.3 117 | ---- 118 | 119 | - Adds support for MutationObservers. 120 | - Adds support for IE 11. 121 | - Wrapped the pure javascript version of the library (to hide non-public methods). 122 | 123 | v0.2 124 | ---- 125 | 126 | - Adds support for IE 8 and below. 127 | 128 | v0.1 129 | ---- 130 | 131 | - Implementation based on the [works][1] of [Back Alley Coder][3]. 132 | - Adds jQuery plugin version. 133 | 134 | 135 | References 136 | ========== 137 | 138 | Similar libraries (but they use timers) 139 | --------------------------------------- 140 | [jQuery-mutate](http://www.jqui.net/jquery-projects/jquery-mutate-official/) 141 | 142 | [jQuery-resize-plugin](http://benalman.com/projects/jquery-resize-plugin/) 143 | 144 | 145 | Don't get me wrong, these are great libraries and work as advertised, it's just that they are not easy on browser resources. 146 | 147 | External links 148 | -------------- 149 | [Back Alley Coder: Cross-Browser, Event-based, Element Resize Detection][1] 150 | [Back Alley Coder: Overflow and Underflow Events][2] 151 | 152 | [1]: http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/ 153 | [2]: http://www.backalleycoder.com/2013/03/14/oft-overlooked-overflow-and-underflow-events/ 154 | [3]: http://www.backalleycoder.com/ 155 | [4]: http://www.w3.org/TR/dom/#mutation-observers 156 | [5]: http://msdn.microsoft.com/en-us/library/ie/ms536959 157 | 158 | -------------------------------------------------------------------------------- /detect-element-resize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Detect Element Resize 3 | * 4 | * https://github.com/sdecima/javascript-detect-element-resize 5 | * Sebastian Decima 6 | * 7 | * version: 0.5.3 8 | **/ 9 | 10 | (function () { 11 | var attachEvent = document.attachEvent, 12 | stylesCreated = false; 13 | 14 | if (!attachEvent) { 15 | var requestFrame = (function(){ 16 | var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || 17 | function(fn){ return window.setTimeout(fn, 20); }; 18 | return function(fn){ return raf(fn); }; 19 | })(); 20 | 21 | var cancelFrame = (function(){ 22 | var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || 23 | window.clearTimeout; 24 | return function(id){ return cancel(id); }; 25 | })(); 26 | 27 | function resetTriggers(element){ 28 | var triggers = element.__resizeTriggers__, 29 | expand = triggers.firstElementChild, 30 | contract = triggers.lastElementChild, 31 | expandChild = expand.firstElementChild; 32 | contract.scrollLeft = contract.scrollWidth; 33 | contract.scrollTop = contract.scrollHeight; 34 | expandChild.style.width = expand.offsetWidth + 1 + 'px'; 35 | expandChild.style.height = expand.offsetHeight + 1 + 'px'; 36 | expand.scrollLeft = expand.scrollWidth; 37 | expand.scrollTop = expand.scrollHeight; 38 | }; 39 | 40 | function checkTriggers(element){ 41 | return element.offsetWidth != element.__resizeLast__.width || 42 | element.offsetHeight != element.__resizeLast__.height; 43 | } 44 | 45 | function scrollListener(e){ 46 | var element = this; 47 | resetTriggers(this); 48 | if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__); 49 | this.__resizeRAF__ = requestFrame(function(){ 50 | if (checkTriggers(element)) { 51 | element.__resizeLast__.width = element.offsetWidth; 52 | element.__resizeLast__.height = element.offsetHeight; 53 | element.__resizeListeners__.forEach(function(fn){ 54 | fn.call(element, e); 55 | }); 56 | } 57 | }); 58 | }; 59 | 60 | /* Detect CSS Animations support to detect element display/re-attach */ 61 | var animation = false, 62 | animationstring = 'animation', 63 | keyframeprefix = '', 64 | animationstartevent = 'animationstart', 65 | domPrefixes = 'Webkit Moz O ms'.split(' '), 66 | startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(' '), 67 | pfx = ''; 68 | { 69 | var elm = document.createElement('fakeelement'); 70 | if( elm.style.animationName !== undefined ) { animation = true; } 71 | 72 | if( animation === false ) { 73 | for( var i = 0; i < domPrefixes.length; i++ ) { 74 | if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { 75 | pfx = domPrefixes[ i ]; 76 | animationstring = pfx + 'Animation'; 77 | keyframeprefix = '-' + pfx.toLowerCase() + '-'; 78 | animationstartevent = startEvents[ i ]; 79 | animation = true; 80 | break; 81 | } 82 | } 83 | } 84 | } 85 | 86 | var animationName = 'resizeanim'; 87 | var animationKeyframes = '@' + keyframeprefix + 'keyframes ' + animationName + ' { from { opacity: 0; } to { opacity: 0; } } '; 88 | var animationStyle = keyframeprefix + 'animation: 1ms ' + animationName + '; '; 89 | } 90 | 91 | function createStyles() { 92 | if (!stylesCreated) { 93 | //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360 94 | var css = (animationKeyframes ? animationKeyframes : '') + 95 | '.resize-triggers { ' + (animationStyle ? animationStyle : '') + 'visibility: hidden; opacity: 0; } ' + 96 | '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }', 97 | head = document.head || document.getElementsByTagName('head')[0], 98 | style = document.createElement('style'); 99 | 100 | style.type = 'text/css'; 101 | if (style.styleSheet) { 102 | style.styleSheet.cssText = css; 103 | } else { 104 | style.appendChild(document.createTextNode(css)); 105 | } 106 | 107 | head.appendChild(style); 108 | stylesCreated = true; 109 | } 110 | } 111 | 112 | window.addResizeListener = function(element, fn){ 113 | if (attachEvent) element.attachEvent('onresize', fn); 114 | else { 115 | if (!element.__resizeTriggers__) { 116 | if (getComputedStyle(element).position == 'static') element.style.position = 'relative'; 117 | createStyles(); 118 | element.__resizeLast__ = {}; 119 | element.__resizeListeners__ = []; 120 | (element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers'; 121 | element.__resizeTriggers__.innerHTML = '
' + 122 | '
'; 123 | element.appendChild(element.__resizeTriggers__); 124 | resetTriggers(element); 125 | element.addEventListener('scroll', scrollListener, true); 126 | 127 | /* Listen for a css animation to detect element display/re-attach */ 128 | animationstartevent && element.__resizeTriggers__.addEventListener(animationstartevent, function(e) { 129 | if(e.animationName == animationName) 130 | resetTriggers(element); 131 | }); 132 | } 133 | element.__resizeListeners__.push(fn); 134 | } 135 | }; 136 | 137 | window.removeResizeListener = function(element, fn){ 138 | if (attachEvent) element.detachEvent('onresize', fn); 139 | else { 140 | element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); 141 | if (!element.__resizeListeners__.length) { 142 | element.removeEventListener('scroll', scrollListener); 143 | element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); 144 | } 145 | } 146 | } 147 | })(); -------------------------------------------------------------------------------- /jquery.resize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Detect Element Resize Plugin for jQuery 3 | * 4 | * https://github.com/sdecima/javascript-detect-element-resize 5 | * Sebastian Decima 6 | * 7 | * version: 0.5.3 8 | **/ 9 | 10 | (function ( $ ) { 11 | var attachEvent = document.attachEvent, 12 | stylesCreated = false; 13 | 14 | var jQuery_resize = $.fn.resize; 15 | 16 | $.fn.resize = function(callback) { 17 | return this.each(function() { 18 | if(this == window) 19 | jQuery_resize.call(jQuery(this), callback); 20 | else 21 | addResizeListener(this, callback); 22 | }); 23 | } 24 | 25 | $.fn.removeResize = function(callback) { 26 | return this.each(function() { 27 | removeResizeListener(this, callback); 28 | }); 29 | } 30 | 31 | if (!attachEvent) { 32 | var requestFrame = (function(){ 33 | var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || 34 | function(fn){ return window.setTimeout(fn, 20); }; 35 | return function(fn){ return raf(fn); }; 36 | })(); 37 | 38 | var cancelFrame = (function(){ 39 | var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || 40 | window.clearTimeout; 41 | return function(id){ return cancel(id); }; 42 | })(); 43 | 44 | function resetTriggers(element){ 45 | var triggers = element.__resizeTriggers__, 46 | expand = triggers.firstElementChild, 47 | contract = triggers.lastElementChild, 48 | expandChild = expand.firstElementChild; 49 | contract.scrollLeft = contract.scrollWidth; 50 | contract.scrollTop = contract.scrollHeight; 51 | expandChild.style.width = expand.offsetWidth + 1 + 'px'; 52 | expandChild.style.height = expand.offsetHeight + 1 + 'px'; 53 | expand.scrollLeft = expand.scrollWidth; 54 | expand.scrollTop = expand.scrollHeight; 55 | }; 56 | 57 | function checkTriggers(element){ 58 | return element.offsetWidth != element.__resizeLast__.width || 59 | element.offsetHeight != element.__resizeLast__.height; 60 | } 61 | 62 | function scrollListener(e){ 63 | var element = this; 64 | resetTriggers(this); 65 | if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__); 66 | this.__resizeRAF__ = requestFrame(function(){ 67 | if (checkTriggers(element)) { 68 | element.__resizeLast__.width = element.offsetWidth; 69 | element.__resizeLast__.height = element.offsetHeight; 70 | element.__resizeListeners__.forEach(function(fn){ 71 | fn.call(element, e); 72 | }); 73 | } 74 | }); 75 | }; 76 | 77 | /* Detect CSS Animations support to detect element display/re-attach */ 78 | var animation = false, 79 | animationstring = 'animation', 80 | keyframeprefix = '', 81 | animationstartevent = 'animationstart', 82 | domPrefixes = 'Webkit Moz O ms'.split(' '), 83 | startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(' '), 84 | pfx = ''; 85 | { 86 | var elm = document.createElement('fakeelement'); 87 | if( elm.style.animationName !== undefined ) { animation = true; } 88 | 89 | if( animation === false ) { 90 | for( var i = 0; i < domPrefixes.length; i++ ) { 91 | if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { 92 | pfx = domPrefixes[ i ]; 93 | animationstring = pfx + 'Animation'; 94 | keyframeprefix = '-' + pfx.toLowerCase() + '-'; 95 | animationstartevent = startEvents[ i ]; 96 | animation = true; 97 | break; 98 | } 99 | } 100 | } 101 | } 102 | 103 | var animationName = 'resizeanim'; 104 | var animationKeyframes = '@' + keyframeprefix + 'keyframes ' + animationName + ' { from { opacity: 0; } to { opacity: 0; } } '; 105 | var animationStyle = keyframeprefix + 'animation: 1ms ' + animationName + '; '; 106 | } 107 | 108 | function createStyles() { 109 | if (!stylesCreated) { 110 | //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360 111 | var css = (animationKeyframes ? animationKeyframes : '') + 112 | '.resize-triggers { ' + (animationStyle ? animationStyle : '') + 'visibility: hidden; opacity: 0; } ' + 113 | '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }', 114 | head = document.head || document.getElementsByTagName('head')[0], 115 | style = document.createElement('style'); 116 | 117 | style.type = 'text/css'; 118 | if (style.styleSheet) { 119 | style.styleSheet.cssText = css; 120 | } else { 121 | style.appendChild(document.createTextNode(css)); 122 | } 123 | 124 | head.appendChild(style); 125 | stylesCreated = true; 126 | } 127 | } 128 | 129 | window.addResizeListener = function(element, fn){ 130 | if (attachEvent) element.attachEvent('onresize', fn); 131 | else { 132 | if (!element.__resizeTriggers__) { 133 | if (getComputedStyle(element).position == 'static') element.style.position = 'relative'; 134 | createStyles(); 135 | element.__resizeLast__ = {}; 136 | element.__resizeListeners__ = []; 137 | (element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers'; 138 | element.__resizeTriggers__.innerHTML = '
' + 139 | '
'; 140 | element.appendChild(element.__resizeTriggers__); 141 | resetTriggers(element); 142 | element.addEventListener('scroll', scrollListener, true); 143 | 144 | /* Listen for a css animation to detect element display/re-attach */ 145 | animationstartevent && element.__resizeTriggers__.addEventListener(animationstartevent, function(e) { 146 | if(e.animationName == animationName) 147 | resetTriggers(element); 148 | }); 149 | } 150 | element.__resizeListeners__.push(fn); 151 | } 152 | }; 153 | 154 | window.removeResizeListener = function(element, fn){ 155 | if (attachEvent) element.detachEvent('onresize', fn); 156 | else { 157 | element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); 158 | if (!element.__resizeListeners__.length) { 159 | element.removeEventListener('scroll', scrollListener); 160 | element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); 161 | } 162 | } 163 | } 164 | }( jQuery )); --------------------------------------------------------------------------------