浏览器中的各种width/height

由于浏览器的兼容性问题,在求一个元素的长宽或者是其距离视口边缘的距离时,往往不知所措。本文总结了在浏览器中获取元素长宽时,所用到的各种浏览器兼容方法。

window对象

screenLeft/screenTop

用来确定和修改 window 对象的属性和方法有很多。IE、Safari、Opera 和 Chrome 都提供了以下属性:

screenLeft  ==>  窗口相对于屏幕左左边的位置
scrennTop  ==>  窗口相对于屏幕上边的位置

Firefos 则在以下属性中提供相同信息:

screenX  ==>  窗口相对于屏幕左边的位置(Firefox)
screenY ==>  窗口相对于屏幕上边的位置(Firefox)

Safari 和 Chrome 也同时支持 screenXscreenY 这两个属性。Opera 虽然也支持 screenXscreenY,但与 screenLeftscreenTop 属性不对应。因此,不建议在 Opera 中使用。

浏览器 window.screenLeft window.screenTop window.screenX window.screenY
IE 浏览器窗口相对于屏幕左边的位置 浏览器窗口相对于屏幕上边的位置 —— ——
Opera 浏览器窗口相对于屏幕左边的位置 浏览器窗口相对于屏幕上边的位置 !== screenLeft !== screenTop
IE/Opera 这个距离包括浏览器中实际页面上方的工具栏 这个距离包括浏览器中实际页面左边的浏览器栏
Safari 浏览器窗口相对于屏幕左边的位置 浏览器窗口相对于屏幕上边的位置 浏览器窗口相对于屏幕左边的位置 浏览器窗口相对于屏幕上边的位置
Chrome 浏览器窗口相对于屏幕左边的位置 浏览器窗口相对于屏幕上边的位置 浏览器窗口相对于屏幕上边边的位置
Firefox —— —— 浏览器窗口相对于屏幕左边的位置 浏览器窗口相对于屏幕上边的位置
Safari/Chrome/Firefox —— —— 这个距离不包括浏览器工具栏 这个距离不包括浏览器工具栏

由上表,我们可以得出如下的跨浏览器代码:

1
2
var leftPos = (typeof window.screenLeft === 'number') ? window.screenLeft : window.screenX,
topPos = (typeof window.screenTop === 'number') ? window.screenTop : window.screenY;

除去上表关于各个浏览器对属性所表示的位置距离的内涵差异之外,更让人琢磨不透的是,Firefox、Safari 和 Chrome 始终返回页面中每个框架的 top。screenYscreenTop 中值。即使在页面中由于被设置了外边距而发生偏移的情况下,相对于 window 对象使用 screenXscreenY 每次也都会返回相同的值。而 IE 和 Opera 则会给出框架相对于屏幕边界的精确坐标值。

最终结果,就是无法在跨浏览器情况下取得窗口左边和上边的精确坐标值。

innerWidth&innerHeight 与 outerWidth&outerHeight

window.innerWidthwindow.innerHeight 用于获取页面视图区域的宽度和高度。(不包括工具栏和滚动条)

window.outerWidthwindow.outerHeight 用于获取浏览器窗口的宽度和高度。(包括工具栏和滚动条)

浏览器对 window 的这 4 个属性的支持情况如下:

属性 innerWidth innerHeight outerWidth outerHeight
IE9 9+ 9+ 9+ 9+
Firefox/Chrome 1.0 1.0 1.0 1.0
Safari 3.0 3.0 3.0 3.0
Opera 9.0 9.0 9.0 9.0

注意,这 4 个属性的返回值均为一个以 px 为单位的 number 类型的数值。

当然,如果要编写跨浏览器代码,仅仅靠以上 4 个属性是不够的,老版本浏览器不会支持它们,另外早期 IE 还有标准模式与混杂模式之分。这里以 window.innerWidth 属性为例,展示跨浏览器代码的编写:

1
2
3
4
5
6
7
8
9
10
11
// 获取浏览器可见视口的宽度
// 跨浏览器代码
if (typeof window.innerWidth === 'number') { // 如果浏览器的此属性符合预期,就直接返回其值
return window.innerWidth;
} else if (document.compatMode === 'CSS1Compat') { // 页面处于标准模式
return document.documentElement.clientWidth;
} elst { // 页面处于混杂模式
return document.body.clientWidth;
}

上面仅是以 window.innerWidth 为例的获取可见视口宽度的跨浏览器代码,其他四个属性求值的跨浏览器代码类似。

移动设备的可见视口与布局视口大小如图所示:

