什么是jQuery的offset方法?
offset() 方法返回或设置匹配元素相对于文档的偏移(位置)。
看个具体例子,直接上代码
<style type="text/css">
*{
margin: 0;
padding: 0;
}
#box1{
width: 400px;
height: 400px;
background-color:lightcoral;
position: relative;
left: 10px;
top: 10px;
}
#box2{
width: 200px;
height: 200px;
background-color:lightseagreen;
position: absolute;
left: 33px;
top: 33px;
}
</style>
<body>
<div id="box1">
<div id="box2">
</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script type="text/javascript">
console.log($("#box1").offset());//{top: 10, left: 10}
console.log($("#box2").offset());//{top: 43, left: 43}
</script>
</body>
实现效果截图
使用Javascript实现的思路
- 递归
- Element.getBoundingClientRect()
我们通过遍历目标元素、目标元素的父节点、父节点的父节点......依次溯源,并累加这些遍历过的节点相对于其最近祖先节点(且 position 属性非 static)的偏移量,向上直到 document,累加即可得到结果。
其中,我们需要使用 JavaScript 的 offsetTop 来访问一个 DOM 节点上边框相对离其本身最近、且 position 值为非 static 的祖先元素的垂直偏移量。具体实现为:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>jQuery Tiele</title>
</head>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#box1 {
width: 400px;
height: 400px;
background-color: lightcoral;
position: relative;
left: 10px;
top: 10px;
}
#box2 {
width: 200px;
height: 200px;
background-color: lightseagreen;
position: absolute;
left: 33px;
top: 33px;
}
</style>
<body>
<div id="box1">
<div id="box2">
</div>
</div>
<script type="text/javascript">
const offset = ele => {
let result = {
top: 0,
left: 0
}
/*
* nodeType 属性返回以数字值返回指定节点的节点类型。
* 如果节点是元素节点,则 nodeType 属性将返回 1。
* 如果节点是属性节点,则 nodeType 属性将返回 2。
* 如果节点 node.nodeType 类型不是 Element(1),则跳出;
* 如果相关节点的 position 属性为 static,则不计入计算,进入下一个节点(其父节点)的递归。
* 如果相关属性的 display 属性为 none,则应该直接返回 0 作为结果。
*/
const getOffset = (node, init) => {
if (node.nodeType !== 1) {
return
}
position = window.getComputedStyle(node)['position']
if (typeof(init) === 'undefined' && position === 'static') {
getOffset(node.parentNode)
return
}
result.top = node.offsetTop + result.top - node.scrollTop
result.left = node.offsetLeft + result.left - node.scrollLeft
if (position === 'fixed') {
return
}
getOffset(node.parentNode)
}
// 当前 DOM 节点的 display === 'none' 时, 直接返回 {top: 0, left: 0}
if (window.getComputedStyle(ele)['display'] === 'none') {
return result
}
let position
getOffset(ele, true)
return result
}
let box = document.getElementById('box2')
let result = offset(box);
console.log(result)
</script>
</body>
</html>
实现截图
接下来,换一种思路,用一个相对较新的API:getBoundingClientRect来实现jQuery offset方法
getBoundingClientRect 方法
rect.png返回值是一个 DOMRect 对象,这个对象是由该元素的
getClientRects()
方法返回的一组矩形的集合, 即:是与该元素相关的CSS 边框集合 。
[图片上传中...(rect.png-6dad39-1559057838375-0)]
DOMRect 对象包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。
代码实现:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>jQuery Tiele</title>
</head>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
#box1 {
width: 400px;
height: 400px;
background-color: lightcoral;
position: relative;
left: 10px;
top: 10px;
}
#box2 {
width: 200px;
height: 200px;
background-color: lightseagreen;
position: absolute;
left: 33px;
top: 33px;
}
</style>
<body>
<div id="box1">
<div id="box2">
</div>
</div>
<script type="text/javascript">
const offset = ele => {
let result = {
top: 0,
left: 0
}
// 当前为 IE11以下, 直接返回 {top: 0,left: 0}
if (!ele.getClientRects().length) {
return result
}
// 当前 DOM 节点的 display === 'none' 时,直接返回 {top: 0,left: 0}
if (window.getComputedStyle(ele)['display'] === 'none') {
return result
}
result = ele.getBoundingClientRect()
// 得到ele所在文档的HTML节点
let document = ele.ownerDocument.documentElement
return {
//docElement.clientTop 一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距。clientTop 是只读的
top: result.top + window.pageYOffset - document.clientTop,
left: result.left + window.pageXOffset - document.clientLeft
}
}
let box1 = document.getElementById('box1')
let box2 = document.getElementById('box2')
let result1 = offset(box1);
let result2 = offset(box2);
console.log(result1)
console.log(result2)
</script>
</body>
</html>
实现截图
需要注意的细节:
- 第一个
node.ownerDocument.documentElement
Node.ownerDocument 只读属性会返回当前节点的顶层的 document 对象。
看个示例
// 得到p元素所在文档的HTML节点
d = p.ownerDocument;
html = d.documentElement;
- 第二个
docElement.clientTop
一个元素顶部边框的宽度(以像素表示)。不包括顶部外边距或内边距。clientTop 是只读的。
网友评论