diff --git a/sources/BoundsInfo.js b/sources/BoundsInfo.js
index 2433659..8ef9d30 100644
--- a/sources/BoundsInfo.js
+++ b/sources/BoundsInfo.js
@@ -10,6 +10,17 @@ PIE.BoundsInfo.prototype = {
_locked: 0,
+ /**
+ * Determines if the element's position has changed since the last update.
+ * TODO this does a simple getBoundingClientRect comparison for detecting the
+ * changes in position, which may not always be accurate; it's possible that
+ * an element will actually move relative to its positioning parent, but its position
+ * relative to the viewport will stay the same. Need to come up with a better way to
+ * track movement. The most accurate would be the same logic used in RootRenderer.updatePos()
+ * but that is a more expensive operation since it performs DOM walking, and we want this
+ * check to be as fast as possible. Perhaps introduce a -pie-* flag to trigger the slower
+ * but more accurate method?
+ */
positionChanged: function() {
var last = this._lastBounds,
bounds;
diff --git a/sources/Element.js b/sources/Element.js
index ba7ab8b..61da965 100644
--- a/sources/Element.js
+++ b/sources/Element.js
@@ -53,7 +53,7 @@ PIE.Element = (function() {
function Element( el ) {
var me = this,
- renderers,
+ childRenderers,
rootRenderer,
boundsInfo = new PIE.BoundsInfo( el ),
styleInfos,
@@ -79,8 +79,7 @@ PIE.Element = (function() {
cs = el.currentStyle,
lazy = cs.getAttribute( lazyInitCssProp ) === 'true',
trackActive = cs.getAttribute( trackActiveCssProp ) !== 'false',
- trackHover = cs.getAttribute( trackHoverCssProp ) !== 'false',
- childRenderers;
+ trackHover = cs.getAttribute( trackHoverCssProp ) !== 'false';
// Polling for size/position changes: default to on in IE8, off otherwise, overridable by -pie-poll
poll = cs.getAttribute( pollCssProp );
@@ -160,7 +159,6 @@ PIE.Element = (function() {
}
rootRenderer.childRenderers = childRenderers; // circular reference, can't pass in constructor; TODO is there a cleaner way?
}
- renderers = [ rootRenderer ].concat( childRenderers );
// Add property change listeners to ancestors if requested
initAncestorEventListeners();
@@ -229,28 +227,18 @@ PIE.Element = (function() {
if( initialized ) {
lockAll();
- var i, len = renderers.length,
+ var i = 0, len = childRenderers.length,
sizeChanged = boundsInfo.sizeChanged();
-
- for( i = 0; i < len; i++ ) {
- renderers[i].prepareUpdate();
+ for( ; i < len; i++ ) {
+ childRenderers[i].prepareUpdate();
}
for( i = 0; i < len; i++ ) {
- if( force || sizeChanged || ( isPropChange && renderers[i].needsUpdate() ) ) {
- renderers[i].updateRendering();
+ if( force || sizeChanged || ( isPropChange && childRenderers[i].needsUpdate() ) ) {
+ childRenderers[i].updateRendering();
}
}
- rootRenderer.finishUpdate();
-
- if( force || ( ( !isPropChange || rootRenderer.isPositioned ) && boundsInfo.positionChanged() ) ) {
- /* TODO just using getBoundingClientRect (used internally by BoundsInfo) for detecting
- position changes may not always be accurate; it's possible that
- an element will actually move relative to its positioning parent, but its position
- relative to the viewport will stay the same. Need to come up with a better way to
- track movement. The most accurate would be the same logic used in RootRenderer.updatePos()
- but that is a more expensive operation since it does some DOM walking, and we want this
- check to be as fast as possible. */
- rootRenderer.updatePos();
+ if( force || sizeChanged || isPropChange || boundsInfo.positionChanged() ) {
+ rootRenderer.updateRendering();
}
unlockAll();
@@ -265,14 +253,12 @@ PIE.Element = (function() {
* Handle property changes to trigger update when appropriate.
*/
function propChanged() {
- var e = event;
-
// Some elements like
fire onpropertychange events for old-school background properties
// ('background', 'bgColor') when runtimeStyle background properties are changed, which
// results in an infinite loop; therefore we filter out those property names. Also, 'display'
// is ignored because size calculations don't work correctly immediately when its onpropertychange
// event fires, and because it will trigger an onresize event anyway.
- if( !( e && e.propertyName in ignorePropertyNames ) ) {
+ if( initialized && !( event && event.propertyName in ignorePropertyNames ) ) {
update( 1 );
}
}
@@ -341,7 +327,7 @@ PIE.Element = (function() {
*/
function ancestorPropChanged() {
var name = event.propertyName;
- if( name === 'className' || name === 'id' ) {
+ if( name === 'className' || name === 'id' || name.indexOf( 'style.' ) === 0 ) {
propChanged();
}
}
@@ -399,12 +385,13 @@ PIE.Element = (function() {
destroyed = 1;
// destroy any active renderers
- if( renderers ) {
- for( i = 0, len = renderers.length; i < len; i++ ) {
- renderers[i].finalized = 1;
- renderers[i].destroy();
+ if( childRenderers ) {
+ for( i = 0, len = childRenderers.length; i < len; i++ ) {
+ childRenderers[i].finalized = 1;
+ childRenderers[i].destroy();
}
}
+ rootRenderer.destroy();
// Remove from list of polled elements in IE8
if( poll ) {
@@ -414,7 +401,7 @@ PIE.Element = (function() {
PIE.OnResize.unobserve( update );
// Kill references
- renderers = boundsInfo = styleInfos = styleInfosArr = el = null;
+ childRenderers = rootRenderer = boundsInfo = styleInfos = styleInfosArr = el = null;
me.el = me = 0;
}
}
diff --git a/sources/IE9BorderImageRenderer.js b/sources/IE9BorderImageRenderer.js
index 103b152..538fbe9 100644
--- a/sources/IE9BorderImageRenderer.js
+++ b/sources/IE9BorderImageRenderer.js
@@ -137,7 +137,7 @@ PIE.IE9BorderImageRenderer = PIE.RendererBase.newRenderer( {
// will have been created asynchronously after the main element's update has finished; we'll
// therefore need to force the root renderer to sync to the final background once finished.
if( isAsync ) {
- me.parent.finishUpdate();
+ me.parent.updateRendering();
}
}, me );
diff --git a/sources/IE9RootRenderer.js b/sources/IE9RootRenderer.js
index dbe6b8b..6e0d5ac 100644
--- a/sources/IE9RootRenderer.js
+++ b/sources/IE9RootRenderer.js
@@ -5,10 +5,6 @@
*/
PIE.IE9RootRenderer = PIE.RendererBase.newRenderer( {
- updatePos: PIE.emptyFn,
- updateRendering: PIE.emptyFn,
- updateVisibility: PIE.emptyFn,
-
outerCommasRE: /^,+|,+$/g,
innerCommasRE: /,+/g,
@@ -19,7 +15,7 @@ PIE.IE9RootRenderer = PIE.RendererBase.newRenderer( {
bgLayers[zIndex] = bg || undef;
},
- finishUpdate: function() {
+ updateRendering: function() {
var me = this,
bgLayers = me._bgLayers,
bg;
diff --git a/sources/RootRenderer.js b/sources/RootRenderer.js
index 2c1889f..4b1c4b9 100644
--- a/sources/RootRenderer.js
+++ b/sources/RootRenderer.js
@@ -6,12 +6,6 @@
*/
PIE.RootRenderer = PIE.RendererBase.newRenderer( {
- /**
- * Flag indicating the element has already been positioned at least once.
- * @type {boolean}
- */
- isPositioned: false,
-
isActive: function() {
var children = this.childRenderers;
for( var i in children ) {
@@ -22,72 +16,58 @@ PIE.RootRenderer = PIE.RendererBase.newRenderer( {
return false;
},
- needsUpdate: function() {
- return this.styleInfos.visibilityInfo.changed();
- },
-
- /**
- * Tell the renderer to update based on modified element position
- */
- updatePos: function() {
- if( this.isActive() && this.getBoxEl() ) {
- var el = this.getPositioningElement(),
- par = el,
- docEl,
- parRect,
- tgtCS = el.currentStyle,
- tgtPos = tgtCS.position,
- boxPos,
- s = this.getBoxEl().style, cs,
- x = 0, y = 0,
- elBounds = this.boundsInfo.getBounds(),
- logicalZoomRatio = elBounds.logicalZoomRatio;
-
- if( tgtPos === 'fixed' && PIE.ieVersion > 6 ) {
- x = elBounds.x * logicalZoomRatio;
- y = elBounds.y * logicalZoomRatio;
- boxPos = tgtPos;
+ getBoxCssText: function() {
+ var el = this.getPositioningElement(),
+ par = el,
+ docEl,
+ parRect,
+ tgtCS = el.currentStyle,
+ tgtPos = tgtCS.position,
+ boxPos,
+ cs,
+ x = 0, y = 0,
+ elBounds = this.boundsInfo.getBounds(),
+ vis = this.styleInfos.visibilityInfo.getProps(),
+ logicalZoomRatio = elBounds.logicalZoomRatio;
+
+ if( tgtPos === 'fixed' && PIE.ieVersion > 6 ) {
+ x = elBounds.x * logicalZoomRatio;
+ y = elBounds.y * logicalZoomRatio;
+ boxPos = tgtPos;
+ } else {
+ // Get the element's offsets from its nearest positioned ancestor. Uses
+ // getBoundingClientRect for accuracy and speed.
+ do {
+ par = par.offsetParent;
+ } while( par && ( par.currentStyle.position === 'static' ) );
+ if( par ) {
+ parRect = par.getBoundingClientRect();
+ cs = par.currentStyle;
+ x = ( elBounds.x - parRect.left ) * logicalZoomRatio - ( parseFloat(cs.borderLeftWidth) || 0 );
+ y = ( elBounds.y - parRect.top ) * logicalZoomRatio - ( parseFloat(cs.borderTopWidth) || 0 );
} else {
- // Get the element's offsets from its nearest positioned ancestor. Uses
- // getBoundingClientRect for accuracy and speed.
- do {
- par = par.offsetParent;
- } while( par && ( par.currentStyle.position === 'static' ) );
- if( par ) {
- parRect = par.getBoundingClientRect();
- cs = par.currentStyle;
- x = ( elBounds.x - parRect.left ) * logicalZoomRatio - ( parseFloat(cs.borderLeftWidth) || 0 );
- y = ( elBounds.y - parRect.top ) * logicalZoomRatio - ( parseFloat(cs.borderTopWidth) || 0 );
- } else {
- docEl = doc.documentElement;
- x = ( elBounds.x + docEl.scrollLeft - docEl.clientLeft ) * logicalZoomRatio;
- y = ( elBounds.y + docEl.scrollTop - docEl.clientTop ) * logicalZoomRatio;
- }
- boxPos = 'absolute';
+ docEl = doc.documentElement;
+ x = ( elBounds.x + docEl.scrollLeft - docEl.clientLeft ) * logicalZoomRatio;
+ y = ( elBounds.y + docEl.scrollTop - docEl.clientTop ) * logicalZoomRatio;
}
-
- s.position = boxPos;
- s.left = x;
- s.top = y;
- s.zIndex = tgtPos === 'static' ? -1 : tgtCS.zIndex;
- this.isPositioned = true;
+ boxPos = 'absolute';
}
- },
- updateVisibility: function() {
- var vis = this.styleInfos.visibilityInfo,
- box = this._box;
- if ( box && vis.changed() ) {
- vis = vis.getProps();
- box.style.display = ( vis.visible && vis.displayed ) ? '' : 'none';
- }
+ return 'direction:ltr;' +
+ 'position:absolute;' +
+ 'behavior:none !important;' +
+ 'position:' + boxPos + ';' +
+ 'left:' + x + 'px;' +
+ 'top:' + y + 'px;' +
+ 'z-index:' + ( tgtPos === 'static' ? -1 : tgtCS.zIndex ) + ';' +
+ 'display:' + ( vis.visible && vis.displayed ? 'block' : 'none' );
},
- updateRendering: function() {
- if( this.isActive() ) {
- this.updateVisibility();
- } else {
- this.destroy();
+ updateBoxStyles: function() {
+ var me = this,
+ boxEl = me.getBoxEl();
+ if( boxEl && ( me.boundsInfo.positionChanged() || me.styleInfos.visibilityInfo.changed() ) ) {
+ boxEl.style.cssText = me.getBoxCssText();
}
},
@@ -111,48 +91,59 @@ PIE.RootRenderer = PIE.RendererBase.newRenderer( {
/**
* Render any child rendrerer shapes which have not already been rendered into the DOM.
*/
- finishUpdate: function() {
+ updateRendering: function() {
var me = this,
queue = me._shapeRenderQueue,
renderedShapes, markup, i, len, j,
- ref, pos;
-
- if( queue ) {
- // We've already rendered something once, so do incremental insertion of new shapes
- renderedShapes = me._renderedShapes;
- if( renderedShapes ) {
- for( i = 0, len = queue.length; i < len; i++ ) {
- for( j = renderedShapes.length; j--; ) {
- if( renderedShapes[ j ].ordinalGroup < queue[ i ].ordinalGroup ) {
- break;
+ ref, pos, vis;
+
+ if (me.isActive()) {
+ if( queue ) {
+ // We've already rendered something once, so do incremental insertion of new shapes
+ renderedShapes = me._renderedShapes;
+ if( renderedShapes ) {
+ for( i = 0, len = queue.length; i < len; i++ ) {
+ for( j = renderedShapes.length; j--; ) {
+ if( renderedShapes[ j ].ordinalGroup < queue[ i ].ordinalGroup ) {
+ break;
+ }
}
- }
- if ( j < 0 ) {
- ref = me.getBoxEl();
- pos = 'afterBegin';
- } else {
- ref = renderedShapes[ j ].getShape();
- pos = 'afterEnd';
+ if ( j < 0 ) {
+ ref = me.getBoxEl();
+ pos = 'afterBegin';
+ } else {
+ ref = renderedShapes[ j ].getShape();
+ pos = 'afterEnd';
+ }
+ ref.insertAdjacentHTML( pos, queue[ i ].getMarkup() );
+ renderedShapes.splice( j < 0 ? 0 : j, 0, queue[ i ] );
}
- ref.insertAdjacentHTML( pos, queue[ i ].getMarkup() );
- renderedShapes.splice( j < 0 ? 0 : j, 0, queue[ i ] );
- }
- }
- // This is the first render, so build up a single markup string and insert it all at once
- else {
- queue.sort( me.shapeSorter );
- markup = [ '' ];
- for( i = 0, len = queue.length; i < len; i++ ) {
- markup.push( queue[ i ].getMarkup() );
+ me._shapeRenderQueue = 0;
+ me.updateBoxStyles();
}
- markup.push( '' );
+ // This is the first render, so build up a single markup string and insert it all at once
+ else {
+ vis = me.styleInfos.visibilityInfo.getProps();
+ if( vis.visible && vis.displayed ) {
+ queue.sort( me.shapeSorter );
+ markup = [ '' ];
+ for( i = 0, len = queue.length; i < len; i++ ) {
+ markup.push( queue[ i ].getMarkup() );
+ }
+ markup.push( '' );
- me.getPositioningElement().insertAdjacentHTML( 'beforeBegin', markup.join( '' ) );
+ me.getPositioningElement().insertAdjacentHTML( 'beforeBegin', markup.join( '' ) );
- me._renderedShapes = queue;
+ me._renderedShapes = queue;
+ me._shapeRenderQueue = 0;
+ }
+ }
+ } else {
+ me.updateBoxStyles();
}
- me._shapeRenderQueue = 0;
+ } else {
+ me.destroy();
}
},
diff --git a/sources/VisibilityStyleInfo.js b/sources/VisibilityStyleInfo.js
index 182923d..fb9bf69 100644
--- a/sources/VisibilityStyleInfo.js
+++ b/sources/VisibilityStyleInfo.js
@@ -6,25 +6,23 @@
PIE.VisibilityStyleInfo = PIE.StyleInfoBase.newStyleInfo( {
getCss: PIE.StyleInfoBase.cacheWhenLocked( function() {
- var cs = this.targetElement.currentStyle;
- return cs.visibility + '|' + cs.display;
- } ),
-
- parseCss: function() {
var el = this.targetElement,
rs = el.runtimeStyle,
cs = el.currentStyle,
rsVis = rs.visibility,
- csVis;
-
+ ret;
rs.visibility = '';
- csVis = cs.visibility;
+ ret = cs.visibility + '|' + cs.display;
rs.visibility = rsVis;
+ return ret;
+ } ),
+ parseCss: function() {
+ var info = this.getCss().split('|');
return {
- visible: csVis !== 'hidden',
- displayed: cs.display !== 'none'
- }
+ visible: info[0] !== 'hidden',
+ displayed: info[1] !== 'none'
+ };
},
/**
diff --git a/tests/hiding-tests.html b/tests/hiding-tests.html
index 1c51070..13dc02b 100644
--- a/tests/hiding-tests.html
+++ b/tests/hiding-tests.html
@@ -116,7 +116,7 @@