移动设备 可见视口 布局视口
移动IE documeng.documentElement.clientWidth/document.documentElement.clientHeight document.body.clientWidth/document.body.clientHeight
其他浏览器 window.innerWidth/window.innerHeight document.documentElement.clientWidth/document.documentElement.clientHeight

对于移动设备,window.innerWidthwindow.innerHeight 保存着可见视口,也就是屏幕上可见页面区域的大小。移动 IE浏览器不支持这些属性,但通过 document.documentElement.clientWidthdocuemnt.documentElement.clientHeight 提供了相同的信息。随着页面的缩放,这些值也会相应发生变化。

在其他移动浏览器中,document.documentElement 度量的是布局视口,即渲染后页面的实际大小(与可见视口不同,可见视口只是整个页面中的一小部分)。移动 IE 浏览器把布局视口的信息保存在 document.body.clientWidthdocument.body.clientHeight中。这些值不会随着页面缩放变化。

另外,使用 resizeTo()resizeBy() 方法可以调整整个浏览器窗口的大小。这两个方法都接收两个参数,其中 resizeTo() 接收浏览器窗口的新宽度和新高度,而 resizeBy() 接收新窗口与原窗口的宽度和高度之差。

pageXOffset&pageYOffset

window.pageXoffsetwindow.pageYOffset 用于获取当前文档相对于浏览器视口左上角已经被滚动过的水平、垂直距离。

二者的返回值均为一个以 px 为单位的 number 类型的值。

注意: IE8 及更早版本不支持这两个属性。其余主流现代浏览器(Firefox/Chrome/Safari)均支持此属性。

跨浏览器代码如下(这里仅以 window.pageXOffset 为例):

1
2
3
4
5
6
7
8
9
if (typeof window.pageXOffset === 'number') { // 所有浏览器,除了 IE9 及更早
return window.pageXOffset;
} else if (document.compatMode === 'CSS1Compat') { // 标准模式
return document.documentElement.scrollLeft;
} else { // 混杂模式
return document.body.scrollLeft;
}

普通元素

可见大小

元素的可见大小由其高度、宽度决定,包括所有内边距、滚动条和边框大小(注意:不包括外边距)。

本部分所说的四个与偏移量相关的属性均是只读的。

offsetWidth/offsetHeight——元素在水平/垂直方向上占用的空间大小,以像素计算

offsetWidth  ==>  内容的宽度 + 左内边距 + 右内边距 + (可见的)垂直滚动条的宽度 + 左边框的宽度 + 右边框的宽度
offsetHeight  ==>  内容的高度 + 上内边距 + 下内边距 + (可见的)水平滚动条的高度 + 上边框的高度 + 下边框的高度

offsetLeft/offsetTop——元素的外边框至包含元素的内边距之间的像素距离

offsetLeft  ==>  元素的左外边框到包含元素的元素的左内边距之间的像素距离
offsetTop  ==>  元素的上外边框到包含元素的元素的上内边距之间的像素距离

offsetLeftoffsetTop 属性与包含元素有关,包含元素的引用保存在 offsetParent 属性中。offsetParent 不一定与 parentNode 的值相等。例如,<td> 元素的 offsetParent 是 作为其祖先元素的 <table> 元素,因为 <table> 是在 DOM 层次中距 <td> 元素最近的一个具有大小的元素。

