元素的位置

 

获取元素的位置

Element接口提供了众多属性用于获取元素的各种位置(单位像素),这些属性通常为只读,并且只有元素已经在DOM树中且CSS属性的display不为none时才有效。

偏移位置

偏移位置指的是元素相对于其父级元素的当前位置。

  • Element.offsetTop
    元素的左上角与父容器(offsetParent对象)左上角的垂直距离
  • Element.offsetLeft
    元素的左上角与父容器(offsetParent对象)左上角的水平距离

定位级的父级元素并不等同于于文档结构中的父级元素(Node.parentElement),通过Element.offsetParent属性获取。

元素的offsetParent的计算遵从下面的算法:

  1. 没有关联一个CSS布局框的元素,其offsetParent是null
  2. 根元素,其offsetParent是null
  3. BODY元素,其offsetParent是null
  4. position为fixed的元素,其offsetParent是null(有些浏览器返回body),实际上是body
  5. AREA元素,且其某个祖先元素是MAP元素,其offsetParent为其最近的MAP祖先元素
  6. position为absolute, relative的元素,其offsetParent总是为DOM树中距其最近的已定位的祖先元素
  7. position为static的元素,如果在表格内, 其offsetParent为其最近的TD、TH 或 TABLE元素,如果不在表格内,其offsetParent为DOM树中距其最近的已定位的祖先元素。

备注:

  • 已定位的祖先元素: position不为static的元素

绝对位置

绝对位置指的是元素在文档页面中的位置,即相对于文档左上原点的当前位置。

每个元素都有offsetTop和offsetLeft属性,表示该元素的左上角与父容器(offsetParent对象)左上角的距离。所以,只需要将这两个值进行累加,就可以得到该元素的绝对坐标。

function getPagePosition(el){
  var left = el.offsetLeft,
      top = el.offsetTop,
      p = el.offsetParent;         
 while (p !== null){
   left += p.offsetLeft;
     top += p.offsetTop;
   p = p.offsetParent;
 }
 return {
     "top" : top,
     "left": left
  };
}

另外,使用getBoundingClientRect()方法可以快速的获取元素的绝对位置

function getPagePosition(el){
  var obj = el.getBoundingClientRect();
  return {
     "left": obj.left + window.pageXOffset,
     "top": obj.top + window.pageYOffset
  }
}

相对位置

网页元素的相对位置,指该元素左上角相对于浏览器窗口左上角的坐标。

function getViewportPosition(el){
  var pagePos= getPagePosition(el);
  return {
     "left": pagePos.left - window.pageXOffset,
     "top": pagePos.top - window.pageYOffset
  }
}

另外,使用getBoundingClientRect()方法可以快速的获取元素的绝对位置

function getViewportPosition(el){
  var obj = el.getBoundingClientRect();
  return {
     "left": obj.left,
     "top": obj.top
  }
}

工具函数列表

uDOMlib所提供的工具函数之中,有一部分与元素位置相关,下面列出部分函数的源代码实现以供参考。

position()

返回或设定相对于定位父元素左上角的位置。 如果只处理水平或垂直位置的话可调用left()或top()方法。

        position : function(node,coords) {
            if (value == undefined) {
                var // Get *real* offsetParent
                    offsetParent = this.offsetParent(node),
                    // Get correct offsets
                    offset = this.boundingPosition(node),
                    parentOffset = this.boundingPosition(offsetParent),
                    mex = this.marginExtents(node),
                    pbex = this.borderExtents(offsetParent);

                // Subtract parent offsets and element margins
                return {
                    top: offset.top - pbex.top - mex.top,
                    left: offset.left - pbex.left - mex.left
                }
            } else {
                var props = {
                        top: coords.top,
                        left: coords.left
                    }

                if (styler.css(node,"position") == "static") {
                    props['position'] = "relative";
                }
                styler.css(props);
                return this;
            }
        },

boundingPosition()

返回或设定相对于视口左上原点的位置

boundingPosition : function(node,coords) {
    if (coords === undefined) {
        return rootNodeRE.test(node.nodeName) ?  { top: 0, left: 0 } : node.getBoundingClientRect();
    } else {
        var // Get *real* offsetParent
            offsetParent = this.offsetParent(node),
            // Get correct offsets
            parentOffset =  this.boundingPosition(offsetParent),
            mex = this.marginExtents(node),
            pbex = this.borderExtents(offsetParent);

            this.position(node,{
                top: coords.top - parentOffset.top - mex.top - pbex.top,
                left: coords.left - parentOffset.left - mex.left - pbex.left
            });
            return this;
   }
}

pagePosition()

返回或设定相对于页面左上原点的位置

pagePosition: function(node,coords) {
    if (coords === undefined) {
        var obj = node.getBoundingClientRect()
        return {
           left: obj.left + window.pageXOffset,
           top: obj.top + window.pageYOffset
        }
    } else {
        var // Get *real* offsetParent
           offsetParent = this.offsetParent(node),
            // Get correct offsets
           parentOffset =  this.pagePosition(offsetParent),
           mex = this.marginExtents(node),
           pbex = this.borderExtents(offsetParent);
         this.position(node,{
             top: coords.top - parentOffset.top - mex.top - pbex.top,
             left: coords.left - parentOffset.left - mex.left - pbex.left
         });
         return this;
     }
}

offsetParent()

返回元素定位上的父元素。 offsetParent()内部调用了Element.offsetParent属性,并作了一定的封装处理,以保证返回的父元素一定是已定位元素(position不是static)。 以下是函数的实现代码

function offsetParent(node) {
  var parent = node.offsetParent || document.body;
  while (parent) {
     if (!rootNodeRE.test(parent.nodeName) {
        break;
     }
     
     if (getComputedStyle(parent).position != "static")) {
        break;
     }   
     parent = parent.offsetParent;
     
  }    
  return parent;
}

scrollLeft

返回或设置元素的滚动条的水平位置。滚动条的水平位置指的是从其左侧滚动过的像素数。当滚动条位于最左侧时,位置是 0。

scrollLeft: function(node,value) {
    var hasScrollLeft = "scrollLeft" in node;
    if (value === undefined) {
      	return hasScrollLeft ? node.scrollLeft : node.pageXOffset
    } else {
      	if (hasScrollLeft){
       		node.scrollLeft = value;
       	} else {
       		node.scrollTo(value,node.scrollY);
       	}
       	return this;
    }
}

scrollTop

返回或设置元素的滚动条的垂直位置。滚动条的垂直位置指的是从其上侧滚动过的像素数。当滚动条位于最上侧时,位置是 0。

scrollTop: function(node,value) {
    var hasScrollTop = "scrollTop" in node;
            
    if (value === undefined) {
      	return hasScrollTop ? node.scrollTop : node.pageYOffset
    } else {
        if (hasScrollTop) {
       	   	node.scrollTop = value;
        } else {
               	node.scrollTo(node.scrollX, value);
        }
        return this;
    }
}