From cbfb5d7c614822b7b0f28bf73ccb3e6387db8b91 Mon Sep 17 00:00:00 2001 From: Nick Stakenburg Date: Mon, 14 Apr 2008 14:16:56 +0200 Subject: [PATCH] Added Element#cloneDimensions. Using it to fix dimension in Element#clonePosition. Added unit tests for both. Fixed Element#getOffsetParent, IE can't do Element#getStyle on documentnode. Fixed viewportOffset on Opera 9.5. Made Element#getDimensions work with display:none ancestors. --- src/dom.js | 110 ++++++++++++++++++++++++++---------------- test/unit/dom.html | 135 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 193 insertions(+), 52 deletions(-) diff --git a/src/dom.js b/src/dom.js index e9b878c..5bbb1b7 100644 --- a/src/dom.js +++ b/src/dom.js @@ -304,15 +304,15 @@ Element.Methods = { } return element; }, - + getHeight: function(element) { return $(element).getDimensions().height; }, - + getWidth: function(element) { return $(element).getDimensions().width; }, - + classNames: function(element) { return new Element.ClassNames(element); }, @@ -423,30 +423,39 @@ Element.Methods = { (value < 0.00001) ? 0 : value; return element; }, - + getDimensions: function(element) { element = $(element); - var display = element.getStyle('display'); - if (display != 'none' && display != null) // Safari bug - return {width: element.offsetWidth, height: element.offsetHeight}; - - // All *Width and *Height properties give 0 on elements with display none, - // so enable the element temporarily - var els = element.style; - var originalVisibility = els.visibility; - var originalPosition = els.position; - var originalDisplay = els.display; - els.visibility = 'hidden'; - els.position = 'absolute'; - els.display = 'block'; - var originalWidth = element.clientWidth; - var originalHeight = element.clientHeight; - els.display = originalDisplay; - els.position = originalPosition; - els.visibility = originalVisibility; - return {width: originalWidth, height: originalHeight}; + var dimensions = {width: element.clientWidth, height: element.clientHeight}; + + // *Width or *Height properties can return 0 on elements with display none, + // or when ancestors have display none, so enable those temporarily + if (dimensions.width == 0 || dimensions.height == 0) { + var check = element.ancestors(), + restore = [], styles = []; + check.push(element); + + check.each(function(c) { + if (c != element && c.visible()) return; + restore.push(c); + styles.push({ + display: c.getStyle('display'), + position: c.getStyle('position'), + visibility: c.getStyle('visibility') + }); + c.setStyle('display:block;visibility:visible;position:absolute;'); + }); + + // get the dimensions + dimensions = {width: element.clientWidth, height: element.clientHeight}; + + // restore the styles + for (var i = 0, length = restore.length; i < length; i++) + restore[i].setStyle(styles[i]); + } + return dimensions; }, - + makePositioned: function(element) { element = $(element); var pos = Element.getStyle(element, 'position'); @@ -572,10 +581,9 @@ Element.Methods = { if (element.offsetParent) return $(element.offsetParent); if (element == document.body) return $(element); - while ((element = element.parentNode) && element != document.body) + while ((element = element.parentNode) && element != document.body && element.nodeType != 9) if (Element.getStyle(element, 'position') != 'static') return $(element); - return $(document.body); }, @@ -595,7 +603,7 @@ Element.Methods = { element = forElement; do { - if (!Prototype.Browser.Opera || element.tagName.toUpperCase() == 'BODY') { + if (!Prototype.Browser.Opera || element.tagName.toUpperCase() == 'HTML') { valueT -= element.scrollTop || 0; valueL -= element.scrollLeft || 0; } @@ -604,7 +612,25 @@ Element.Methods = { return Element._returnOffset(valueL, valueT); }, + cloneDimensions: function(element, source) { + element = $(element); + var dimensions = $(source).getDimensions(), + options = Object.extend({ + setWidth: true, + setHeight: true, + offsetWidth: 0, + offsetHeight: 0 + }, arguments[2] || {}); + + if (options.setWidth) element.setStyle({width: (dimensions.width + options.offsetWidth || 0) + 'px'}); + if (options.setHeight) element.setStyle({height: (dimensions.height + options.offsetHeight || 0) + 'px'}); + + return element; + }, + clonePosition: function(element, source) { + element = $(element); + source = $(source); var options = Object.extend({ setLeft: true, setTop: true, @@ -614,17 +640,12 @@ Element.Methods = { offsetLeft: 0 }, arguments[2] || { }); - // find page position of source - source = $(source); - var p = source.viewportOffset(); - // find coordinate system to use - element = $(element); - var delta = [0, 0]; - var parent = null; + var parent, delta = [0, 0]; + // delta [0,0] will do fine with position: fixed elements, // position:absolute needs offsetParent deltas - if (Element.getStyle(element, 'position') == 'absolute') { + if (element.getStyle('position') == 'absolute') { parent = element.getOffsetParent(); delta = parent.viewportOffset(); } @@ -632,14 +653,19 @@ Element.Methods = { // correct by body offsets (fixes Safari) if (parent == document.body) { delta[0] -= document.body.offsetLeft; - delta[1] -= document.body.offsetTop; + delta[1] -= document.body.offsetTop; } + // find page position of source + var p = source.viewportOffset(); + // set position - if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; - if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; - if (options.setWidth) element.style.width = source.offsetWidth + 'px'; - if (options.setHeight) element.style.height = source.offsetHeight + 'px'; + if (options.setLeft) element.setStyle({ left: (p[0] - delta[0] + options.offsetLeft) + 'px'}); + if (options.setTop) element.setStyle({ top: (p[1] - delta[1] + options.offsetTop) + 'px'}); + + // set dimensions + element.cloneDimensions(source, { setWidth: options.setWidth, setHeight: options.setHeight }); + return element; } }; @@ -706,7 +732,7 @@ if (Prototype.Browser.Opera) { else if (Prototype.Browser.IE) { // IE doesn't report offsets correctly for static elements, so we change them - // to "relative" to get the values, then change them back. + // to "relative" to get the values, then change them back. Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( function(proceed, element) { element = $(element); @@ -721,7 +747,7 @@ else if (Prototype.Browser.IE) { return value; } ); - + $w('positionedOffset viewportOffset').each(function(method) { Element.Methods[method] = Element.Methods[method].wrap( function(proceed, element) { diff --git a/test/unit/dom.html b/test/unit/dom.html index 42cc498..28b86fd 100644 --- a/test/unit/dom.html +++ b/test/unit/dom.html @@ -67,18 +67,58 @@ left: 15px; } - #dimensions-display-none, #imensions-display-none-pos-rel, #dimensions-display-none-pos-abs { - display: none; + #dimensions-display-none, #dimensions-display-none-pos-rel, #dimensions-display-none-pos-abs { + display: none; } #dimensions-table, #dimensions-tbody, #dimensions-tr, #dimensions-td { - font-size: 10px; - margin: 0; - padding: 0; - border: 0; - border-spacing: 0; - height: 10em; - width: 20em; + font-size: 10px; + margin: 0; + padding: 0; + border: 0; + border-spacing: 0; + height: 10em; + width: 20em; + } + + .getDimensionsBox { + float: left; + clear: both; + width: 200px; + height: 200px; + background: #1d8bcb; + } + .getDimensionsBox div { float: left; } + .getDimensionsBox .deep { + height: 150px; + width: 150px; + background: #21bacc; + } + .getDimensionsBox .deeper { + height: 130px; + width: 130px; + background: #94dee6; + } + .getDimensionsBox .deepest { + height: 100px; + width: 100px; + background: #cbeff3; + } + + #clonePositionSource, + #cloneDimensionsSource { + position: absolute; + top: 10px; + left: 560px; + height: 20px; + width: 30px; + background: red; + } + + #clonePositionTarget, + #cloneDimensionsTarget { + position: absolute; + background: #21bacc; } #notInlineAbsoluted { position: absolute; } @@ -386,6 +426,44 @@ +
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+ +
+ + + +
+
+ +
+
+