要想知道某个元素在页面上的偏移量,将这个元素的 offsetWidthoffsetTopoffsetParent 的相同属性相加,如此循环直至根元素,就可以得到一个基本准确的值。下面两个函数分别用于取得一个元素的左偏移和上偏移量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 计算元素的相对于整个页面的左偏移
function getElementLeft(element) {
var actualLeft = element.offsetLeft,
current = element.offsetParent;
while (current !== null) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
// 计算元素相对于整个元素的上偏移
function getElementTop(element) {
var actualTop = element.offsetTop,
current = element.offsetParent;
while (current !== null) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}

客户区大小

元素的客户区大小指的是元素内容及其内边距所占据的空间大小。有关客户区大小的属性有两个:clientWidthclientHeight。这两个属性是只读的。

clientWidth  ==>  内容宽度 + 左内边距宽度 + 右内边距宽度
clientHeight  ==>  内容宽度 + 上内边距高度 + 下内边距高度

从字面上看,客户区大小就是元素内部的空间大小,因此滚动条所占用的空间不计算在内。

下面是一段获取浏览器视口大小的跨浏览器代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
function getViewport() {
if (document.compactMoce === 'BackCompact') { // 非标准模式
return {
width: document.body.clientWidth;
height: document.body.clientHeight;
};
} else {
return {
width: document.documentElement.clientWidth;
height: document.documentElement.clientHeight;
};
}
}

滚动大小

滚动大小,指的是包含滚动内容的元素的大小。有些元素(例如<html>元素),即使没有执行任何代码也能自动地添加滚动条;但另外一些元素,则需要通过 CSS 的 overflow 属性进行设置才能滚动。以下是 4 个与滚动大小相关的属性:

scrollHeight  ==>  在没有滚动条的情况下,元素内容的总高度
scrollWidth  ==>  在没有滚动条的情况下,元素内容的总高度

scrollLeft  ==>  被隐藏在内容区域左侧的像素数。设置这个属性可以改变元素的滚动位置
scrollTop  ==>  被隐藏在内容区域上方的像素数。设置这个属性可以改变元素的滚动位置

scrollWidthscrollHeight 主要用于确定元素内容的实际大小。例如,通常认为 <html> 元素是在Web浏览器中的视口中滚动的元素(IE6及之前的版本运行在混杂模式下时是<<body>元素)。因此,带有垂直滚动条的页面总高度就是 documeng.documengElement.scrollHeight

对于不包含滚动条的页面而言,scrollWidthscrollHeightclientWidthclientHeight 之间的关系并不十分明确。在这种情况下,基于 document.documentElement 查看这些属性会在不同浏览器间发现一些不一致性问题,如下:

浏览器 scrollWidth/scrollHeight clientWidth/clientHeight
Firefox clientWidth/clientHeight,表示文档内容区域大小 scrollWidth/scrollHeight,表示文档内容区域大小
Opera/Safari3.1+ /Chrome 视口大小 文档内容区域大小
IE(标准模式) 文档内容区域大小 视口大小

在确定文档的总高度时(包括基于视口的最小高度时),必须取得 scrollWidth/clientWidthscrollHeight/clientHeight 中的最大值,才能保证在跨浏览器的环境下得到精确结果。

1
2
3
var docHeight = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight);
var docWidth = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth);

注意:对于运行在混杂模式下的 IE,需要使用 document.body 代替 document.documentElement

通过 scrollLeftscrollTop 属性可以确定元素当前滚动的状态,也可以设置元素的滚动位置。在元素尚未滚动时,这两个属性的值都等于 0。如果元素被垂直滚动了,那么 scrollTop 的值会大于 0,且表示元素上方左侧不可见内容的像素高度。如果元素被水平滚动了,那么 scrollLeft 的值会大于 0,且表示元素左侧不可见内容的像素高度。这两个属性都是可以设置的,
因此将元素的 scrollLeftscrollTop 设置为 0,就可以重置元素的滚动位置。

1
2
3
4
5
function scrollToTop(element) {
if (element.scrollTop !==0) {
element.scrollTop = 0;
}
}

确定元素大小

IE 最先提出,而后被 W3C 标准采纳的一个方法 getBoundingClientRect(),可用来获取元素的位置信息。该方法的返回结果在每个浏览器中的具体输出不完全一样,但是都返回一个对象,这个对象有以下 6 个属性(各大浏览器均支持):

width  ==>  元素的可视宽度:width + padding-left + padding-right + border-left + border-right
height  ==>  元素的可视高度:height + padding-top + padding-bottom + border=top + border-bottom

left  ==>  元素 border 的左外边界到浏览器视口左边界的距离
top  ==>  元素 border 的上外边界到浏览器视口上边界的距离
right  ==>  元素 border 的右外边界到浏览器视口的左边界的距离
bottom  ==>  元素 border 的下外边界到浏览器视口的上边界的距离

但是,关于此方法的实现,IE8 及早期版本略有不同。IE8 及早期版本认为文档的左上角左边是 (2,2),而其他浏览器包括 IE9 还算正常,认为 (0,0) 是浏览器的左上角起点坐标。因此,跨浏览器代码需要在一开始检查一下位于 (0,0) 位置的元素的位置信息,在 IE8 及早期版本会返回 (2,2),而在其他浏览器则会返回 (0,0)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function getBoundingClientRect(element) {
if (typeof arguments.callee.offset !== 'number') {
var scrollTop = document.documentElement.scrollTop,
temp = documen.createElement('div');
temp.style.cssText = 'position: absolute; left: 0; top: 0;';
document.body.appendChild(temp);
// 减去视口的 scrollTop,防止调用函数时,窗口滚动了
arguments.callee.offset = -temp.getBoundingClientRect().top - scrollTop;
document.body.removeChild(temp);
temp = null;
}
var rect = element.getBoundingClientRect(),
offset = arguments.callee.offset;
return {
left: rect.left + offset,
right: rect.right + offset,
top: rect.top + offset,
bottom: rect.bottom + offset
};
}