diff --git a/README.md b/README.md index 73278b4..d767f77 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,20 @@ jQuery plugin library usage ``` +ES6 module usage +---------------- +```javascript +import Resize from 'detect-element-resize'; + +var resizeElement = document.getElementById('resizeElement'), + resizeCallback = function() { + /* do something */ + }; + +Resize.addResizeListener(resizeElement, resizeCallback); +Resize.removeResizeListener(resizeElement, resizeCallback); +``` + Compatibility ------------- Works great on: diff --git a/detect-element-resize.es6.js b/detect-element-resize.es6.js new file mode 100644 index 0000000..2fb5415 --- /dev/null +++ b/detect-element-resize.es6.js @@ -0,0 +1,158 @@ +/** +* Detect Element Resize +* +* https://github.com/sdecima/javascript-detect-element-resize +* Sebastian Decima +* +* version: 0.5.3 +**/ + +var attachEvent = document.attachEvent, + stylesCreated = false; + +var resetTriggers, checkTriggers, scrollListener; + +if (!attachEvent) { + var requestFrame = (function(){ + var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || + function(fn){ return window.setTimeout(fn, 20); }; + return function(fn){ return raf(fn); }; + })(); + + var cancelFrame = (function(){ + var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || + window.clearTimeout; + return function(id){ return cancel(id); }; + })(); + + resetTriggers = function resetTriggers(element){ + var triggers = element.__resizeTriggers__, + expand = triggers.firstElementChild, + contract = triggers.lastElementChild, + expandChild = expand.firstElementChild; + contract.scrollLeft = contract.scrollWidth; + contract.scrollTop = contract.scrollHeight; + expandChild.style.width = expand.offsetWidth + 1 + 'px'; + expandChild.style.height = expand.offsetHeight + 1 + 'px'; + expand.scrollLeft = expand.scrollWidth; + expand.scrollTop = expand.scrollHeight; + } + + checkTriggers = function checkTriggers(element){ + return element.offsetWidth != element.__resizeLast__.width || + element.offsetHeight != element.__resizeLast__.height; + } + + scrollListener = function scrollListener(e){ + var element = this; + resetTriggers(this); + if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__); + this.__resizeRAF__ = requestFrame(function(){ + if (checkTriggers(element)) { + element.__resizeLast__.width = element.offsetWidth; + element.__resizeLast__.height = element.offsetHeight; + element.__resizeListeners__.forEach(function(fn){ + fn.call(element, e); + }); + } + }); + } + + /* Detect CSS Animations support to detect element display/re-attach */ + var animation = false, + animationstring = 'animation', + keyframeprefix = '', + animationstartevent = 'animationstart', + domPrefixes = 'Webkit Moz O ms'.split(' '), + startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(' '), + pfx = ''; + { + var elm = document.createElement('fakeelement'); + if( elm.style.animationName !== undefined ) { animation = true; } + + if( animation === false ) { + for( var i = 0; i < domPrefixes.length; i++ ) { + if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { + pfx = domPrefixes[ i ]; + animationstring = pfx + 'Animation'; + keyframeprefix = '-' + pfx.toLowerCase() + '-'; + animationstartevent = startEvents[ i ]; + animation = true; + break; + } + } + } + } + + var animationName = 'resizeanim'; + var animationKeyframes = '@' + keyframeprefix + 'keyframes ' + animationName + ' { from { opacity: 0; } to { opacity: 0; } } '; + var animationStyle = keyframeprefix + 'animation: 1ms ' + animationName + '; '; +} + +function createStyles() { + if (!stylesCreated) { + //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360 + var css = (animationKeyframes ? animationKeyframes : '') + + '.resize-triggers { ' + (animationStyle ? animationStyle : '') + 'visibility: hidden; opacity: 0; } ' + + '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; z-index: -1; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }', + head = document.head || document.getElementsByTagName('head')[0], + style = document.createElement('style'); + + style.type = 'text/css'; + if (style.styleSheet) { + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } + + head.appendChild(style); + stylesCreated = true; + } +} + +const Resize = { + addResizeListener: function(element, fn){ + if (attachEvent) element.attachEvent('onresize', fn); + else { + if (!element.__resizeTriggers__) { + if (getComputedStyle(element).position == 'static') element.style.position = 'relative'; + createStyles(); + element.__resizeLast__ = {}; + element.__resizeListeners__ = []; + (element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers'; + element.__resizeTriggers__.innerHTML = '
' + + '
'; + element.appendChild(element.__resizeTriggers__); + resetTriggers(element); + element.addEventListener('scroll', scrollListener, true); + + /* Listen for a css animation to detect element display/re-attach */ + if (animationstartevent) { + element.__resizeTriggers__.animationStartListener = function (e) { + if (e.animationName == animationName) { + resetTriggers(element); + } + } + + element.__resizeTriggers__.addEventListener(animationstartevent, element.__resizeTriggers__.animationStartListener); + } + } + element.__resizeListeners__.push(fn); + } + }, + removeResizeListener: function(element, fn){ + if (attachEvent) element.detachEvent('onresize', fn); + else { + element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); + if (!element.__resizeListeners__.length) { + element.removeEventListener('scroll', scrollListener, true); + if (animationstartevent) { + element.__resizeTriggers__.removeEventListener(animationstartevent, element.__resizeTriggers__.animationStartListener); + } + element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); + } + } + } +}; + +export default Resize; diff --git a/detect-element-resize.js b/detect-element-resize.js index cbb3c02..9a6cdb7 100644 --- a/detect-element-resize.js +++ b/detect-element-resize.js @@ -35,7 +35,7 @@ expandChild.style.height = expand.offsetHeight + 1 + 'px'; expand.scrollLeft = expand.scrollWidth; expand.scrollTop = expand.scrollHeight; - }; + } function checkTriggers(element){ return element.offsetWidth != element.__resizeLast__.width || @@ -55,7 +55,7 @@ }); } }); - }; + } /* Detect CSS Animations support to detect element display/re-attach */ var animation = false, @@ -71,11 +71,11 @@ if( animation === false ) { for( var i = 0; i < domPrefixes.length; i++ ) { - if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { - pfx = domPrefixes[ i ]; + if( elm.style[ domPrefixes[i] + 'AnimationName'] !== undefined) { + pfx = domPrefixes[i]; animationstring = pfx + 'Animation'; keyframeprefix = '-' + pfx.toLowerCase() + '-'; - animationstartevent = startEvents[ i ]; + animationstartevent = startEvents[i]; animation = true; break; } @@ -93,7 +93,7 @@ //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360 var css = (animationKeyframes ? animationKeyframes : '') + '.resize-triggers { ' + (animationStyle ? animationStyle : '') + 'visibility: hidden; opacity: 0; } ' + - '.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%; }', + '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; z-index: -1; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }', head = document.head || document.getElementsByTagName('head')[0], style = document.createElement('style'); @@ -108,7 +108,7 @@ stylesCreated = true; } } - + window.addResizeListener = function(element, fn){ if (attachEvent) element.attachEvent('onresize', fn); else { @@ -125,10 +125,15 @@ element.addEventListener('scroll', scrollListener, true); /* Listen for a css animation to detect element display/re-attach */ - animationstartevent && element.__resizeTriggers__.addEventListener(animationstartevent, function(e) { - if(e.animationName == animationName) - resetTriggers(element); - }); + if (animationstartevent) { + element.__resizeTriggers__.animationStartListener = function (e) { + if (e.animationName == animationName) { + resetTriggers(element); + } + } + + element.__resizeTriggers__.addEventListener(animationstartevent, element.__resizeTriggers__.animationStartListener); + } } element.__resizeListeners__.push(fn); } @@ -139,9 +144,12 @@ else { element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); if (!element.__resizeListeners__.length) { - element.removeEventListener('scroll', scrollListener); - element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); + element.removeEventListener('scroll', scrollListener, true); + if (animationstartevent) { + element.__resizeTriggers__.removeEventListener(animationstartevent, element.__resizeTriggers__.animationStartListener); + } + element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); } } - } + }; })(); \ No newline at end of file diff --git a/jquery.resize.js b/jquery.resize.js index ba42a80..d570c33 100644 --- a/jquery.resize.js +++ b/jquery.resize.js @@ -20,13 +20,13 @@ else addResizeListener(this, callback); }); - } + }; $.fn.removeResize = function(callback) { return this.each(function() { removeResizeListener(this, callback); }); - } + }; if (!attachEvent) { var requestFrame = (function(){ @@ -52,7 +52,7 @@ expandChild.style.height = expand.offsetHeight + 1 + 'px'; expand.scrollLeft = expand.scrollWidth; expand.scrollTop = expand.scrollHeight; - }; + } function checkTriggers(element){ return element.offsetWidth != element.__resizeLast__.width || @@ -72,7 +72,7 @@ }); } }); - }; + } /* Detect CSS Animations support to detect element display/re-attach */ var animation = false, @@ -88,11 +88,11 @@ if( animation === false ) { for( var i = 0; i < domPrefixes.length; i++ ) { - if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { - pfx = domPrefixes[ i ]; + if( elm.style[ domPrefixes[i] + 'AnimationName'] !== undefined) { + pfx = domPrefixes[i]; animationstring = pfx + 'Animation'; keyframeprefix = '-' + pfx.toLowerCase() + '-'; - animationstartevent = startEvents[ i ]; + animationstartevent = startEvents[i]; animation = true; break; } @@ -110,7 +110,7 @@ //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360 var css = (animationKeyframes ? animationKeyframes : '') + '.resize-triggers { ' + (animationStyle ? animationStyle : '') + 'visibility: hidden; opacity: 0; } ' + - '.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%; }', + '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; z-index: -1; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }', head = document.head || document.getElementsByTagName('head')[0], style = document.createElement('style'); @@ -125,7 +125,7 @@ stylesCreated = true; } } - + window.addResizeListener = function(element, fn){ if (attachEvent) element.attachEvent('onresize', fn); else { @@ -140,12 +140,17 @@ element.appendChild(element.__resizeTriggers__); resetTriggers(element); element.addEventListener('scroll', scrollListener, true); - + /* Listen for a css animation to detect element display/re-attach */ - animationstartevent && element.__resizeTriggers__.addEventListener(animationstartevent, function(e) { - if(e.animationName == animationName) - resetTriggers(element); - }); + if (animationstartevent) { + element.__resizeTriggers__.animationStartListener = function (e) { + if (e.animationName == animationName) { + resetTriggers(element); + } + } + + element.__resizeTriggers__.addEventListener(animationstartevent, element.__resizeTriggers__.animationStartListener); + } } element.__resizeListeners__.push(fn); } @@ -156,9 +161,12 @@ else { element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); if (!element.__resizeListeners__.length) { - element.removeEventListener('scroll', scrollListener); - element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); + element.removeEventListener('scroll', scrollListener, true); + if (animationstartevent) { + element.__resizeTriggers__.removeEventListener(animationstartevent, element.__resizeTriggers__.animationStartListener); + } + element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); } } - } + }; }( jQuery )); \ No newline at end of file diff --git a/package.json b/package.json index 67c06b8..55002b3 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "javascript-detect-element-resize", "version": "0.5.3", "description": "A Cross-Browser, Event-based, Element Resize Detection", - "main": "detect-element-resize.js", + "main": "detect-element-resize.es6.js", "directories": { "test": "tests" }, diff --git a/tests/tests-javascript.es6.html b/tests/tests-javascript.es6.html new file mode 100644 index 0000000..7fe52e3 --- /dev/null +++ b/tests/tests-javascript.es6.html @@ -0,0 +1,16 @@ + + + + + javascript-detect-element-resize Tests + + + + +
+
+ + + + + diff --git a/tests/tests-javascript.es6.js b/tests/tests-javascript.es6.js new file mode 100644 index 0000000..6e1d471 --- /dev/null +++ b/tests/tests-javascript.es6.js @@ -0,0 +1,93 @@ +import Resize from '../detect-element-resize.es6.js'; +const {addResizeListener, removeResizeListener} = Resize; + +var container, element, content; + +QUnit.module('main', { + setup: function() { + var fixture = '
'; + $("#qunit-fixture").append(fixture); + + container = document.getElementById('container'); + element = document.getElementById('resizable-element'); + content = document.getElementById('content'); + + $('#container').hide(); + addResizeListener(element, detectCallback); + $('#container').show(); + shouldDetect = true; + detected = false; + }, + teardown: function() { + $('#styleTest').remove(); + try { + removeResizeListener(element, detectCallback); + } catch(e) {} + } +}); + +var newWidth = 0, newHeight = 0, shouldDetect = true, detected = false; +var detectCallback = function() { + detected = true; +}; + +var validateEvent = function(assert) { + setTimeout(function() { + if(shouldDetect) { + assert.ok(shouldDetect === true && detected === true, 'resize event fired OK'); + } + assert.ok($(content).width() == newWidth, 'Resize OK'); + + QUnit.start(); + }, 2000); +}; + +QUnit.asyncTest( "JS addResizeListener css resize test", function( assert ) { + expect( 2 ); + + newWidth = 100; + + var myCss = ''; + $('head').append(myCss); + + validateEvent(assert); +}); + +QUnit.asyncTest( "JS addResizeListener script resize test", function( assert ) { + expect( 2 ); + + newWidth = 30; + + $(content).width(newWidth); + + validateEvent(assert); +}); + +QUnit.asyncTest( "JS addResizeListener script reattach element test", function( assert ) { + expect( 2 ); + + var elem = $(content).detach(); + + setTimeout(function() { + $(container).append("div").append(elem); + //elem.appendTo(container); + newWidth = 68; + $(content).width(newWidth); + }, 500); + + validateEvent(assert); +}); + +QUnit.asyncTest( "JS removeResizeListener test", function( assert ) { + expect( 1 ); + + newWidth = 0; + shouldDetect = false; + + removeResizeListener(element, detectCallback); + + $(content).width(newWidth); + $(content).height(0); + + validateEvent(assert); +}); diff --git a/tests/tests-javascript.html b/tests/tests-javascript.html index dd0f1ae..4edcdf8 100644 --- a/tests/tests-javascript.html +++ b/tests/tests-javascript.html @@ -3,15 +3,15 @@ javascript-detect-element-resize Tests - +
- - + + - \ No newline at end of file + diff --git a/tests/tests-jquery.html b/tests/tests-jquery.html index 91fd694..72cc6bf 100644 --- a/tests/tests-jquery.html +++ b/tests/tests-jquery.html @@ -3,15 +3,15 @@ javascript-detect-element-resize Tests - +
- - + + - \ No newline at end of file +