Fix errors in DomElement.getOffset() by using native #getBoundingClientRect() in DomElement.getBounds() and relying on that. Closes #29

This commit is contained in:
Jürg Lehni 2011-07-31 14:13:29 +01:00
parent e1d90c921c
commit d84f0d34cf
3 changed files with 69 additions and 97 deletions

View file

@ -14,96 +14,67 @@
* All rights reserved.
*/
var DomElement = new function() {
function cumulateOffset(el, name, parent, test) {
var left = name + 'Left',
top = name + 'Top',
x = 0,
y = 0,
style;
// If we're asked to calculate positioned offset, stop at any parent
// element that has relative or absolute position.
while (el && el.style && (!test || !test.test(
style = DomElement.getComputedStyle(el, 'position')))) {
x += el[left] || 0;
y += el[top] || 0;
el = el[parent];
var DomElement = {
getBounds: function(el, viewport) {
var rect = el.getBoundingClientRect(),
doc = el.ownerDocument,
body = doc.body,
docEl = doc.documentElement,
x = rect.left - (docEl.clientLeft || body.clientLeft || 0),
y = rect.top - (docEl.clientTop || body.clientTop || 0);
if (!viewport) {
var win = DomElement.getViewport(doc);
x += win.pageXOffset || docEl.scrollLeft || body.scrollLeft;
y += win.pageYOffset || docEl.scrollTop || body.scrollTop;
}
return {
offset: Point.create(x, y),
element: el,
style: style
};
return new Rectangle(x, y, rect.width, rect.height);
},
getOffset: function(el, viewport) {
return this.getBounds(el, viewport).getPoint();
},
getSize: function(el) {
return this.getBounds(el, true).getSize();
},
/**
* Checks if element is invisibile (display: none, ...)
*/
isInvisible: function(el) {
return this.getSize(el).equals([0, 0]);
},
/**
* Checks if element is visibile in current viewport
*/
isVisible: function(el) {
// See if the viewport bounds intersect with the windows rectangle
// which always starts at 0, 0
return !this.isInvisible(el) && this.getViewportBounds(el).intersects(
this.getBounds(el, false, true));
},
getViewport: function(doc) {
return doc.defaultView || doc.parentWindow;
},
getViewportBounds: function(el) {
var doc = el.ownerDocument,
view = this.getViewport(doc),
body = doc.getElementsByTagName(
doc.compatMode === 'CSS1Compat' ? 'html' : 'body')[0];
return Rectangle.create(0, 0,
view.innerWidth || body.clientWidth,
view.innerHeight || body.clientHeight
);
},
getComputedStyle: function(el, name) {
if (el.currentStyle)
return el.currentStyle[Base.camelize(name)];
var style = this.getViewport(el.ownerDocument)
.getComputedStyle(el, null);
return style ? style.getPropertyValue(Base.hyphenate(name)) : null;
}
function getScrollOffset(el, test) {
return cumulateOffset(el, 'scroll', 'parentNode', test).offset;
}
return {
getOffset: function(el, positioned, viewport) {
var res = cumulateOffset(el, 'offset', 'offsetParent',
positioned ? /^(relative|absolute|fixed)$/ : /^fixed$/);
// We need to handle fixed positioned elements seperately if we're
// asked to calculate offsets within the page (= not within
// viewport), by adding their scroll offset to the result.
if (res.style == 'fixed' && !viewport)
return res.offset.add(getScrollOffset(res.element));
// Otherwise remove scrolling from the calculated offset if we asked
// for viewport coordinates
return viewport
? res.offset.subtract(getScrollOffset(el, /^fixed$/))
: res.offset;
},
getSize: function(el) {
return Size.create(el.offsetWidth, el.offsetHeight);
},
getBounds: function(el, positioned, viewport) {
return new Rectangle(this.getOffset(el, positioned, viewport),
this.getSize(el));
},
/**
* Checks if element is invisibile (display: none, ...)
*/
isInvisible: function(el) {
return this.getSize(el).equals([0, 0]);
},
/**
* Checks if element is visibile in current viewport
*/
isVisible: function(el) {
// See if the viewport bounds intersect with the windows rectangle
// which always starts at 0, 0
return !this.isInvisible(el)
&& new Rectangle([0, 0], this.getViewportSize(el))
.intersects(this.getBounds(el, false, true));
},
getViewport: function(doc) {
return doc.defaultView || doc.parentWindow;
},
getViewportSize: function(el) {
var doc = el.ownerDocument,
view = this.getViewport(doc),
body = doc.getElementsByTagName(
doc.compatMode === 'CSS1Compat' ? 'html' : 'body')[0];
return Size.create(
view.innerWidth || body.clientWidth,
view.innerHeight || body.clientHeight
);
},
getComputedStyle: function(el, name) {
if (el.currentStyle)
return el.currentStyle[Base.camelize(name)];
var style = this.getViewport(el.ownerDocument)
.getComputedStyle(el, null);
return style ? style.getPropertyValue(Base.hyphenate(name)) : null;
}
};
};

View file

@ -60,8 +60,8 @@ var DomEvent = {
getOffset: function(event, target) {
// Remove target offsets from page coordinates
return DomEvent.getPoint(event).subtract(
DomElement.getOffset(target || DomEvent.getTarget(event), true));
return DomEvent.getPoint(event).subtract(DomElement.getOffset(
target || DomEvent.getTarget(event)));
},
preventDefault: function(event) {

View file

@ -42,9 +42,10 @@ var View = this.View = Base.extend(/** @lends View# */{
if (PaperScript.hasAttribute(canvas, 'resize')) {
// Subtract canvas' viewport offset from the total size, to
// stretch it in
var offset = DomElement.getOffset(canvas, false, true),
var offset = DomElement.getOffset(canvas, true),
that = this;
size = DomElement.getViewportSize(canvas).subtract(offset);
size = DomElement.getViewportBounds(canvas)
.getSize().subtract(offset);
canvas.width = size.width;
canvas.height = size.height;
DomEvent.add(window, {
@ -52,11 +53,11 @@ var View = this.View = Base.extend(/** @lends View# */{
// Only update canvas offset if it's not invisible, as
// otherwise the offset would be wrong.
if (!DomElement.isInvisible(canvas))
offset = DomElement.getOffset(canvas, false, true);
offset = DomElement.getOffset(canvas, true);
// Set the size now, which internally calls onResize
// and redraws the view
that.setViewSize(DomElement.getViewportSize(canvas)
.subtract(offset));
that.setViewSize(DomElement.getViewportBounds(canvas)
.getSize().subtract(offset));
}
});
} else {