当在 Web 浏览器中查看 HTML 文档时,DOM 节点被解析,并被渲染成盒模型(如下图),有时我们需要知道一些信息,比如盒模型的大小,盒模型在浏览器中的位置等等,本文我们就来详细了解下元素节点的几何量以及滚动几何量。
获取元素相对于 offsetParent 的 offsetTop 以及 offsetLeft 值
使用 offsetTop 和 offsetLeft 属性,我们可以获取元素节点相对于 offsetParent 的偏移像素量。这些元素节点属性告诉我们某元素上部与左侧边框最外沿到 offsetParent 上部和左侧边框内部的距离,以像素为单位。 offsetParent 的值判定依据为查找离该元素最近的 CSS 定位值不为 static 的祖先元素。如果没有,则 <body>
元素,某些人或称之为“文档”(而不是浏览器视区),即为 offsetParent 值。如果,在查询祖先过程中,找到 <td>
<th>
或 <table>
元素,且它的定位为 static,则它将成为 offsetParent 的值。
<style>
body {
margin: 0;
}
#blue {
width: 100px;
height: 100px;
background-color: blue;
border: 10px solid gray;
padding: 25px;
margin: 25px;
}
#red {
width: 50px;
height: 50px;
background-color: red;
border: 10px solid gray;
}
</style>
<div id='blue'>
<div id='red'></div>
</div>
<script>
var myDiv = document.querySelector('#red');
console.log(myDiv.offsetLeft); // 60
console.log(myDiv.offsetTop); // 60
console.log(myDiv.offsetParent); // body
</script>
如果将以上代码中的蓝色 div 元素定位改为 absolute,那么将会得到完全不一样的结果,因为蓝色 div 会成为红色 div 的offsetParent。
offsetLeft 和 offsetTop 属性和包含元素有关,包含元素的引用保存在 offsetParent 属性中,而 offsetParent 属性不一定与 parentNode 的值相等。例如,<td>
元素的 offsetParent 是作为其祖先元素的 <table>
元素,因为 table 元素是在 DOM 层次中距离 td 元素最近的一个具有大小的元素。
要想知道某个元素在页面上的偏移量,将这个元素的 offsetLeft 和 offsetTop 与其 offsetParent 的相同属性相加,如此循环直至根元素,就可以得到一个基本准确的值。
function getElementLeft(element) {
var actualLeft = element.offsetLeft;
var current = element.offsetParent;
while (current !== null) {
actualLeft += current.offsetLeft;
current = current.offsetParent;
}
return actualLeft;
}
function getElementTop(element) {
var actualTop = element.offsetTop;
var current = element.offsetParent;
while (current !== null) {
actualTop += current.offsetTop;
current = current.offsetParent;
}
return actualTop;
}
对于简单的 CSS 布局的页面,这两函数可以得到非常准确的结果。但是对于利用表格和内嵌框架布局的页面,由于不同浏览器实现这些元素的方式不同,因此得到的值就不太精确了。一般来说,页面中的所有元素都会被包含在几个 div 元素中,而这些 div 元素的 offsetParent 又是 body 元素,所以 getElementLeft() 与 getElementTop() 会返回和 offsetLeft 和 offsetTop 相同的值。
使用 getBoundingClientRect() 获取元素相对于视区的 Top、Right、Buttom 以及 Left 边沿偏移量
使用 getBoundingClientRect() 方法,我们可以获取元素外边沿的位置。元素调用 getBoundingClientRect() 方法后会返回一个对象,该对象包含 top、right、bottom 以及 left 属性。
还是上面的代码:
<style>
body {
margin: 0;
}
#blue {
width: 100px;
height: 100px;
background-color: blue;
border: 10px solid gray;
padding: 25px;
margin: 25px;
}
</style>
<div id='blue'></div>
<script>
var divEdges = document.querySelector('#blue').getBoundingClientRect();
console.log(divEdges.top); // 25
console.log(divEdges.right); // 195
console.log(divEdges.bottom); // 195
console.log(divEdges.left); // 25
</script>
元素大小的获取
<style>
body {
margin: 0;
}
#blue {
width: 100px;
height: 50px;
background-color: blue;
border: 10px solid gray;
padding: 25px;
margin: 25px;
}
</style>
<div id='blue'></div>
<script>
var div = document.querySelector('#blue')
, divEdges = div.getBoundingClientRect();
// 获取元素大小
// border + pading + content
console.log(divEdges.width, divEdges.height); // 170 120
// 也能取得元素大小
console.log(div.offsetWidth, div.offsetHeight); // 170 120
// padding + content 不含 border
console.log(div.clientWidth, div.clientHeight); // 150 100
</script>
获取滚动几何量
<style>
* {
margin: 0;
padding: 0;
}
div {
width: 100px;
height: 100px;
overflow: auto;
}
p {
width: 1000px;
height: 1000px;
background-color: red;
}
</style>
<div><p></p></div>
<script>
var div = document.querySelector('div');
// 获取滚动元素的宽和高
console.log(div.scrollWidth, div.scrollHeight); // 1000 1000
// scrollLeft 和 scrollTop 属性都是读写属性
// 返回可滚动视区中因为滚动而暂时不可见的元素距离左边和上边的像素距离
div.scrollLeft = 500;
div.scrollTop = 500;
console.log(div.scrollLeft, div.scrollTop); // 500 500
</script>
使用 scrollIntoView() 滚动元素到视区
选取可滚动节点中某一节点后,我们可以使用 scrollIntoView() 方法告诉选取的节点滚动到可视区域。
<style>
div {
width: 30px;
height: 30px;
overflow: auto;
}
p {
background-color: red;
}
</style>
<div>
<content>
<p>0</p>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
</content>
</div>
<script>
document.querySelector('content').children[4].scrollIntoView(true);
</script>