Skip to content

Commit 90888f1

Browse files
committed
Changed to work with scroll-events
Changed to work with scroll-events, following the new implementation of Back Alley Coder.
1 parent 6e0bdc4 commit 90888f1

File tree

3 files changed

+190
-301
lines changed

3 files changed

+190
-301
lines changed

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ javascript-detect-element-resize
44
A Cross-Browser, Event-based, Element Resize Detection.
55

66
In short, this implementation does NOT use an internal timer to detect size changes (as most implementations I found do).
7-
It uses [overflow and underflow events][2] on most browsers, and the ['onresize' event][5] on IE10 and below.
7+
It uses `scroll` events on most browsers, and the [`onresize` event][5] on IE10 and below.
88

9-
NOTE: On IE11, due to lack of support for the above events, it only detects changes through a MutationObserver; i.e. only javascript generated resize changes and not CSS pseudo classes e.g. :hover, CSS animations, etc.
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.
1010

1111
About the libraries
1212
===================
@@ -15,11 +15,9 @@ I was searching for a library that allowed me to detect when an DOM element chan
1515
1. only available as jQuery libraries (so no standalone Javascript)
1616
2. all had terrible performance (because all of them use timers to intermittently poll the size of the elements to detect a change).
1717

18-
Then I came across this [great post][1] on [Back Alley Coder][3] about using [overflow and underflow 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).
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).
1919

20-
On the other hand, most new browsers implement [WC3 DOM4 MutationObservers][4], which allows to observe property changes and hence indirectly watch for resize changes.
21-
22-
The libraries on this repository are just a concrete implementation of a mixture of the above technologies/techniques.
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).
2321

2422
Libraries
2523
=========
@@ -60,12 +58,7 @@ Works great on:
6058

6159
- Chrome
6260
- Firefox
63-
- IE 10 and below (tested on 10, 9, 8 and 7)
64-
65-
Works but limited:
66-
67-
- IE 11: due to lack of onresize/overflow events, it only detects changes through a MutationObserver;
68-
i.e. only javascript generated resize changes and not CSS pseudo classes e.g. :hover, CSS animations, etc.
61+
- IE 11 and below (tested on 11, 10, 9, 8 and 7)
6962

7063
Doesn't work on:
7164

@@ -81,10 +74,16 @@ TODO
8174

8275
Release Notes
8376
=============
77+
v0.5
78+
----
79+
80+
- It is now fully compatible with IE11.
81+
- 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.
82+
8483
v0.4.1
8584
----
8685

87-
- Fix for jQuery 'resize' method overlapping
86+
- Fix for jQuery 'resize' method overlapping.
8887

8988
v0.4
9089
----
@@ -106,8 +105,8 @@ v0.2
106105
v0.1
107106
----
108107

109-
- Implementation based on the [works][1] of [Back Alley Coder][3]
110-
- Adds jQuery plugin version
108+
- Implementation based on the [works][1] of [Back Alley Coder][3].
109+
- Adds jQuery plugin version.
111110

112111

113112
References
@@ -132,3 +131,4 @@ External links
132131
[3]: http://www.backalleycoder.com/
133132
[4]: http://www.w3.org/TR/dom/#mutation-observers
134133
[5]: http://msdn.microsoft.com/en-us/library/ie/ms536959
134+

detect-element-resize.js

Lines changed: 89 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -4,174 +4,104 @@
44
* https://github.com/sdecima/javascript-detect-element-resize
55
* Sebastian Decima
66
*
7-
* version: 0.4.1
7+
* version: 0.5
88
**/
99

10-
(function ( $ ) {
11-
var is_above_ie10 = !(window.ActiveXObject) && "ActiveXObject" in window;
12-
var supports_mutation_observer = 'MutationObserver' in window;
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+
})();
1326

