1.offset 概述
offset 翻译过来就是偏移量, 我们使用 offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
(1) 获得元素距离带有定位父元素的位置
(2) 获得元素自身的大小(宽度高度)
(3) 注意: 返回的数值都不带单位
offset 系列常用属性有:
1.1 代码体验
<style> * { margin: 0; padding: 0; } .father { margin: 100px; width: 200px; height: 200px; background-color: pink; } .son { margin-left: 30px; width: 100px; height: 100px; background-color: purple; } .box { margin: 0 100px; width: 200px; height: 200px; padding: 20px; background-color: skyblue; border: 10px solid red; } </style> <body> <div class="father"> <div class="son"></div> </div> <div class="box"></div> <script> // offset 系列 let father = document.querySelector('.father'); let son = document.querySelector('.son'); // 1. 返回带有定位的父亲,否则返回的是body console.log(son.offsetParent); // 2. 可以得到元素的偏移量(位置),返回的是不带单位的数值 console.log(father.offsetTop); // 100 ,因为css为其设置的margin为100 console.log(father.offsetLeft); // 100 ,因为css为其设置的margin为100 console.log(son.offsetLeft); // 130 ,因为虽然其有父亲,但是其父亲没有定位,所以以body为准,即100+30=130 // 3. 可以得到元素的大小(宽度和高度),是包含padding + border + width/height的,不包括margin let box = document.querySelector('.box') console.log(box.offsetWidth); // 260 console.log(box.offsetHeight); // 260 </script> </body>
特别注意:offset系列返回的位置是距离最近一级带有定位父元素的位置,如果父元素没有定位,则以body为参照。
2 offset 与 style 区别
(1)offset
① offset 可以得到任意样式表中的样式值
② offset 系列获得的数值是没有单位的
③ offsetWidth、offsetHeight包含padding+border+width
④ offsetWidth、offsetHeight等属性是只读属性,只能获取不能赋值
所以,我们想要获取元素大小位置,用offset更合适。
(2)style
① style 只能得到行内样式表中的样式值
② style.width 获得的是带有单位的字符串
③ style.width、style.height获得的是不包含padding和border 的值
④ style.width、style.height是可读写属性,可以获取也可以赋值
所以,我们想要给元素更改值,则需要用style改变。
3.案例:模态框拖拽
需求:
1. 点击弹出层, 会弹出模态框, 并且显示灰色半透明的遮挡层。
2. 点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层。
3. 鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动。
4. 鼠标松开,可以停止拖动模态框移动。<!-- css样式 --> <style> * { margin: 0; padding: 0; box-sizing: border-box; } .login-bg { display: none; width: 100%; height: 100%; position: fixed; top: 0; left: 0; background: rgba(0, 0, 0, .3); } .login-header { font-size: 20px; margin: 10px 0; text-align: center; } a { text-decoration: none; color: #333; } .login { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); display: none; width: 480px; padding-bottom: 40px; background-color: #fff; border: 1px solid #ebebeb; box-shadow: 0px 0px 20px #ddd; z-index: 3; } .login-title { position: relative; text-align: center; padding: 20px 0 30px; font-size: 18px; cursor: move; } .close-login { position: absolute; top: -20px; right: -20px; display: block; text-align: center; line-height: 40px; width: 40px; height: 40px; border-radius: 50%; font-size: 12px; border: 1px solid #ccc; background-color: #fff; } .login-input-content { margin-left: 20px; font-size: 14px; } .login-input:nth-child(1) label { margin-left: 14px; } .login-input-content input { width: 340px; height: 30px; outline: none; border: none; border: 1px solid #ccc; margin-bottom: 30px; padding-left: 5px; } .login-button { padding-bottom: 30px; width: 260px; height: 30px; border: 1px solid #ccc; margin: 0 auto; text-align: center; line-height: 30px; } </style> <body> <!-- html结构 --> <div class="login-header"> <a href="javascript:;">点击,弹出登录框</a> </div> <div class="login"> <div class="login-title">登录会员 <span><a href="javascript:;" class="close-login">关闭</a></span> </div> <div class="login-input-content"> <div class="login-input"> <label for="">用户名:</label> <input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="username"> </div> <div class="login-input"> <label for="">登录密码:</label> <input type="text" placeholder="请输入登录密码" name="info[password]" id="password" class="password"> </div> </div> <div class="login-button" id="loginBtn"> <a href="javascript:;" id="login-button-submit">登录会员</a> </div> </div> <!-- 遮盖层 --> <div id="bg" class="login-bg"></div> <!-- js代码 --> <script> // 1. 获取元素 let loginHeader = document.querySelector('.login-header'); let login = document.querySelector('.login'); let bg = document.getElementById('bg'); let close = document.querySelector('.close-login'); // 2. 点击,弹出登录框和遮盖层 loginHeader.onclick = function() { login.style.display = 'block'; bg.style.display = 'block'; }; // 3. 点击关闭,隐藏登录框和遮盖层 close.onclick = function() { login.style.display = 'none'; bg.style.display = 'none'; }; // 4. 鼠标放到模态框最上面的登录会员一行,可以按住鼠标拖拽模态框在页面中移动 // 由于鼠标拖拽模态框时,鼠标的位置是不变的,所以要先获取鼠标在登录会员框中的位置 // (1) 当鼠标按下,就能获得鼠标在盒子内的坐标 let title = document.querySelector('.login-title'); title.onmousedown = function(e) { let x = e.pageX - login.offsetLeft; let y = e.pageY - login.offsetTop; // (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去鼠标在盒子内的坐标就是模态框的left和top值 document.addEventListener('mousemove', move); // 由于后面会移除事件,为了方便使用所以这里给回调函数单独设置并命名为move function move(e) { login.style.left = e.pageX - x + 'px'; login.style.top = e.pageY - y + 'px'; }; // (3) 鼠标弹起mouseup,就让鼠标移动事件移除 document.addEventListener('mouseup', function() { document.removeEventListener('mousemove', move) }) } </script> </body>
案例分析:
① 点击弹出层, 模态框和遮挡层就会显示出来 display:block;
② 点击关闭按钮,模态框和遮挡层就会隐藏起来 display:none;
③ 在页面中拖拽的原理: 鼠标按下并且移动, 之后松开鼠标
④ 触发事件是鼠标按下 mousedown, 鼠标移动mousemove 鼠标松开 mouseup
⑤ 拖拽过程: 鼠标移动过程中,获得最新的值赋值给模态框的left和top值, 这样模态框可以跟着鼠标走了,所以模态框必须要带定位
⑥ 鼠标按下触发的事件源是 最上面一行,就是 class 为 title
⑦ 鼠标的坐标 减去 鼠标在盒子内的坐标, 才是模态框真正的位置。
⑧ 鼠标按下,我们要得到鼠标在盒子的坐标。
⑨ 鼠标移动,就让模态框的坐标 设置为 : 鼠标坐标 减去盒子坐标即可,注意移动事件写到按下事件里面。
⑩ 鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除