close param tag
[mootools.git] / Source / Element / Element.Dimensions.js
blob6bae24b12117d854bd8c0149bdce91a6099e8a92
1 /*
2 ---
4 name: Element.Dimensions
6 description: Contains methods to work with size, scroll, or positioning of Elements and the window object.
8 license: MIT-style license.
10 credits:
11   - Element positioning based on the [qooxdoo](http://qooxdoo.org/) code and smart browser fixes, [LGPL License](http://www.gnu.org/licenses/lgpl.html).
12   - Viewport dimensions based on [YUI](http://developer.yahoo.com/yui/) code, [BSD License](http://developer.yahoo.com/yui/license.html).
14 requires: [Element, Element.Style]
16 provides: [Element.Dimensions]
18 ...
21 (function(){
23 var element = document.createElement('div'),
24         child = document.createElement('div');
25 element.style.height = '0';
26 element.appendChild(child);
27 var brokenOffsetParent = (child.offsetParent === element);
28 element = child = null;
30 var heightComponents = ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'],
31         widthComponents = ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth'];
33 var svgCalculateSize = function(el){
35         var gCS = window.getComputedStyle(el),
36                 bounds = {x: 0, y: 0};
38         heightComponents.each(function(css){
39                 bounds.y += parseFloat(gCS[css]);
40         });
41         widthComponents.each(function(css){
42                 bounds.x += parseFloat(gCS[css]);
43         });
44         return bounds;
47 var isOffset = function(el){
48         return styleString(el, 'position') != 'static' || isBody(el);
51 var isOffsetStatic = function(el){
52         return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
55 Element.implement({
57         scrollTo: function(x, y){
58                 if (isBody(this)){
59                         this.getWindow().scrollTo(x, y);
60                 } else {
61                         this.scrollLeft = x;
62                         this.scrollTop = y;
63                 }
64                 return this;
65         },
67         getSize: function(){
68                 if (isBody(this)) return this.getWindow().getSize();
70                 //<ltIE9>
71                 // This if clause is because IE8- cannot calculate getBoundingClientRect of elements with visibility hidden.
72                 if (!window.getComputedStyle) return {x: this.offsetWidth, y: this.offsetHeight};
73                 //</ltIE9>
75                 // This svg section under, calling `svgCalculateSize()`, can be removed when FF fixed the svg size bug.
76                 // Bug info: https://bugzilla.mozilla.org/show_bug.cgi?id=530985
77                 if (this.get('tag') == 'svg') return svgCalculateSize(this);
78                 
79                 var bounds = this.getBoundingClientRect();
80                 return {x: bounds.width, y: bounds.height};
81         },
83         getScrollSize: function(){
84                 if (isBody(this)) return this.getWindow().getScrollSize();
85                 return {x: this.scrollWidth, y: this.scrollHeight};
86         },
88         getScroll: function(){
89                 if (isBody(this)) return this.getWindow().getScroll();
90                 return {x: this.scrollLeft, y: this.scrollTop};
91         },
93         getScrolls: function(){
94                 var element = this.parentNode, position = {x: 0, y: 0};
95                 while (element && !isBody(element)){
96                         position.x += element.scrollLeft;
97                         position.y += element.scrollTop;
98                         element = element.parentNode;
99                 }
100                 return position;
101         },
103         getOffsetParent: brokenOffsetParent ? function(){
104                 var element = this;
105                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
107                 var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
108                 while ((element = element.parentNode)){
109                         if (isOffsetCheck(element)) return element;
110                 }
111                 return null;
112         } : function(){
113                 var element = this;
114                 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
116                 try {
117                         return element.offsetParent;
118                 } catch(e){}
119                 return null;
120         },
122         getOffsets: function(){
123                 var hasGetBoundingClientRect = this.getBoundingClientRect;
124 //<1.4compat>
125                 hasGetBoundingClientRect = hasGetBoundingClientRect && !Browser.Platform.ios
126 //</1.4compat>
127                 if (hasGetBoundingClientRect){
128                         var bound = this.getBoundingClientRect(),
129                                 html = document.id(this.getDocument().documentElement),
130                                 htmlScroll = html.getScroll(),
131                                 elemScrolls = this.getScrolls(),
132                                 isFixed = (styleString(this, 'position') == 'fixed');
134                         return {
135                                 x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
136                                 y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
137                         };
138                 }
140                 var element = this, position = {x: 0, y: 0};
141                 if (isBody(this)) return position;
143                 while (element && !isBody(element)){
144                         position.x += element.offsetLeft;
145                         position.y += element.offsetTop;
146 //<1.4compat>
147                         if (Browser.firefox){
148                                 if (!borderBox(element)){
149                                         position.x += leftBorder(element);
150                                         position.y += topBorder(element);
151                                 }
152                                 var parent = element.parentNode;
153                                 if (parent && styleString(parent, 'overflow') != 'visible'){
154                                         position.x += leftBorder(parent);
155                                         position.y += topBorder(parent);
156                                 }
157                         } else if (element != this && Browser.safari){
158                                 position.x += leftBorder(element);
159                                 position.y += topBorder(element);
160                         }
161 //</1.4compat>
162                         element = element.offsetParent;
163                 }
164 //<1.4compat>
165                 if (Browser.firefox && !borderBox(this)){
166                         position.x -= leftBorder(this);
167                         position.y -= topBorder(this);
168                 }
169 //</1.4compat>
170                 return position;
171         },
173         getPosition: function(relative){
174                 var offset = this.getOffsets(),
175                         scroll = this.getScrolls();
176                 var position = {
177                         x: offset.x - scroll.x,
178                         y: offset.y - scroll.y
179                 };
181                 if (relative && (relative = document.id(relative))){
182                         var relativePosition = relative.getPosition();
183                         return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
184                 }
185                 return position;
186         },
188         getCoordinates: function(element){
189                 if (isBody(this)) return this.getWindow().getCoordinates();
190                 var position = this.getPosition(element),
191                         size = this.getSize();
192                 var obj = {
193                         left: position.x,
194                         top: position.y,
195                         width: size.x,
196                         height: size.y
197                 };
198                 obj.right = obj.left + obj.width;
199                 obj.bottom = obj.top + obj.height;
200                 return obj;
201         },
203         computePosition: function(obj){
204                 return {
205                         left: obj.x - styleNumber(this, 'margin-left'),
206                         top: obj.y - styleNumber(this, 'margin-top')
207                 };
208         },
210         setPosition: function(obj){
211                 return this.setStyles(this.computePosition(obj));
212         }
217 [Document, Window].invoke('implement', {
219         getSize: function(){
220                 var doc = getCompatElement(this);
221                 return {x: doc.clientWidth, y: doc.clientHeight};
222         },
224         getScroll: function(){
225                 var win = this.getWindow(), doc = getCompatElement(this);
226                 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
227         },
229         getScrollSize: function(){
230                 var doc = getCompatElement(this),
231                         min = this.getSize(),
232                         body = this.getDocument().body;
234                 return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
235         },
237         getPosition: function(){
238                 return {x: 0, y: 0};
239         },
241         getCoordinates: function(){
242                 var size = this.getSize();
243                 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
244         }
248 // private methods
250 var styleString = Element.getComputedStyle;
252 function styleNumber(element, style){
253         return styleString(element, style).toInt() || 0;
256 function borderBox(element){
257         return styleString(element, '-moz-box-sizing') == 'border-box';
260 function topBorder(element){
261         return styleNumber(element, 'border-top-width');
264 function leftBorder(element){
265         return styleNumber(element, 'border-left-width');
268 function isBody(element){
269         return (/^(?:body|html)$/i).test(element.tagName);
272 function getCompatElement(element){
273         var doc = element.getDocument();
274         return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
277 })();
279 //aliases
280 Element.alias({position: 'setPosition'}); //compatability
282 [Window, Document, Element].invoke('implement', {
284         getHeight: function(){
285                 return this.getSize().y;
286         },
288         getWidth: function(){
289                 return this.getSize().x;
290         },
292         getScrollTop: function(){
293                 return this.getScroll().y;
294         },
296         getScrollLeft: function(){
297                 return this.getScroll().x;
298         },
300         getScrollHeight: function(){
301                 return this.getScrollSize().y;
302         },
304         getScrollWidth: function(){
305                 return this.getScrollSize().x;
306         },
308         getTop: function(){
309                 return this.getPosition().y;
310         },
312         getLeft: function(){
313                 return this.getPosition().x;
314         }