14-
function addFlowListener(element, type, fn){
15-
var flow = type == 'over';
16-
element.addEventListener('OverflowEvent' in window ? 'overflowchanged' : type + 'flow', function(e){
17-
if (e.type == (type + 'flow') ||
18-
((e.orient == 0 && e.horizontalOverflow == flow) ||
19-
(e.orient == 1 && e.verticalOverflow == flow) ||
20-
(e.orient == 2 && e.horizontalOverflow == flow && e.verticalOverflow == flow))) {
21-
e.flow = type;
22-
return fn.call(this, e);
23-
}
24-
}, false);
25-
};
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+
};
2639

27-
function newResizeMutationObserverCallback(element, fn) {
28-
var oldWidth = element.clientWidth,
29-
oldHeight = element.clientHeight;
30-
return function() {
31-
if(oldWidth != element.clientWidth || oldHeight != element.clientHeight) {
32-
oldWidth = element.clientWidth;
33-
oldHeight = element.clientHeight;
34-
fn.call(element);
35-
}
40+
function checkTriggers(element){
41+
return element.offsetWidth != element.__resizeLast__.width ||
42+
element.offsetHeight != element.__resizeLast__.height;
3643
}
37-
}
38-
39-
function addResizeMutationObserver(element, fn){
40-
var observer = new MutationObserver(newResizeMutationObserverCallback(element, fn));
41-
observer.observe(element, {
42-
attributes: true,
43-
subtree: true,
44-
attributeFilter: ['style']
45-
});
46-
return observer;
47-
};
48-
49-
function fireEvent(element, type, data, options){
50-
var options = options || {},
51-
event = document.createEvent('Event');
52-
event.initEvent(type, 'bubbles' in options ? options.bubbles : true, 'cancelable' in options ? options.cancelable : true);
53-
for (var z in data) event[z] = data[z];
54-
element.dispatchEvent(event);
55-
};
56-
57-
function addResizeListener(element, fn){
58-
if (is_above_ie10 && supports_mutation_observer) {
59-
fn._mutationObserver = addResizeMutationObserver(element, fn);
60-
var events = element._mutationObservers || (element._mutationObservers = []);
61-
if (indexOf.call(events, fn) == -1) events.push(fn);
62-
} else {
63-
var supports_onresize = 'onresize' in element;
64-
if (!supports_onresize && !element._resizeSensor) {
65-
var sensor_style = 'position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; z-index: -1;';
66-
var sensor = element._resizeSensor = document.createElement('div');
67-
sensor.className = 'resize-sensor';
68-
sensor.innerHTML = '<div class="resize-overflow" style="' + sensor_style
69-
+ '"><div></div></div><div class="resize-underflow" style="' + sensor_style
70-
+ '"><div></div></div>';
71-
72-
var x = 0, y = 0,
73-
first = sensor.firstElementChild.firstChild,
74-
last = sensor.lastElementChild.firstChild,
75-
matchFlow = function(event){
76-
var change = false,
77-
width = element.offsetWidth;
78-
if (x != width) {
79-
first.style.width = width - 1 + 'px';
80-
last.style.width = width + 1 + 'px';
81-
change = true;
82-
x = width;
83-
}
84-
var height = element.offsetHeight;
85-
if (y != height) {
86-
first.style.height = height - 1 + 'px';
87-
last.style.height = height + 1 + 'px';
88-
change = true;
89-
y = height;
90-
}
91-
if (change && event.currentTarget != element) fireEvent(element, 'resize');
92-
};
93-
94-
if (getComputedStyle(element).position == 'static'){
95-
element.style.position = 'relative';
96-
element._resizeSensor._resetPosition = true;
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+
});
9756
}
98-
addFlowListener(sensor, 'over', matchFlow);
99-
addFlowListener(sensor, 'under', matchFlow);
100-
addFlowListener(sensor.firstElementChild, 'over', matchFlow);
101-
addFlowListener(sensor.lastElementChild, 'under', matchFlow);
102-
element.appendChild(sensor);
103-
matchFlow({});
57+
});
58+
};
59+
}
60+
61+
function createStyles() {
62+
if (!stylesCreated) {
63+
var css = '.resize-triggers { visibility: hidden; } .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%; }',
64+
head = document.head || document.getElementsByTagName('head')[0],
65+
style = document.createElement('style');
66+
67+
style.type = 'text/css';
68+
if (style.styleSheet) {
69+
style.styleSheet.cssText = css;
70+
} else {
71+
style.appendChild(document.createTextNode(css));
10472
}
105-
var events = element._flowEvents || (element._flowEvents = []);
106-
if (indexOf.call(events, fn) == -1) events.push(fn);
107-
if (!supports_onresize) element.addEventListener('resize', fn, false);
108-
element.onresize = function(e){
109-
forEach.call(events, function(fn){
110-
fn.call(element, e);
111-
});
112-
};
113-
}
114-
};
11573

