文档和元素的几何形态和滚动
本节主要阐述在浏览器中完成文档的布局之后,怎样才能在抽象的基于树的文档模型与几何形状的基于坐标的视图之间来回变换。
文档坐标和视口坐标
元素的位置是以像素来度量的,向右代表 X 坐标的增加,向下代表 Y 坐标的增加。但是,有两个不同的点作为坐标系的原点:元素的X和Y坐标可以相对于文档的左上角或者相对于在其中显示文档的视口的左上角。在顶级窗口和标签页中,「视口」只是实际显示文档内容的浏览器的一部分,它不包括浏览器「外壳」(如菜单、工具条和标签页),无论在何种情况下,讨论元素的位置时,必须弄清楚所使用的坐标是文档坐标还是视口坐标(视口坐标有时候也叫窗口坐标)。
文档坐标比视口坐标更加基础,并且在用户滚动时它们不会发生变化,在两种坐标系之间相互转换,必须加上或减去滚动的偏移量。为了在坐标系之间相互转换,需要判定浏览器窗口滚动态的位置。Window 对象的 pageXOffset
和 pageYOffset
属性在所有的浏览器中会提供这些值(除了 IE 8 及更早的版本除外),此外还可以通过 scrollLeft
和 scrollTop
属性来获得滚动条的位置:
/**
* 查询窗口滚动条的位置
* @param w
* @returns {*}
*/
function getScrollOffsets(w) {
w = w || window; // 使用指定窗口,如果参数为空则使用当前窗口
// IE 9 及以上版本,非 IE 浏览器
if (w.pageXOffset !== null)
return {x: w.pageXOffset, y: pageYOffset};
// 标准模式下的 IE(或任何浏览器)
var d = w.document;
if (document.compatMode === "CSS1Compact")
return {x: d.documentElement.scrollLeft, y: d.documentElement.scrollTop};
// 怪异模式下的浏览器
return {x: d.body.scrollLeft, y: d.body.scrollTop};
}
有时候判定视口的尺寸也非常有用,例如,确定文档的哪些部分是可见度:
/**
* 查询窗口的视口尺寸
* @param w
* @returns {*}
*/
function getViewportSize(w) {
w = w || window; // 使用指定窗口,如果参数为空则使用当前窗口
// IE 9 及以上版本,非 IE 浏览器
if (w.innerWidth !== null)
return {w: w.innerWidth, h: w.innerHeight};
// 标准模式下的 IE(或任何浏览器)
var d = w.document;
if (document.compatMode === "CSS1Compact")
return {w: d.documentElement.clientWidth, h: d.documentElement.clientHeight};
// 怪异模式下的浏览器
return {w: d.body.clientWidth, h: d.body.clientHeight};
}
查询元素的几何尺寸
我们可以通过调用元素对象的 getBoundingClientRect()
方法判定一个元素的尺寸,该方法是在 IE 5 引入的,而现在当前的所有浏览器都实现了,该方法返回一个包含 left
、top
、right
和 bottom
属性的对象,分别代表左上角和右下角的X和Y坐标。
这个方法返回的是元素在视口坐标中的位置,为了将其转换为文档坐标,需要加上滚动偏移量:
var box = e.getBoundingClientRect();
var offset = getScrollOffsets();
var x = box.left + offset.x;
var y = box.top + offset.y;
注:
getBoundingClientRect()
所返回的坐标包含元素的边框和内边距,但不包含元素的外边距,关于边距和边框将会在下一章介绍。
浏览器在布局时块状元素总是矩形,但内联元素可能跨了多行,因此可能由多个矩形组成,如果在内联元素上调用 getBoundingClientRect()
,它返回的是「边界矩形」,如果想查询内联元素的每个独立矩形,需要调用 getClientRects()
获得一个只读的类数组对象,它的每个元素类似于 getBoundingClientRect()
返回的矩形对象。
此外,getBoundingClientRect()
和 getClientRects()
所返回的矩形对象都是调用方法时文档视觉状态的静态快照,在用户滚动或改变浏览器窗口大小时并不会更新结果对象。
判定元素在某点
如果想要反过来判断视口中的指定位置有什么元素,可以调用 Document 对象的 elementFromPoint()
方法,传递 X 和 Y 坐标(视口坐标),该方法返回在指定位置的一个元素,如果指定的点在视口以外,该方法返回 null
:
实际编码过程中,很少用到该方法。
滚动
scrollLeft
和 scrollTop
属性除了可以用来查询之外,还可以用来设置让浏览器滚动,但一种更简单的方法是使用 Window 的对象 scrollTop()
方法(与 scroll()
方法等价),该方法接收一个 X 和 Y 坐标(文档坐标),并作为滚动条的偏移量设置它们。以下代码演示了滚动浏览器到文档最下面的页面:
var documentHeight = document.documentElement.offsetHeight;
var viewPortHeight = window.innerHeight;
window.screenTop(0, documentHeight - viewPortHeight);
Window 对象的 scrollBy()
方法与上述方法类似,但是它的参数是相对的,并在当前滚动条的偏移量上增加,例如,快速阅读者可能喜欢这样的书签:
javascript:void setInterval(function(){scrollBy(0,10)}, 200);
通常,我们只是想要滚动条滚动到文档中的某个元素可见,可以通过 getBoundingClientRect()
获取元素位置并将其转换为文档坐标,然后用 scrollTo()
方法实现。但是在需要显示的 HTML 元素上调用 scrollIntoView()
方法则更加方便,它能保证元素在视口中可见。
关于元素尺寸、位置和溢出的更多信息
不支持 getBoundingClientRect()
的老式浏览器获取元素位置信息讨论。(太古老,略过)
No Comments