深入理解元素视图的3个方法

  前面介绍了offset偏移client客户区scroll滚动,这三部分主要从属性的角度来对元素尺寸信息进行获取和修改。本文主要介绍元素视图的三个方法,包括getBoundingClientRect()、getClientRects()和elementFromPoint()

 

getBoundingClientRect()

  判断一个元素的尺寸和位置最简单的方法就是使用getBoundingClientRect()

  Element.getBoundingClientRect()方法返回一个对象,该对象提供当前元素节点的大小、它相对于视口(viewport)的位置等信息。但是,各个浏览器返回的对象包含的属性不相同

firefox: top left right bottom width height x y(其中,x=left,y=top)
chrome/safari/IE9+:top left right bottom width height
IE8-:  top left right bottom

  问题来了,该方法返回的width和height是客户区宽高client,还是滚动宽高scroll,或者是偏移宽高offset,或者是设置宽高呢

<div id="test" style="width: 100px;height: 100px;padding: 10px;line-height: 200px;border:1px solid black;overflow:scroll">内容</div>    
<script>
//chrome/safari: 220(10+200+10)
//firefox/IE: 210(10+200)
console.log(test.scrollHeight)
//103(100+10+10-17)
console.log(test.clientHeight)
//122(100+10+10+1+1)
console.log(test.offsetHeight)
//122(100+10+10+1+1)
console.log(test.getBoundingClientRect().height)
</script>

  由代码结果看出,该方法返回的宽高是偏移宽高offset

Element.getBoundingClientRect().width =  border-left-width + padding-left + width + padding-right + border-right-width

Element.getBoundingClientRect().height =  border-top-width + padding-top + height + padding-bottom + border-bottom-width

  下面来分析top、left、right、bottom这四个值

  top:   元素顶部相对于视口的纵坐标

  left:  元素左边界相对视口的横坐标

  right: 元素右边界相对视口的横坐标

  bottom:元素底部相对于视口的纵坐标

bottom = top + height
right = left + width

  [注意]该方法的所有属性值都没有单位,且给定的是元素在页面中相对于视口的位置

  问题又来了,相对于视口和相对于页面有什么区别。理论上,与absolute和fixed的区别类似,但表现上与它们正相反。发生滚动时,fixed元素保持不动是为了保持与视口的原始距离;而发生滚动时,getBoundingClientRect()方法的top、left、right、bottom这四个值相应的发生改变,是因为元素位置移动了,与视口距离自然也改变了

bug

  IE7-浏览器把视口的左上角坐标设置为(2,2),其他浏览器则将(0,0)作为起点坐标

<body style="margin:0">
<div id="test" style="width: 100px;height: 50px;padding: 10px;line-height: 200px;overflow:scroll;border:1px solid black">内容</div>    
<script>
//chrome/firefox/safari/IE8+ 0 72(50+10+10+1+1)
//IE7- 2 74(72+2)
console.log(test.getBoundingClientRect().top,test.getBoundingClientRect().bottom)

//chrome/firefox/safari/IE8+ 0 122(100+10+10+1+1)
//IE71 2 124(122+2)
console.log(test.getBoundingClientRect().left,test.getBoundingClientRect().right)
</script>
</body>

兼容

  可以利用IE7-浏览器中特性节点的specified属性实现浏览器识别

function getBoundingClientRect(obj){
    var temp = obj.getBoundingClientRect();
    //IE7-浏览器
    if(Boolean(obj.attributes[0]) && !obj.attributes[0].specified){
        return{
            left: temp.left -2,
            top: temp.top -2,
            right: temp.right -2,
            bottom: temp.bottom -2
        }
    }else{
        return temp;
    }    
}

 

getClientRects()

  getClientRects()方法与getBoundingClientRect()不同,该方法是一个返回元素的数个矩形区域的类数组对象。每个类数组对象的参数与getBoundingClientRect()方法相同,每个矩形都有bottom、height、left、right、top和width六个属性,表示它们相对于视口的四个坐标,以及本身的高度和宽度

  如果应用于块级元素,则getClientRects()[0]和getBoundingClientRect()的属性返回相同的值,且IE7-浏览器在getClientRects()方法中,同样存在视口左上角坐标被设置为(2,2)的bug

<body style="margin:0">
<div id="test" style="width: 100px;height: 50px;padding: 10px;line-height: 200px;overflow:scroll;border:1px solid black">内容</div>    
<script>
//其他浏览器返回0 0, IE7-浏览器返回 2 2 
console.log(test.getClientRects()[0].top,test.getBoundingClientRect().top)
//其他浏览器返回0 0, IE7-浏览器返回 2 2 
console.log(test.getClientRects()[0].left,test.getBoundingClientRect().left)
//72(50+10+10+1+1) 72
console.log(test.getClientRects()[0].height,test.getBoundingClientRect().height)
</script>

  实际上,该方法主要用于内联元素,内联元素有多少行,该方法返回的对象有多少个成员。这个方法主要用于判断行内元素是否换行,以及行内元素的每一行的位置偏移

<body style="margin:0">
<div style="width:100px;"><span id="el">
Hello World
Hello World
Hello World
</span></div>
<script>
console.log(el.getClientRects().length); // 3
console.log(el.getClientRects()[0].left); // 0
console.log(el.getClientRects()[0].right); // 88
console.log(el.getClientRects()[0].bottom); // 17
console.log(el.getClientRects()[0].height); // 16
console.log(el.getClientRects()[0].width); // 88
</script>
</body>

 

elementFromPoint()

  getBoundingClientRect(x,y)方法使我们能在视口中判定元素的位置。但有时我们想反过来,判定在视口中的指定位置上有什么元素。这可以用Document对象的elementFromPoint()方法来判定。传递X和Y坐标(相对于视口),该方法选择在指定坐标的最上层和最里层的Element对象。如果指定的点在视口以外,elementFromPoint()返回null

  [注意]最上层是指z-index最大的元素;最里层是指最里层的子元素

  这个方法可以用来检测元素是否发生重叠或是碰撞

<body style="margin:0">
<div id="test" style="width: 100px;height: 100px;">
    <span id="span1">123</span>
</div>
<script>
console.log(document.elementFromPoint(2,2).id); //span1
</script>

results matching ""

    No results matching ""