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.
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]
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 isOffset = function(el){
31 return styleString(el, 'position') != 'static' || isBody(el);
34 var isOffsetStatic = function(el){
35 return isOffset(el) || (/^(?:table|td|th)$/i).test(el.tagName);
38 var computeSize = function(v){
39 return parseFloat(v.match(/([0-9.?]{1,})/));
44 scrollTo: function(x, y){
46 this.getWindow().scrollTo(x, y);
55 if (isBody(this)) return this.getWindow().getSize();
57 // This if clause is because IE8- cannot calculate getBoundingClientRect of elements with hidden visibility.
58 if (!document.addEventListener) return {x: this.offsetWidth, y: this.offsetHeight};
60 // This section inside the `if == true` can be removed when FF fixed the svg size bug.
61 // The same applies to the computeSize function at L38 `var computeSize = function(v){`.
62 if (this.get('tag') == 'svg'){
64 var gCS = window.getComputedStyle(this), bounds;
66 height: ['height', 'paddingTop', 'paddingBottom', 'borderTopWidth', 'borderBottomWidth'],
67 width: ['width', 'paddingLeft', 'paddingRight', 'borderLeftWidth', 'borderRightWidth']
69 bounds = {x: 0, y: 0};
70 gCScomponents.height.each(function(css){
71 bounds.y += computeSize(gCS[css]);
73 gCScomponents.width.each(function(css){
74 bounds.x += computeSize(gCS[css]);
79 bounds = this.getBoundingClientRect();
80 return {x: bounds.width, y: bounds.height};
84 getScrollSize: function(){
85 if (isBody(this)) return this.getWindow().getScrollSize();
86 return {x: this.scrollWidth, y: this.scrollHeight};
89 getScroll: function(){
90 if (isBody(this)) return this.getWindow().getScroll();
91 return {x: this.scrollLeft, y: this.scrollTop};
94 getScrolls: function(){
95 var element = this.parentNode, position = {x: 0, y: 0};
96 while (element && !isBody(element)){
97 position.x += element.scrollLeft;
98 position.y += element.scrollTop;
99 element = element.parentNode;
104 getOffsetParent: brokenOffsetParent ? function(){
106 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
108 var isOffsetCheck = (styleString(element, 'position') == 'static') ? isOffsetStatic : isOffset;
109 while ((element = element.parentNode)){
110 if (isOffsetCheck(element)) return element;
115 if (isBody(element) || styleString(element, 'position') == 'fixed') return null;
118 return element.offsetParent;
123 getOffsets: function(){
124 var hasGetBoundingClientRect = this.getBoundingClientRect;
126 hasGetBoundingClientRect = hasGetBoundingClientRect && !Browser.Platform.ios
128 if (hasGetBoundingClientRect){
129 var bound = this.getBoundingClientRect(),
130 html = document.id(this.getDocument().documentElement),
131 htmlScroll = html.getScroll(),
132 elemScrolls = this.getScrolls(),
133 isFixed = (styleString(this, 'position') == 'fixed');
136 x: bound.left.toInt() + elemScrolls.x + ((isFixed) ? 0 : htmlScroll.x) - html.clientLeft,
137 y: bound.top.toInt() + elemScrolls.y + ((isFixed) ? 0 : htmlScroll.y) - html.clientTop
141 var element = this, position = {x: 0, y: 0};
142 if (isBody(this)) return position;
144 while (element && !isBody(element)){
145 position.x += element.offsetLeft;
146 position.y += element.offsetTop;
148 if (Browser.firefox){
149 if (!borderBox(element)){
150 position.x += leftBorder(element);
151 position.y += topBorder(element);
153 var parent = element.parentNode;
154 if (parent && styleString(parent, 'overflow') != 'visible'){
155 position.x += leftBorder(parent);
156 position.y += topBorder(parent);
158 } else if (element != this && Browser.safari){
159 position.x += leftBorder(element);
160 position.y += topBorder(element);
163 element = element.offsetParent;
166 if (Browser.firefox && !borderBox(this)){
167 position.x -= leftBorder(this);
168 position.y -= topBorder(this);
174 getPosition: function(relative){
175 var offset = this.getOffsets(),
176 scroll = this.getScrolls();
178 x: offset.x - scroll.x,
179 y: offset.y - scroll.y
182 if (relative && (relative = document.id(relative))){
183 var relativePosition = relative.getPosition();
184 return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)};
189 getCoordinates: function(element){
190 if (isBody(this)) return this.getWindow().getCoordinates();
191 var position = this.getPosition(element),
192 size = this.getSize();
199 obj.right = obj.left + obj.width;
200 obj.bottom = obj.top + obj.height;
204 computePosition: function(obj){
206 left: obj.x - styleNumber(this, 'margin-left'),
207 top: obj.y - styleNumber(this, 'margin-top')
211 setPosition: function(obj){
212 return this.setStyles(this.computePosition(obj));
218 [Document, Window].invoke('implement', {
221 var doc = getCompatElement(this);
222 return {x: doc.clientWidth, y: doc.clientHeight};
225 getScroll: function(){
226 var win = this.getWindow(), doc = getCompatElement(this);
227 return {x: win.pageXOffset || doc.scrollLeft, y: win.pageYOffset || doc.scrollTop};
230 getScrollSize: function(){
231 var doc = getCompatElement(this),
232 min = this.getSize(),
233 body = this.getDocument().body;
235 return {x: Math.max(doc.scrollWidth, body.scrollWidth, min.x), y: Math.max(doc.scrollHeight, body.scrollHeight, min.y)};
238 getPosition: function(){
242 getCoordinates: function(){
243 var size = this.getSize();
244 return {top: 0, left: 0, bottom: size.y, right: size.x, height: size.y, width: size.x};
251 var styleString = Element.getComputedStyle;
253 function styleNumber(element, style){
254 return styleString(element, style).toInt() || 0;
257 function borderBox(element){
258 return styleString(element, '-moz-box-sizing') == 'border-box';
261 function topBorder(element){
262 return styleNumber(element, 'border-top-width');
265 function leftBorder(element){
266 return styleNumber(element, 'border-left-width');
269 function isBody(element){
270 return (/^(?:body|html)$/i).test(element.tagName);
273 function getCompatElement(element){
274 var doc = element.getDocument();
275 return (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
281 Element.alias({position: 'setPosition'}); //compatability
283 [Window, Document, Element].invoke('implement', {
285 getHeight: function(){
286 return this.getSize().y;
289 getWidth: function(){
290 return this.getSize().x;
293 getScrollTop: function(){
294 return this.getScroll().y;
297 getScrollLeft: function(){
298 return this.getScroll().x;
301 getScrollHeight: function(){
302 return this.getScrollSize().y;
305 getScrollWidth: function(){
306 return this.getScrollSize().x;
310 return this.getPosition().y;
314 return this.getPosition().x;