116-
function removeResizeListener(element, fn){
117-
if (is_above_ie10 && supports_mutation_observer) {
118-
var index = indexOf.call(element._mutationObservers, fn);
119-
if (index > -1) {
120-
var observer = element._mutationObservers[index]._mutationObserver;
121-
element._mutationObservers.splice(index, 1);
122-
observer.disconnect();
123-
}
124-
} else {
125-
var supports_onresize = 'onresize' in element;
126-
var index = indexOf.call(element._flowEvents, fn);
127-
if (index > -1) element._flowEvents.splice(index, 1);
128-
if (!element._flowEvents.length) {
129-
var sensor = element._resizeSensor;
130-
if (sensor) {
131-
element.removeChild(sensor);
132-
if (sensor._resetPosition) element.style.position = 'static';
133-
try { delete element._resizeSensor; } catch(e) { /* delete arrays not supported on IE 7 and below */}
134-
}
135-
if (supports_onresize) element.onresize = null;
136-
try { delete element._flowEvents; } catch(e) { /* delete arrays not supported on IE 7 and below */}
137-
}
138-
if(!supports_onresize) element.removeEventListener('resize', fn);
74+
head.appendChild(style);
13975
}
140-
};
141-
142-
/* Array.indexOf for IE < 9 */
143-
var indexOf = function(needle) {
144-
if(typeof Array.prototype.indexOf === 'function') {
145-
indexOf = Array.prototype.indexOf;
146-
} else {
147-
indexOf = function(needle) {
148-
var i = -1, index = -1;
149-
for(i = 0; i < this.length; i++) {
150-
if(this[i] === needle) {
151-
index = i;
152-
break;
153-
}
154-
}
155-
return index;
156-
};
76+
}
77+
78+
window.addResizeListener = function(element, fn){
79+
if (attachEvent) element.attachEvent('onresize', fn);
80+
else {
81+
if (!element.__resizeTriggers__) {
82+
if (getComputedStyle(element).position == 'static') element.style.position = 'relative';
83+
createStyles();
84+
element.__resizeLast__ = {};
85+
element.__resizeListeners__ = [];
86+
(element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers';
87+
element.__resizeTriggers__.innerHTML = '<div class="expand-trigger"><div></div></div>' +
88+
'<div class="contract-trigger"></div>';
89+
element.appendChild(element.__resizeTriggers__);
90+
resetTriggers(element);
91+
element.addEventListener('scroll', scrollListener, true);
92+
}
93+
element.__resizeListeners__.push(fn);
15794
}
158-
return indexOf.call(this, needle);
15995
};
160-
161-
/* Array.forEach for IE < 9 */
162-
var forEach = function(action, that) {
163-
if(typeof Array.prototype.forEach === 'function') {
164-
forEach = Array.prototype.forEach;
165-
} else {
166-
forEach = function(action, that) {
167-
for (var i= 0, n= this.length; i<n; i++)
168-
if (i in this)
169-
action.call(that, this[i], i, this);
170-
};
96+
97+
window.removeResizeListener = function(element, fn){
98+
if (attachEvent) element.detachEvent('resize', fn);
99+
else {
100+
element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1);
101+
if (!element.__resizeListeners__.length) {
102+
element.removeEventListener('scroll', scrollListener);
103+
element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__);
104+
}
171105
}
172-
return forEach.call(this, action, that);
173-
};
174-
175-
window.addResizeListener = addResizeListener;
176-
window.removeResizeListener = removeResizeListener;
177-
}());
106+
}
107+
})();

0 commit comments

Comments
 (0)