DOM元素位置与偏移量解析

前言:本文是可见曝光的前驱文章。要了解可见曝光,首先要明确dom元素位置和页面偏移量等基础知识。而这一块一直以来,由于不同浏览器的实现不同,也因为特别容易混淆,在JS中一直是不容易搞清楚的环节。本文详细解析了各属性的意义,在阐述时辅以图片帮助理解,并探讨了引申内容视口坐标和文档坐标。

本文所有内容都只能在IE8以上环境运行正确,IE8及以下在这方面表现很特殊,有需要的朋友可以自己研究一下。

尺寸相关 偏移量相关 滚动相关 window相关
clientTop offsetTop scrollTop pageXOffset
clientLeft offsetLeft scrollLeft pageYOffset
clientHeight offsetHeight scrollHeight innerHeight
clientWidth offsetWidth scrollWidth innerWidth
-- offsetParent -- --

1. 尺寸相关

从最简单的开始,

clientTop和clientLeft:是指上边框和左边框的宽度。

clientHeight和clientWidth:描述dom元素内尺寸,是指元素内容+padding,不包括border,margin和滚动条。

具体见图1: 图1

2. 偏移量相关

offsetParent

offsetParent returns a reference to the object which is the closest (nearest in the containment hierarchy) positioned containing element. If the element is non-positioned, the nearest table cell or root element (html in standards compliant mode; body in quirks rendering mode) is the offsetParent. offsetParent returns null when the element has style.display set to "none". The offsetParent is useful because offsetTop and offsetLeft are relative to its padding edge.

简而言之,offsetParent会返回当前元素的、最近的、进行过CSS定位(position为absolute或者relative)的祖先元素。 当某个元素及其DOM结构层次中元素都未进行CSS定位时,则这个元素的offsetParent属性的取值为根元素。更确切地说,这个元素的各种偏移量计算(offsetTop、offsetLeft等)的参照物为Body元素。

offsetTop和offsetLeft:是指该元素的左上角(边框外边缘)与已定位的祖先容器(offsetParent对象)左上角(边框内边缘)的距离

offsetWidth和offsetHeight:元素外尺寸(当没有其他元素遮挡时,应该在页面上显示的大小),是指元素内容+padding+border,不包括margin。body对象的offsetWidth不包含滚动条,其他元素由于滚动条在border之内,则一定是包含的。

那么对于非body元素:

offsetWidth - clientWidth = 边框宽度 * 2 + 滚动条宽度;

具体见图2:

图2

3. 滚动相关

scrollWidth和scrollHeight:是元素的内容区域加上内边距加上溢出尺寸,当内容正好和内容区域匹配没有溢出时,这些属性与clientWidth和clientHeight相等

scrollLeft和scrollTop:隐藏起来的内容区的像素数。这两个数据是可以编辑的,通过编辑这两个数值可以调整滚动条的位置。

让我们看一下来自于《JAVASCRIPT高级程序设计》(第二版)的一张图

图3

4. window相关

innerWidth和innerHeight:指代窗口中文档显示区域的宽度、高度,不包括菜单栏、工具栏等部分,但包括滚动条

pageXOffset和pageYOffset:指代窗口向右/向下滚动的像素数。通过它们可以得到滚动条的位置,即滚动偏移量。默认值为0。

以上四个属性都是window对象的独有属性,支持各浏览器(IE8及以下不支持)。都可读写,我现在没有发现设置这几个值有什么用,有知道的朋友也望不吝告知。

5. 文档坐标与视口坐标

坦白来说,我们讨论前面各个宽高的含义,最终还是要落在文档和视口坐标的计算上才有意义。当我们分析一个dom元素在页面上的位置时,有两种坐标:

视口坐标:当前窗口显示的部分(不包括滚动条)中,选定的元素所在的位置

文档坐标:指的是整个页面部分,而不仅仅是当前窗口显示的部分。例如,我们常说这个页面有三屏,意思就是指页面有三个窗口高度那么高。如果文档小于视口,那么文档坐标就等于视口坐标。而一般来说要在二者中进行切换,要加上滚动偏移量。

视口坐标计算:

得益于函数getBoundingClientRect(), 我们可以轻松的获得元素的视口坐标。函数返回一个有left、right、top、bottom、width、height属性的对象,分别表示元素四个位置的相对于视口的坐标和元素的宽高。getBoundingClientRect所返回的坐标包含元素的内边距和边框,不包含外边距。对于body元素,不包含滚动条。IE8及以上支持,其中IE8中返回的对象没有width和height参数,不过我们可以使用bottom-top轻松算出。

使用方法是:

var coordinate = elem.getBoundingClientRect()

文档坐标计算:

使用offsetParent,我们可以一直向上寻找父节点,直至body元素;通过累加与父节点之间的距离,得到相应的文档坐标。

// 得到文档坐标
function getElementPosition(e) {
    var x = 0;
    var    y = 0;
    while (e != null) {
        x += e.offsetLeft;
        y += e.offsetTop;
        e = e.offsetParent;
    }
    return {
        x: x,
        y: y
    };
}

6. 其他

还有一些其他可能用到的函数:

滚动偏移量:

// 得到滚动偏移量
function getScrollOffsets(w) {
    var w = w || window;
    if (w.pageXoffset != null) {
        return {
            x: w.pageXoffset,
            y: pageYoffset
        };
    }
    var d = w.document;
    if (document.compatMode == "CSS1Compat")
        return {
            x: d.documentElement.scrollLeft,
            y: d.documentElement.scrollTop
        };
    return {
        x: d.body.scrollLeft,
        y: d.body.scrollTop
    };
}

视口尺寸:

// 得到视口的尺寸(不包含滚动条)
function getViewPortSize(w) {
    var w = w || window;
    var scrollbarSize = getScrollbarSize();
    if (w.innerWidth != null)
        return {
            w: w.innerWidth - scrollbarSize.x,
            h: w.innerHeight - scrollbarSize.y
        };
    var d = w.document;
    if (document.compatMode == "CSS1Compat")
        return {
            w: d.documentElement.clientWidth,
            h: d.documentElement.clientHeight
        };
    return {
        w: d.body.clientWidth,
        h: d.body.clientHeight
    };
}
// 得到滚动条的宽度
function getScrollbarSize() {
    var scrollbarSize = {};
    // 首先判断页面是否有横竖滚动条
    var x = (document.documentElement.scrollLeft == 0) ? document.body.scrollLeft : document.documentElement.scrollLeft;
    var y = (document.documentElement.scrollTop == 0) ? document.body.scrollTop : document.documentElement.scrollTop;
    var scrollbarWidth;
    // 若有滚动条,得到滚动条的宽度
    if (x != 0 || y != 0) {
        var oP = document.createElement('p');
        oP.style.width = '100px';
        oP.style.height = '100px';
        oP.style.overflowY = 'scroll';
        document.body.appendChild(oP);
        scrollbarWidth = oP.offsetWidth - oP.clientWidth;
    }
    scrollbarSize.x = (y != 0) ? scrollbarWidth : 0;
    scrollbarSize.y = (x != 0) ? scrollbarWidth : 0;
    return scrollbarSize;
}

results matching ""

    No results matching ""