元素尺寸(~视图)
关于元素尺寸,一般地,有偏移量 offset、客户区 client 和滚动 scroll。
偏移量 offset(只读)
- 所有偏移量属性都是只读的。
- 如果给元素设置了 display:none,则它的偏移量属性都为 0。
- 每次访问偏移量属性都需要重新计算。重复访问偏移量属性需要耗费大量的性能,所以要尽量避免重复访问这些属性;如果需要重复访问,则把它们的值保存在变量中,以提高性能。
主要涉及 4 个属性:offsetLeft、offsetTop、offsetHeight、offsetWidth和一个偏移参照 — 定位父级 offsetParent。
[1] 定位父级 offsetParent
人们并没有把 offsetParent 翻译为偏移父级,而是定位父级,很大原因是 offsetParent 与定位有关。
offsetParent 的定义是:与当前元素最近的经过定位(不等于 static)的父级元素,主要分为下列几种情况:
- 元素自身有 fixed 定位,offsetParent 的结果为 null( firfox存在兼容性问题,返回 <body>)。
- 元素自身无 fixed 定位,且父级元素都未经过定位,offsetParent 的结果为 <body>。
- 元素自身无 fixed 定位,且父级元素存在经过定位的元素,offsetParent 的结果为第一个定位元素。
- <body>元素的 parentNode 是 null。
[2] 偏移量
偏移量,主要涉及 offsetHeight、offsetWidth、offsetLeft、offsetTop 这 4 个属性。
若存在垂直滚动条,offsetWidth包括垂直滚动条的宽度;若存在水平滚动条,offsetHeight包括水平滚动条的高度。
offsetWidth:表示元素在水平方向上占用的空间大小,无单位(以像素px计)
1 offsetWidth = border-left-width + padding-left + width + padding-right + border-right-width;offsetHeight:表示元素在垂直方向上占用的空间大小,无单位(以像素px计)
1 offsetHeight = border-top-width + padding-top + height + padding-bottom + border-bottom-widthoffsetTop:表示元素的上外边框至offsetParent元素的上内边框之间的像素距离
offsetLeft:表示元素的左外边框至offsetParent元素的左内边框之间的像素距离
123456789 <div id="out" style="padding: 5px;position: relative;background-color: pink;margin: 6px;border:1px solid black"><div id="test" style="width:100px; height:100px; margin:10px;background-color:green;"></div></div><script>// 15=test.marginTop(10) + out.paddingTop(5)alert(test.offsetTop);// 15=test.marginLeft(10) + out.paddingLeft(5)alert(test.offsetLeft);</script>
[3] 页面偏移:要知道某个元素在页面上的偏移量,将这个元素的offsetLeft和offsetTop与其offsetParent的相同属性相加,并加上offsetParent的相应方向的边框,如此循环直到根元素,就可以得到元素到页面的偏移量。
在默认情况下,IE8-如果使用 currentStyle() 方法获取<html> 和 <body>(甚至普通div元素)的边框宽度都是 medium,而如果使用 clientLeft(或clientTop) 获取边框宽度,则是实际的数值。
12 html,body{border: 0;}body{margin:0;}
123456789101112131415161718 function getElementLeft(element){var actualLeft = element.offsetLeft;var current = element.offsetParent;while(current != null){actualLeft += current.offsetLeft + current.clientLeft;current = current.offsetParent;}return actualLeft + 'px';}function getElementTop(element){var actualTop = element.offsetTop;var current = element.offsetParent;while(current != null){actualTop += current.offsetTop + current.clientTop;current = current.offsetParent;}return actualTop + 'px';}
123456789 <div style="padding: 20px;border:1px solid black;position:absolute;"><div id="test" style="width:100px; height:100px; margin:10px;"></div></div><script>// 其他浏览器返回31(10+20+1),而IE7-浏览器返回21((20和10的较大值)+1)console.log(getElementTop(test));// 所有浏览器返回31(10+20+1)console.log(getElementLeft(test));</script>
客户区 Client(只读)
客户区 client,指的是元素内容及其内边距所占据的空间大小;滚动条不计算在内。
- clientHeight:返回元素节点的客户区高度
1 clientHeight = padding-top + height + padding-bottom
- clientWidth:返回元素节点的客户区宽度
1 clientWidth = padding-left + width + padding-right
- clientLeft:返回左边框的宽度
- clientTop:返回上边框的宽度
如果display为inline时,clientLeft属性和clientTop属性都返回0。
[ 页面大小 ] 常用 document.documentElement 的 client属性来表示页面大小( 不包含滚动条宽度)。
- 在IE7-浏览器中,<html>元素默认存在垂直滚动条。
1234567 <body style="overflow:scroll"><script>//1903(1920-17)console.log(document.documentElement.clientWidth);//930(947-17)console.log(document.documentElement.clientHeight);</script>
- 另一对常用的表示页面大小的属性是 window.innerHeight 和 innerWidth属性( 包含滚动条宽度)。
innerHeight和innerWidth表示的是浏览器窗口大小减去菜单栏、地址栏等剩余的页面尺寸,由于滚动条是属于页面的,所以包含滚动条。但,IE8-不支持innerHeight和innerWidth属性。
如果没有滚动条,这两类属性在电脑端表示同样的值,但是却表示不同的含义。在移动端,innerWidth和innerHeight表示的是视觉视口,即用户正在看到的网站的区域;而document.documentElement.clientWidth和clientHeight表示的是布局视口,指CSS布局的尺寸。
页面的客户区大小和页面的实际大小是不同的,页面的实际大小将由 scroll 滚动大小来表示。
[3] 注意事项
- 所有客户区client属性都是只读的。
- 如果给元素设置了display:none,则客户区client属性都为0。
- 每次访问客户区client属性都需要重新计算,重复访问需要耗费大量的性能,所以要尽量避免重复访问这些属性。如果需要重复访问,则把它们的值保存在变量中,以提高性能。
滚动 Scroll
[1] 滚动宽高(只读)
- scrollHeight:表示元素的总高度,包括由于溢出而无法展示在网页的不可见部分
- scrollWidth:表示元素的总宽度,包括由于溢出而无法展示在网页的不可见部分
- 没有滚动条时,scrollHeight与clientHeight属性结果相等,scrollWidth与clientWidth属性结果相等。
- 存在滚动条时,但元素设置宽高大于等于元素内容宽高时,scroll和client属性的结果相等。
- 存在滚动条,但元素设置宽高小于元素内容宽高,即存在内容溢出的情况时,scroll 属性大于 client 属性
[ 页面尺寸 ] document.documentElement.clientHeight表示页面的可视区域的尺寸,
而 document.documentElement.scrollHeight 表示 html 元素内容的实际尺寸。但是由于各个浏览器表现不一样:
- html元素没有滚动条时,IE和firefox的client和scroll属性始终相同,且返回可视区的尺寸大小;而safari和chrome表现正常,clientHeight返回可视区域大小,而scrollHeight返回元素内容大小。
- html存在滚动条时,各浏览器表现正常。clientHeight返回可视区域大小,而scrollHeight返回元素内容大小。
[ 兼容 ] 因此要取得文档实际高度时,要取得<html>元素的scrollHeight和clientHeight的最大值。
12 var docHeight = Math.max(document.documentElement.scrollHeight,document.documentElement.clientHeight);var docWidth = Math.max(document.documentElement.scrollWidth,document.documentElement.clientWidth);
[2] 滚动长度(可写)
- scrollTop:表示被隐藏在内容区域上方的像素数。元素未滚动时,scrollTop的值为0,如果元素被垂直滚动了,scrollTop的值大于0,且表示元素上方不可见内容的像素宽度。
- scrollLeft:表示被隐藏在内容区域左侧的像素数。元素未滚动时,scrollLeft的值为0,如果元素被水平滚动了,scrollLeft的值大于0,且表示元素左侧不可见内容的像素宽度。
当滚动条滚动到内容底部时,符合以下等式
1 scrollHeight == scrollTop + clientHeight与 scrollHeight 和 scrollWidth 属性不同的是,scrollLeft 和 scrollTop 是可写的。
12345678910 <div id="test" style="width: 100px;height: 100px;padding: 10px;margin: 10px;border: 1px solid black;overflow:scroll;font-size:20px;line-height:200px;">内容</div><button id='btn1'>向下滚动</button><button id='btn2'>向上滚动</button><script>btn1.onclick = function(){test.scrollTop += 10;}btn2.onclick = function(){test.scrollTop -= 10;}</script>// scrollLeft和scrollTop赋值为负值时,并不会报错,而是静默失败。
[ 页面滚动 ] 理论上,通过document.documentElement.scrollTop和scrollLeft可以反映和控制页面的滚动;但是chrome和safari浏览器是通过document.body.scrollTop和scrollLeft来控制的。
123456789 <body style="height:1000px"><button id='btn1' style="position:fixed;top:0;">点击</button><div id="result" style="position:fixed;top:30px;"></div><script>btn1.onclick = function(){result.innerHTML = 'html的scrollTop:' + document.documentElement.scrollTop +';body的scrollTop:' + document.body.scrollTop;}</script></body>所以,页面的滚动高度兼容写法是:
1 var docScrollTop = document.documentElement.scrollTop || document.body.scrollTop
[ 代码示例 - 回到顶部?!] 可以利用scrollTop来实现回到顶部的功能
12345 function scrollTop(){if((document.body.scrollTop || document.documentElement.scrollTop) != 0){document.body.scrollTop = document.documentElement.scrollTop = 0;}}
1234567891011 <body style="height:1000px"><button id='btn' style="position:fixed">回到顶部</button><script>function scrollTop(){if((document.body.scrollTop || document.documentElement.scrollTop) != 0){document.body.scrollTop = document.documentElement.scrollTop = 0;}}btn.onclick = scrollTop;</script></body>
[3] 页面滚动长度(只读):window的两个只读属性可以获取整个页面滚动的像素值:pageXOffset和pageYOffset。
- pageXOffset:表示水平方向上页面滚动的像素值。
- pageYOffset:表示垂直方向上页面滚动的像素值。
12345678910 // IE8-浏览器不支持<body style="height:1000px"><button id='btn1' style="position:fixed;top:0;">点击</button><div id="result" style="position:fixed;top:30px;"></div><script>btn1.onclick = function(){result.innerHTML = 'pageYOffset:' + window.pageYOffset;}</script></body>
[4] 滚动方法
[4-1] scrollTo(x,y):滚动当前window中显示的文档,让文档中由坐标 x 和 y 指定的点位于显示区域的左上角。
12345 <body style="height:1000px"><button id='btn' style="position:fixed">滚动</button><script>btn.onclick = function(){scrollTo(0,0);}</script>[4-2] scrollBy(x,y):滚动当前 window 中显示的文档,x 和 y 指定滚动的相对量。
1234567 <body style="height:1000px"><button id='btn1' style="position:fixed">向下滚动</button><button id='btn2' style="position:fixed;top:40px">向上滚动</button><script>btn1.onclick = function(){scrollBy(0,100);}btn2.onclick = function(){scrollBy(0,-100);}</script>[ 代码示例 ] 利用 scrollBy() 加 setInterval 计时器实现简单的快速滚动功能。
1234567891011121314 <body style="height:1000px"><button id='btn1' style="position:fixed">开始滚动</button><button id='btn2' style="position:fixed;top:40px">停止滚动</button><script>var timer = 0;btn1.onclick = function(){timer = setInterval(function(){scrollBy(0,10);},100)}btn2.onclick = function(){clearInterval(timer);timer = 0;}</script>[4-3] Element.scrollIntoView():滚动当前元素,进入浏览器的可见区域
该方法可以接受一个布尔值作为参数。
- 如果为 true(默认),表示元素的顶部与当前区域的可见部分的顶部对齐(前提是当前区域可滚动);
- 如果为 false,表示元素的底部与当前区域的可见部分的尾部对齐(前提是当前区域可滚动)。
123456789101112 <body style="height:1000px"><div id="test" style="height:100px;width:100px;position:absolute;left:0;top:500px;background-color:green"></div><button id='btn1' style="position:fixed">滚动到页面开头</button><button id='btn2' style="position:fixed;top:40px">滚动到页面结尾</button><script>btn1.onclick = function(){test.scrollIntoView();};btn2.onclick = function(){test.scrollIntoView(false);}</script>
[5] 滚动事件
scroll 事件是在 window 对象上发生的,它表示的是页面中相应元素的变化。当然,也可以用在有滚动条的元素上。
12345678 <body style="height:1000px"><div id="result" style="position:fixed;top:10px;"></div><script>window.onscroll = function(){result.innerHTML = '页面的scrollTop:' + (document.documentElement.scrollTop||document.body.scrollTop);}</script></body>
元素视图方法
偏移量 offset、客户区 client和滚动 scroll,主要从属性的角度来对元素尺寸信息进行获取和修改;
以下为元素视图的三个方法:getBoundingClientRect()、getClientRects()和elementFromPoint()。
[1] getBoundingClientRect()
判断一个元素的尺寸和位置最简单的方法就是使用 getBoundingClientRect()。
Element.getBoundingClientRect()方法返回一个对象,该对象提供当前元素节点的大小、它相对于视口(viewport)的位置等信息。但是,各个浏览器返回的对象包含的属性不相同.
123 firefox: top left right bottom width height x y(其中,x=left,y=top)chrome/safari/IE9+:top left right bottom width heightIE8-: top left right bottom问题在于,该方法返回的width和height是客户区宽高client,还是滚动宽高scroll,或者是偏移宽高offset,或者是设置宽高呢?
123456789101112 <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。
123 Element.getBoundingClientRect().width = border-left-width + padding-left + width + padding-right + border-right-widthElement.getBoundingClientRect().height = border-top-width + padding-top + height + padding-bottom + border-bottom-width下面来分析top、left、right、bottom这四个值
top: 元素顶部相对于视口的纵坐标
left: 元素左边界相对视口的横坐标
right: 元素右边界相对视口的横坐标
bottom:元素底部相对于视口的纵坐标
12 bottom = top + heightright = left + width[注意]该方法的所有属性值都没有单位,且给定的是元素在页面中相对于视口的位置
问题又来了,相对于视口和相对于页面有什么区别。理论上,与absolute和fixed的区别类似,但表现上与它们正相反。发生滚动时,fixed元素保持不动是为了保持与视口的原始距离;而发生滚动时,getBoundingClientRect()方法的top、left、right、bottom这四个值相应的发生改变,是因为元素位置移动了,与视口距离自然也改变了
bug
IE7-浏览器把视口的左上角坐标设置为(2,2),其他浏览器则将(0,0)作为起点坐标。
123456789101112 <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属性实现浏览器识别
1234567891011121314 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;}}
[2] getClientRects()
getClientRects() 方法与 getBoundingClientRect() 不同,该方法是一个返回元素的数个矩形区域的类数组对象。每个类数组对象的参数与 getBoundingClientRect() 方法相同,每个矩形都有 bottom、height、left、right、top 和 width 六个属性,表示它们相对于视口的四个坐标,以及本身的高度和宽度。
如果应用于块级元素,则 getClientRects()[0] 和 getBoundingClientRect() 的属性返回相同的值,且 IE7- 浏览器在 getClientRects() 方法中,同样存在视口左上角坐标被设置为 (2,2) 的 bug。
12345678910 <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 2console.log(test.getClientRects()[0].top,test.getBoundingClientRect().top)//其他浏览器返回0 0, IE7-浏览器返回 2 2console.log(test.getClientRects()[0].left,test.getBoundingClientRect().left)//72(50+10+10+1+1) 72console.log(test.getClientRects()[0].height,test.getBoundingClientRect().height)</script>实际上,该方法主要用于内联元素,内联元素有多少行,该方法返回的对象有多少个成员。这个方法主要用于判断行内元素是否换行,以及行内元素的每一行的位置偏移。
123456789101112131415 <body style="margin:0"><div style="width:100px;"><span id="el">Hello WorldHello WorldHello World</span></div><script>console.log(el.getClientRects().length); // 3console.log(el.getClientRects()[0].left); // 0console.log(el.getClientRects()[0].right); // 88console.log(el.getClientRects()[0].bottom); // 17console.log(el.getClientRects()[0].height); // 16console.log(el.getClientRects()[0].width); // 88</script></body>
[3] elementFromPoint()
getBoundingClientRect(x,y)方法使我们能在视口中判定元素的位置。但有时我们想反过来,判定在视口中的指定位置上有什么元素。这可以用Document对象的elementFromPoint()方法来判定。传递X和Y坐标(相对于视口),该方法选择在指定坐标的最上层和最里层的Element对象。如果指定的点在视口以外,elementFromPoint()返回null。
[注意]最上层是指z-index最大的元素;最里层是指最里层的子元素。
这个方法可以用来检测元素是否发生重叠或是碰撞。
1234567 <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>