JavaScript - offset、client、scroll 系列

一、 网页偏移量 offset 系列

offset : 网页偏移量,动态获取 该元素的 位置及大小等

注意:

  • 获取元素距离带有定位父元素的位置
  • 获取元素自身的大小(宽度和高度)
  • 返回 的数值没有单位

常用属性:

属性作用
element.offsetParent返回作为该元素的带有定位的父级元素,
如果父级都没有定位,则返回 body
element.offsetTop返回元素 相对带有定位的,父元素上方的偏移
element.offsetLeft返回元素 相对带有定位的,父元素左边框的偏移
element.offsetWidth返回自身包括 padding 、 边框 、 内容区的宽度(不带单位)
element.offsetHeight返回自身包括 padding 、 边框 、 内容区的高度(不带单位)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div class="father">
<div class="son"></div>
</div>
<div class="w"></div>
<script>
// offset 系列
var father = document.querySelector('.father');
var son = document.querySelector('.son');
// 1.可以得到元素的偏移 位置 返回的不带单位的数值
console.log(father.offsetTop); // 150
console.log(father.offsetLeft); // 150
// 它以带有定位的父亲为准 如果么有父亲或者父亲没有定位 则以 body 为准
console.log(son.offsetLeft); // 45
var w = document.querySelector('.w');
// 2.可以得到元素的大小 宽度和高度 是包含padding + border + width
console.log(w.offsetWidth);
console.log(w.offsetHeight);
// 3. 返回带有定位的父亲 否则返回的是body
console.log(son.offsetParent); // 返回带有定位的父亲 否则返回的是body
console.log(son.parentNode); // 返回父亲 是最近一级的父亲 亲爸爸 不管父亲有没有定位
</script>

</code-block>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
* {
margin: 0;
padding: 0;
}

.father {
/* position: relative; */
width: 200px;
height: 200px;
background-color: pink;
margin: 150px;
}

.son {
width: 100px;
height: 100px;
background-color: purple;
margin-left: 45px;
}

.w {
height: 200px;
background-color: skyblue;
margin: 0 auto 200px;
padding: 10px;
border: 15px solid red;
}

</code-block>
</code-group>

offset 和 style 的区别

offset(用于获取元素大小、位置style(用于更改元素样式
offset 可以得到任意样式表中的样式值style 只能得到行内样式表中的样式值
offset 获得的值 没有单位style.width 获得的是带有单位的字符串
offsetWidth 包含 padding+border+widthstyle.width 获得不包含
padding 和 border的值
offsetWidth 等属性是 只读属性
只能获取不能复制
style.width 是可读可写属性,
可以获取,也可以赋值

案例 —- 动态获取 鼠标在盒子中的坐标

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="zh">
<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></title>
<style type="text/css">
.demo{
margin: 100px auto;
width: 200px;
height: 200px;
background-color: #55ffff;
}
</style>
</head>
<body>
<div class="demo"></div>
<script type="text/javascript">
var div = document.querySelector('.demo');
div.addEventListener('mousemove', function (e) {
var x = e.pageX - div.offsetLeft;
var y = e.pageY - div.offsetTop;
div.innerHTML = 'x 坐标为:'+ x + '<br> y 坐标为:' + y;
})
</script>
</body>
</html>

:::

案例 —- 模态框的拖拽

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<!DOCTYPE html>
<html>

<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0px;
margin: 0px;
}

.login-header {
width: 100%;
text-align: center;
height: 30px;
font-size: 24px;
line-height: 30px;
}

.login {
display: none;
width: 512px;
height: 280px;
position: fixed;
border: #ebebeb solid 1px;
left: 50%;
top: 50%;
background: #ffffff;
box-shadow: 0px 0px 20px #ddd;
z-index: 9999;
transform: translate(-50%, -50%);
}

.login-title {
width: 100%;
margin: 10px 0px 0px 0px;
text-align: center;
line-height: 40px;
height: 40px;
font-size: 18px;
position: relative;
cursor: move;
}

.login-input-content {
margin-top: 20px;
}

.login-button {
width: 50%;
margin: 30px auto 0px auto;
line-height: 40px;
font-size: 14px;
border: #ebebeb 1px solid;
text-align: center;
}

.login-bg {
display: none;
width: 100%;
height: 100%;
position: fixed;
top: 0px;
left: 0px;
background: rgba(0, 0, 0, .3);
}

a {
text-decoration: none;
color: #000000;
}

.login-button a {
display: block;
}

.login-input input.list-input {
float: left;
line-height: 35px;
height: 35px;
width: 350px;
border: #ebebeb 1px solid;
text-indent: 5px;
}

.login-input {
overflow: hidden;
margin: 0px 0px 20px 0px;
}

.login-input label {
float: left;
width: 90px;
padding-right: 10px;
text-align: right;
line-height: 35px;
height: 35px;
font-size: 14px;
}

.login-title span {
position: absolute;
font-size: 12px;
right: -20px;
top: -30px;
background: #ffffff;
border: #ebebeb solid 1px;
width: 40px;
height: 40px;
border-radius: 20px;
}
</style>
</head>

<body>
<div class="login-header"><a id="link" href="javascript:;">点击,弹出登录框</a></div>
<div id="login" class="login">
<div id="title" class="login-title">登录会员
<span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span>
</div>
<div class="login-input-content">
<div class="login-input">
<label>用户名:</label>
<input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input">
</div>
<div class="login-input">
<label>登录密码:</label>
<input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input">
</div>
</div>
<div id="loginBtn" class="login-button"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div>
</div>
<!-- 遮盖层 -->
<div id="bg" class="login-bg"></div>
<script>
// 1. 获取元素
var login = document.querySelector('.login');
var mask = document.querySelector('.login-bg');
var link = document.querySelector('#link');
var closeBtn = document.querySelector('#closeBtn');
var title = document.querySelector('#title');
// 2. 点击弹出层这个链接 link 让mask 和login 显示出来
link.addEventListener('click', function () {
mask.style.display = 'block';
login.style.display = 'block';
})
// 3. 点击 closeBtn 就隐藏 mask 和 login
closeBtn.addEventListener('click', function () {
mask.style.display = 'none';
login.style.display = 'none';
})
// 4. 开始拖拽
// (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标
title.addEventListener('mousedown', function (e) {
var x = e.pageX - login.offsetLeft;
var y = e.pageY - login.offsetTop;
// (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值
document.addEventListener('mousemove', move)

function move(e) {
login.style.left = e.pageX - x + 'px';
login.style.top = e.pageY - y + 'px';
}
// (3) 鼠标弹起,就让鼠标移动事件移除
document.addEventListener('mouseup', function () {
document.removeEventListener('mousemove', move);
})
})
</script>
</body>

</html>

:::

案例 —- 放大镜

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<!DOCTYPE html>
<html>

<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0px;
margin: 0px;
}

