PDA

View Full Version : [OPEN] Element.getCenterXY() doesn't take scrollX or Y into account



rexxe
11 Oct 2006, 1:36 PM
In the code for YAHOO.ext.Element.getCenterXY(), it never takes the scrollX or Y into account. I have a use case where I want to move an object to the center of the page anytime the user scrolls. Using getCenterXY() only yields the center of the object as if the user had never scrolled. For example, if the center is x 300, y 300, and then the user scrolls down where the center is now x 400 and y 400, getCenterXY() continues to yiled 300,300. The code below is what I've been using which works:



getCenterXY:function(){
var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
var centerX=Math.round(((YAHOO.util.Dom.getViewportWidth()-this.getWidth())/2) + scrollX);
var centerY=Math.round(((YAHOO.util.Dom.getViewportHeight()-this.getHeight())/2) + scrollY);

return[centerX < 0 ? 0 : centerX,centerY < 0 ? 0 : centerY];
}

jack.slocum
11 Oct 2006, 1:53 PM
getCenterXY returns the center XY for the viewport, not the body. Unfortunately, I can't change that behavior because it could break existing code. I can however add an optional boolean param to include scroll in the calc...

getCenterXY(true) would include scroll offsets. How does that sound?

Jack

rexxe
11 Oct 2006, 2:01 PM
Sounds good. Keep up the excellent work. It's been a real time saver.....

jack.slocum
11 Oct 2006, 2:05 PM
How's this look?


getCenterXY : function(offsetScroll){
var centerX = Math.round((YAHOO.util.Dom.getViewportWidth()-this.getWidth())/2);
var centerY = Math.round((YAHOO.util.Dom.getViewportHeight()-this.getHeight())/2);
if(!offsetScroll){
return [centerX, centerY];
}else{
var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft || 0;
var scrollY = document.documentElement.scrollTop || document.body.scrollTop || 0;
return[centerX + scrollX, centerY + scrollY];
}
}


I took out the negative checks because negative coords are valid center XY values.

rexxe
11 Oct 2006, 2:15 PM
Your variable names are incorrect, you switch to scrollX and scrollY instead of scrollLeft and scrollTop. Also, in IE I get an Invalid Argument error, which obviously is not very informative in finding the problem


How's this look?


getCenterXY : function(offsetScroll){
var centerX = Math.round((YAHOO.util.Dom.getViewportWidth()-this.getWidth())/2);
var centerY = Math.round((YAHOO.util.Dom.getViewportHeight()-this.getHeight())/2);
if(!offsetScroll){
return [centerX, centerY];
}else{
var doc = document;
var scrollTop = Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
var scrollLeft = Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
return[centerX + scrollX, centerY + scrollY];
}
}


I took out the negative checks because negative coords are valid center XY values.

jack.slocum
11 Oct 2006, 2:23 PM
I edited the post after I tested it and got the js error but not quick enough. :)

Take a look again.

rexxe
11 Oct 2006, 2:32 PM
I still get an error in IE. It may be a problem with my code itself, so I'll research it. But there is still an issue. The getHeight and getWidth functions don't take into account elements that have a display of none. When display is none, width and height yield 0, so I have a function which temporarily changes the visibility and display to get the width and height. This is what I have, though it doesn't use your extension code yet since I'm just now migrating over to it:


getDimensions: function() {
if ($D.getStyle(this.el, "display") != "none")
return {width: this.el.offsetWidth, height: this.el.offsetHeight};

// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = this.el.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = '';
var originalWidth = this.el.clientWidth;
var originalHeight = this.el.clientHeight;
els.display = 'none';
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
}

rexxe
11 Oct 2006, 2:38 PM
Fixed the invalid argument error. Was a problem with my code......

rexxe
11 Oct 2006, 2:51 PM
Nevermind on the height and width, it looks like it is working now.

Thanks for your help.....


I still get an error in IE. It may be a problem with my code itself, so I'll research it. But there is still an issue. The getHeight and getWidth functions don't take into account elements that have a display of none. When display is none, width and height yield 0, so I have a function which temporarily changes the visibility and display to get the width and height. This is what I have, though it doesn't use your extension code yet since I'm just now migrating over to it:


getDimensions: function() {
if ($D.getStyle(this.el, "display") != "none")
return {width: this.el.offsetWidth, height: this.el.offsetHeight};

// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = this.el.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = '';
var originalWidth = this.el.clientWidth;
var originalHeight = this.el.clientHeight;
els.display = 'none';
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
}

jack.slocum
11 Oct 2006, 5:14 PM
I thought you might be interested in these two functions which will be in the next release.


beginMeasure : function(){
var p = this.dom;
if(p.offsetWidth || p.offsetHeight){
return; // offsets work already
}
var changed = [];
var p = this.dom; // start with this element
while(p && p.tagName.toLowerCase() != 'body'){
if(YAHOO.util.Dom.getStyle(p, 'display') == 'none'){
changed.push({el: p, visibility: YAHOO.util.Dom.getStyle(p, 'visibility')});
p.style.visibility = 'hidden';
p.style.display = 'block';
}
p = p.parentNode;
}
this._measureChanged = changed;
},

endMeasure : function(){
var changed = this._measureChanged;
if(changed){
for(var i = 0, len = changed.length; i < len; i++) {
var r = changed[i];
r.el.style.visibility = r.visibility;
r.el.style.display = 'none';
}
this._measureChanged = null;
}
},


Calling beginMeasure() ensures any offset calculations will work.

Calling endMeasure() restores the parentNodes' states.

Animal
11 Oct 2006, 11:07 PM
They are very useful to avoid the "display:none" problem. Will you be wrapping internal yui-ext measuring code with calls to these, or do we have to wrap certain segments of our code?

jack.slocum
12 Oct 2006, 5:21 AM
Yes, the two times the grid should use it are when it renders and when it recalcs for window resize. Are there any other uses you can think of?

Animal
12 Oct 2006, 6:00 AM
Autosizing columns when some might be hidden?

Operations on hidden TabPanelItems or descents of them.

Any reason why TabPanelItems which are inactive are display:none rather than visibility:hidden?

jack.slocum
12 Oct 2006, 7:30 AM
Two reasons:
Because in a natural flow tabset (no overflow:auto) you'd want the space taken up by the TabPanel to be correct. Visibility hidden still takes up space and creates scrollbars etc.

The tab body elements are siblings, so if more than one is displayed then they layout on top of each other.

With some creativity we could probably work around both these, but display:none seemed like the solution that made the most sense.