JS BOM和DOM 学习第五天(上)
目录
1. 元素偏移offset
1.1 offset概述
-
定义:偏移量,使用offset系列相关属性获取元素的位置(偏移)、大小
-
获取元素距离带有定位的父元素的位置大小
-
获取元素自身的大小
-
返回值不带单位
-
常见属性
offset系列属性 | 说明 |
---|---|
element.offsetParent | 获取元素的带有定位的父元素,如果父元素都没有定位,则返回body |
element.offsetTop | 获取元素距离带有定位的父元素上方的偏移 |
element.offsetLeft | 获取元素相对于带有定位的父元素左边的偏移 |
element.offsetWidth | 返回自身包括padding,border,内容区域的宽度 |
element.offsetHeight | 返回自身包括padding,border,内容区域的高度 |
- 图解
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father{
position: relative;
width: 200px;
height: 200px;
background-color: pink;
}
.son{
width: 100px;
height: 100px;
background-color: blue;
margin-left: 10px;
border: 1px solid #000;;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
// 1.获取子元素的带有定位的父元素,没有返回body
var son=document.querySelector('.son');
console.log(son.offsetParent);
// 2.获取子元素距离父元素左边距离
console.log(son.offsetLeft);
// 3.获取子元素自身的宽度,包括padding,border
console.log(son.offsetWidth);
</script>
</body>
</html>
- 效果图
1.2 offset与style的区别
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box{
width: 200px;
height: 200px;
background-color: pink;
border: 10px solid #ccc;
}
</style>
</head>
<body>
<div class="box" style="width:200px">
</div>
<script>
// 1.offsetWidth获取任意样式表宽度数值,而style.width只能获得行内样式表的宽度数值
var box=document.querySelector('.box');
// offset的属性值为可读,不可写
box.offsetWidth='300';
console.log(box.offsetWidth);
// 获取不到外部样式表
console.log(box.style.width);
</script>
</body>
</html>
- 效果图
获取鼠标在盒子内的x,y坐标
- 思路
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
width: 200px;
height: 200px;
background-color: pink;
margin: 100px;
}
</style>
</head>
<body>
<div></div>
<script>
var div=document.querySelector('div');
div.addEventListener('mousemove',function(e){
// 获取鼠标距离页面的左边坐标
// 获取当前盒子距离body的左边坐标
// 相减得到鼠标在盒子内的坐标
var x=e.pageX-this.offsetLeft;
var y=e.pageY-this.offsetTop;
this.innerHTML='x的坐标'+x+',y的坐标'+y;
})
</script>
</body>
</html>
- 效果图
模拟模态框案例
- 思路
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
.login-header {
width: 100%;
text-align: center;
height: 30px;
font-size: 24px;
line-height: 30px;
}
ul,
li,
ol,
dl,
dt,
dd,
div,
p,
span,
h1,
h2,
h3,
h4,
h5,
h6,
a {
padding: 0px;
margin: 0px;
}
.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 beginBtn=document.querySelector('#link');
// 获取关闭按钮
var closeBtn=document.querySelector('#closeBtn');
var login=document.querySelector('#login');
var bg=document.querySelector('#bg');
// 弹出登录框
beginBtn.addEventListener('click',function(){
login.style.display='block';
bg.style.display='block';
});
// 隐藏登录框
closeBtn.addEventListener('click',function(){
login.style.display='none';
bg.style.display='none';
});
// 获取可移动范围的部分
// 移动登录框
var title=document.querySelector('#title');
title.addEventListener('mousedown',function(e){
// 获取鼠标当前坐标(距离盒子的坐标,不变化)
var x=e.pageX-login.offsetLeft;
var y=e.pageY-login.offsetTop;
// 移动
document.addEventListener('mousemove',move);
function move(e){
login.style.left=e.pageX-x+'px';
login.style.top=e.pageY-y+'px';
}
// 松开
document.addEventListener('mouseup',function(){
document.removeEventListener('mousemove',move)
});
})
</script>
</body>
</html>
- 效果图
仿京东放大镜效果
- 思路
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{
margin: 0;
padding: 0;
}
.goodbox{
margin-left: 100px;
margin-top: 50px;
position: relative;
width: 400px;
height: 400px;
/* background-color: pink; */
text-align: center;
border: 1px solid #000;
}
.goodbox img{
width: 400px;
/* 垂直水平居中 */
/* margin-top: 50px; */
}
/* 遮罩 */
.goodbox .mask{
display: none;
position: absolute;
left: 0;
top: 0;
width: 300px;
height: 300px;
background-color: orange;
opacity: .35;
cursor: move;
}
/* 右侧大图 */
.goodbox .bigpic{
display: none;
position: absolute;
left: 420px;
top: 0;
width: 500px;
height: 500px;
border: 1px solid #000;
/* background-image: url(./images/big.jpg); */
/* background-size: contain; */
overflow: hidden;
}
/* 内图 */
.goodbox .bigpic img{
position: absolute;
width: 800px;
}
</style>
</head>
<body>
<div class="goodbox">
<img src="./images/b3.png" alt="">
<!-- 遮罩 -->
<div class="mask"></div>
<!-- 大图 -->
<div class="bigpic"><img src="./images/big.jpg" alt=""></div>
</div>
<script>
// 取得商品盒子
window.addEventListener('load',function(){
var goodbox=document.querySelector('.goodbox');
var mask=document.querySelector('.mask');
var bigpic=document.querySelector('.bigpic');
var bigpicImg=bigpic.children[0];
// 鼠标移入商品盒子后显示遮罩和大图
goodbox.addEventListener('mouseover',function(e){
mask.style.display='block';
bigpic.style.display='block';
});
// 鼠标移出隐藏
goodbox.addEventListener('mouseout',function(){
mask.style.display='none';
bigpic.style.display='none';
});
// 鼠标移动
goodbox.addEventListener('mousemove',function(e){
// 遮罩随着鼠标移动,盒子内坐标
var x=e.pageX-this.offsetLeft;
var y=e.pageY-this.offsetTop;
// 赋给遮罩
// 鼠标坐标偏移,使鼠标在遮罩中心
var maskX=x-mask.offsetWidth/2;
var maskY=y-mask.offsetHeight/2;
// 遮罩移动的范围限制
var maskXMax=this.offsetWidth-mask.offsetWidth;
var maskYMax=this.offsetHeight-mask.offsetHeight;
if(maskX<=0){
maskX=0;
}else if(maskX>=maskXMax){
maskX=maskXMax;
}
if(maskY<=0){
maskY=0;
}else if(maskY>=maskYMax){
maskY=maskYMax;
}
mask.style.left=maskX+'px';
mask.style.top=maskY+'px';
// 大图的移动
// 大图移动的距离/大图移动的最大距离=遮罩移动的距离/遮罩最大移动距离
var bigpicXMax=bigpicImg.offsetWidth-bigpic.offsetWidth;
var bigpicYMax=bigpicImg.offsetHeight-bigpic.offsetHeight;
var bigpicX=maskX*bigpicXMax/maskXMax;
var bigpicY=maskY*bigpicYMax/maskYMax;
bigpicImg.style.left=-bigpicX+'px';
bigpicImg.style.top=-bigpicY+'px';
})
});
</script>
</body>
</html>
- 效果图
2. 元素可视区client系列
- client 客户端,获得元素的边框大小,元素大小
- 常用属性
- 返回值不带单位
client系列属性 | 作用 |
---|---|
element.clientTop | 返回元素上边框的大小 |
element.clientLeft | 返回元素左边框的大小 |
element.clientWidth | 返回元素的宽度(包含padding,不包含边框) |
element.clientHeight | 返回元素的高度(包含padding,不包含边框) |
- 图解
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
width: 200px;
height: 200px;
background-color: pink;
border: 10px solid skyblue;
padding: 20px;
}
</style>
</head>
<body>
<div></div>
<script>
var div=document.querySelector('div');
// 1.clientTop获取上边框
console.log(div.clientTop);
// 2.clientWidth获取元素宽度(不包含边框,包含padding)
console.log(div.clientWidth);
</script>
</body>
</html>
- 效果图
2.1 立即执行函数
-
函数在创建时就调用
-
该函数创建了一个独立的作用域,避免了命名冲突问题
-
格式
立即执行函数,格式1(function(){})(),格式2(function(){}())
-
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 立即执行函数,格式1(function(){})(),格式2(function(){}())
(function(a,b){
console.log(a+b);
})(1,2);
// 创建时执行
</script>
</body>
</html>
- 效果图
2.2 移动适配的js源码分析
(function flexible (window, document) {
var docEl = document.documentElement
// dpr 物理像素比,一般电脑为1,移动为2或其他
var dpr = window.devicePixelRatio || 1
// adjust body font size
// 设置body主体部分的字体大小
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
// 循环检测body是否加载完,加载完后设置字体大小
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit () {
// rem的大小为根(html)标签的宽度(不包含边框)的10分之1
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
// 页面大小发生变化时重新设置rem的大小
window.addEventListener('resize', setRemUnit)
// 页面刷新以及回退到该页面时重新设置rem大小
window.addEventListener('pageshow', function (e) {
// 如果该页面是缓存中的页面,也需要计算大小,火狐浏览器回退时页面内容在缓存
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports
// 支持0.5 像素的
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
- 注意点
3. 元素滚动scroll系列
3.1 元素scroll系列属性
- 使用scroll系列相关属性动态的得到该元素的大小,滚动距离
scroll系列属性 | 作用 |
---|---|
element.scrollTop | 返回被卷去的上侧距离,不带单位 |
element.scrollLeft | 返回被卷去的左侧距离,不带单位 |
element.scrollWidth | 返回自身实际的宽度,不含边框,返回不带单位 |
element.scrollHeight | 返回自身实际的宽度,不含边框,返回不带单位 |
- 图解
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
width: 200px;
height: 200px;
background-color: pink;
overflow: auto;
font-size: 20px;
}
</style>
</head>
<body>
<div>
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
我是内容
</div>
<script>
var div=document.querySelector('div');
// 获取滚动条的总长度,内容长度
console.log(div.scrollHeight);
// 获取滚动条超出盒子的上方距离
div.addEventListener('scroll',function(){
console.log(div.scrollTop);
})
</script>
</body>
</html>
- 效果图
仿淘宝滚动条案例
- 思路
<!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>
.slider-bar {
position: absolute;
left: 50%;
top: 300px;
margin-left: 600px;
width: 45px;
height: 130px;
background-color: pink;
}
.w {
width: 1200px;
margin: 10px auto;
}
.header {
height: 150px;
background-color: purple;
}
.banner {
height: 250px;
background-color: skyblue;
}
.main {
height: 1000px;
background-color: yellowgreen;
}
span {
display: none;
position: absolute;
bottom: 0;
}
</style>
</head>
<body>
<div class="slider-bar">
<span class="goBack">返回顶部</span>
</div>
<div class="header w">头部区域</div>
<div class="banner w">banner区域</div>
<div class="main w">主体部分</div>
<script>
// 获取banner
var banner=document.querySelector('.banner');
var sliderbar=document.querySelector('.slider-bar');
var bannerTop=banner.offsetTop;
// 固定定位后的距离
var sliderTop=sliderbar.offsetTop-bannerTop;
// 获取main
var main=document.querySelector('.main');
var mainTop=main.offsetTop;
// 获取回到顶部
var goBack=document.querySelector('.goBack');
// 当页面被卷去的距离为banner区域的offsetTop时,滚动导航栏变为固定单位
document.addEventListener('scroll',function(){
if(window.pageYOffset>=bannerTop){
sliderbar.style.position='fixed';
sliderbar.style.top=sliderTop+'px';
}else{
sliderbar.style.position='absolute';
sliderbar.style.top='300px';
}
if(window.pageYOffset>=mainTop){
goBack.style.display='block';
}else{
goBack.style.display='none';
}
})
</script>
</body>
</html>
- 效果图
3.3 页面被卷去的头部兼容性解决方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 以卷去头部部分为例
// 1.声明了DTD(<!DOCTYPE html>),使用
document.document.scrollTop;
// 2.为声明DTD
document.body.scrollTop;
// 3.新方法
window.pageYOffset;
// 兼容性解决方案
function getScroll() {
// 返回对象
return {
left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
};
}
// 使用的时候 getScroll().left
</script>
</body>
</html>
##4. 三大系列总结
- offset经常用于获得元素位置(offsetTop,offsetLeft)
- client系列经常用于获取元素大小(clientWidth,clientHeight)
- scroll系列常用于获取滚动距离(scrollWidth,scrollHeight)
- 获取页面被卷去的距离(window.pageXoffset,window.pageYoffset)
##5. mouseover 与 mouseenter的区别
- 1.mouseover 经过自身盒子会触发,经过子盒子也会触发,冒泡
- 2.mouseenter 经过自身盒子会触发,经过子盒子不触发,不冒泡
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father{
width: 300px;
height: 300px;
background-color: pink;
}
.son{
width: 200px;
height: 200px;
background-color: skyblue;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
// 1.mouseover 经过自身盒子会触发,经过子盒子也会触发,冒泡
var father=document.querySelector('.father');
// father.addEventListener('mouseover',function(){
// console.log(11);
// });
// 2.mouseenter 经过自身盒子会触发,经过子盒子不触发,不冒泡
father.addEventListener('mouseenter',function(){
console.log(11);
})
</script>
</body>
</html>
- mouseover效果图
- mouseenter效果图
6.动画原理
- 设置定时器实现元素的位移,旋转和缩放
盒子移动案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
width: 200px;
height: 200px;
background-color: pink;
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div></div>
<script>
var div=document.querySelector('div');
// 1.使用定时器让盒子不断往右移动
var timer=null;
timer=setInterval(function(){
// 获取元素距离左边的距离
div.style.left=div.offsetLeft+5+'px';
// 当移动到400px时,停止移动
if(div.offsetLeft>=400){
clearInterval(timer);
}
},50);
</script>
</body>
</html>
- 效果图
6.1 动画函数封装
- 每次调用函数时,都需要定义一个定时器,不仅有重名的歧义,开辟内存空间也大
- 解决方法:把定时器作为一个对象的属性,每个对象都有自己专用的定时器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div{
width: 100px;
height: 100px;
background-color: pink;
position: absolute;
left: 0;
top: 0;
}
span{
width: 200px;
height: 200px;
background-color: blue;
position: absolute;
left: 0;
top: 100px;
}
</style>
</head>
<body>
<div></div>
<span></span>
<script>
// 定义一个动画函数
function animate(obj,target){
// 定时器,每个对象都有一个属性-定时器,
// 防止定时器叠加
clearInterval(obj.timer);
obj.timer=setInterval(function(){
obj.style.left=obj.offsetLeft+5+'px';
// 停止位置
if(obj.offsetLeft>=target){
clearInterval(obj.timer);
}
},30);
}
// 获取对象
var div=document.querySelector('div');
var span=document.querySelector('span');
animate(div,300);
animate(span,200);
</script>
</body>
</html>
- 效果图
注意点
- 多次调用动画函数时,盒子移动速度越来越快(定时器叠加)
- 需要每次调用动画函数时,清除之前的定时器来实现效果