.demo {
margin-top: 100px;
margin-left: 100px;
width: 400px;
height: 400px;
border: 2px solid #ccc;
}

.box {
width: 400px;
height: 400px;
position: relative;
}

.box img {
width: 100%;
height: 100%;
}

.box span {
display: none;
position: absolute;
left: 0;
top: 0;
width: 70%;
height: 70%;
background: rgba(245, 132, 3, 0.3);
}
.big {
display: none;
position: absolute;
left: 505px;
top: 100px;
width: 500px;
height: 500px;
border: 2px solid #ccc;
overflow: hidden;
}
.big img {
position: absolute;
}
</style>
</head>

<body>
<div class="demo">
<div class="box">
<img src="https://img.pupper.cn/img/202112281645931.png" alt="">
<span></span>
</div>
<div class="big">
<img src="https://img.pupper.cn/img/202112281645385.png" alt="">
</div>
</div>
<script>
var box = document.querySelector('.box')
var sp = box.querySelector('span')
var big = document.querySelector('.big');
var big_img = big.querySelector('img');
// 鼠标进过,盒子显示
box.addEventListener('mouseover', function () {
sp.style.display = 'block';
big.style.display = 'block';
})
// 鼠标离开, 盒子隐藏
box.addEventListener('mouseout', function () {
sp.style.display = 'none';
big.style.display = 'none';
})
// 移动 遮罩小图
box.addEventListener('mousemove', function (e) {
// 鼠标在盒子中的位置
var x = e.pageX - box.offsetLeft;
var y = e.pageY - box.offsetTop;
// 将鼠标置于 遮罩的中心位置,其计算结果为遮罩的实际位置
var sp_x = x - sp.offsetWidth / 2;
var sp_y = y - sp.offsetHeight / 2;
// 遮罩最大移动距离, 因为是正方形,所以x y 移动距离 一致
var sp_max = box.offsetWidth - sp.offsetWidth;
// 使遮罩始终保持在 盒子中移动
if(sp_x < 0){
sp_x = 0;
}else if (sp_x > sp_max){
sp_x = sp_max;
}
if(sp_y < 0){
sp_y = 0;
}else if (sp_y > sp_max){
sp_y = sp_max;
}
sp.style.left = sp_x + 'px';
sp.style.top = sp_y + 'px';

// 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
// 大图 最大移动距离
var big_max = big_img.offsetWidth - big.offsetWidth;
// 大图移动距离 x y
var bigx = sp_x * big_max / sp_max;
var bigy = sp_y * big_max / sp_max;
big_img.style.left = -bigx + 'px';
big_img.style.top = -bigy + 'px';
})
</script>
</body>

</html>

:::

二、 client 系列

client : 客户端,用于获取元素可视区的相关信息,通过 client 相关属性可以动态得到元素的大小、边框大小等。

属性作用
element.clientTop返回元素 上边框的大小
element.clientLeft返回元素 左边框的大小
element.clientWidth返回元素 自身包括 padding、内容区宽度,
不包含边框,返回值 不带单位
element.clientHeight返回元素 自身包括 padding 、内容区高度,
不包含边框,返回值不带单位

立即执行函数

立即执行函数 : 不需要调用,立即能够自己执行的函数

写法:

1
2
3
(function(){})();
或者
(function(){}());

示例:

1
2
3
4
5
6
7
(function(a, b){
console.log(a + b);
})(1, 2); // 3

(function(a, b){
console.log(a + b);
}(1, 2)) // 3

注意:

  • 立即执行函数 可以命名,也可以传参数;
  • 多个立即执行函数时,需要用 ‘;’ 分号隔开;
  • 立即执行函数 是一个独立的作用域,里面的变量都是局部变量,不存在命名冲突;

三、 scroll 系列

scroll : 滚动,使用相关属性可以动态的获得元素的大小、滚动距离等

属性作用
element.scrollTop返回 被卷去的上侧距离,返回值不带单位
element.scrollLeft返回 被卷去的 左侧距离,返回值不带单位
element.scrollWidth返回 自身实际的宽度,不包含边框,返回值不带单位
element.scrollHeight返回 自身实际的高度,不包含边框,返回值不带单位

::: warning

元素被卷曲使用 : element.scrollTopelement.scrollLeft

页面被卷去使用 : window.pageYOffsetwindow.pageXOffset

:::

案例 —- 返回顶部按钮

::: details

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<!DOCTYPE html>
<html>

<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0px;
margin: 0px;
}

.demo {
/* position: relative; */
margin: 5px auto;
width: 600px;
}

.handr {
width: 100%;
height: 100px;
background-color: aquamarine;
}

.banner {
width: 100%;
height: 500px;
margin-top: 10px;
background-color: chocolate;
}

.bodyr {
width: 100%;
height: 800px;
margin-top: 10px;
background-color: #00A4FF;
}

.loos {
position: absolute;
right: 35px;
top: 200px;
width: 50px;
height: 100px;
background-color: chartreuse;
}

.loos span {
display: none;
}
</style>
</head>

<body>
<div class="demo">
<div class="handr"></div>
<div class="banner"></div>
<div class="bodyr"></div>
<div class="loos">
<span>返回顶部</span>
</div>
</div>
<script>
var loos = document.querySelector('.loos')
var banner = document.querySelector('.banner')
var bodyr = document.querySelector('.bodyr')
var span = loos.querySelector('span')
// banner 距离页面顶部的高度
var bannertop = banner.offsetTop
// 返回块 停止的位置
var loostop = loos.offsetTop - bannertop
// body 距离页面顶部的高度
var bodyrtop = bodyr.offsetTop;


// 页面滚动事件
document.addEventListener('scroll', function() {
if (window.pageYOffset >= bannertop) {
// 固定定位 是以 浏览器可视区域为参考的
// 绝对定位 是以 有定位的父元素 为参考的
loos.style.position = 'fixed';
loos.style.top = loostop + 'px';
} else {
loos.style.position = 'absolute';
loos.style.top = '200px';
}

if (window.pageYOffset >= bodyrtop) {
span.style.display = 'block'
} else {
span.style.display = 'none'
}
})
</script>
</body>

</html>

:::

四、 三大系列的总结

对比作用
element.offsetWidth返回自身包括 padding、 内容区的宽度、边框,返回值不带单位
element.clientWidth返回自身包括 padding、 内容区的宽度,不含边框,返回值不带单位
element.scrollWidth返回自身实际的宽度,不含边框, 返回值不带单位

1. 作用

  • offset 系列,经常用于获得元素的 位置offsetLeftoffsetTop
  • client 系列,经常用于获取元素的 大小clientWidthclientHeight
  • scroll 系列,经常用于获取 滚动的 距离scrollTopscrollLeft
  • 页面的滚动距离 通过 window.pageXOffset 获得

五、 mouseentermouseover 的区别

mouseover : 鼠标经过父盒子和子盒子时,都会打印;

mouseeenter : 鼠标经过父盒子会打印,子盒子不会打印(因为 它不会冒泡)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!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>Document</title>
<style>
.father {
width: 300px;
height: 300px;
background-color: pink;
margin: 100px auto;
}

.son {
width: 200px;
height: 200px;
background-color: purple;
}
</style>
</head>

<body>
<div class="father">
<div class="son"></div>
</div>
<script>
var father = document.querySelector('.father');
var son = document.querySelector('.son');
// 父盒子、子盒子都会打印
father.addEventListener('mouseover', function() {
console.log(11);
})
// 只有父盒子会打印
father.addEventListener('mouseenter', function() {
console.log(11);
})
</script>
</body>

</html>