文章目录
元素偏移量offset系列
概述
offset,偏移量,使用offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
- 获得元素距离带有定位父元素的位置
- 获得元素自身的大小(宽度高度)
- **注:**返回的值都不带单位
offset常见属性
属性 | 作用 |
---|---|
element.offsetParent | 返回作为该元素带有定位的父级元素 如果父级元素都没有定位则返回body |
element.offsetTop | 返回元素相对带有定位父元素上方的偏移 |
element.offsetLeft | 返回元素相对带有定位父元素左边框的偏移 |
element.offsetWidth | 返回自身包括padding、边框、内容区的高度,不带单位 |
element.offsetHeight | 返回自身包括padding、边框、内容区的高度,不带单位 |
<style>
* {
margin: 0;
padding: 0;
}
.father {
width: 200px;
height: 200px;
background-color: pink;
margin: 100px;
position: relative;
}
.son {
width: 100px;
height: 100px;
background-color: purple;
margin-left: 45px;
}
.box {
width: 200px;
height: 200px;
background-color: skyblue;
}
</style>
<body>
<div class="father">
<div class="son"></div>
</div>
<div class="box"></div>
<script>
var father = document.querySelector('.father');
var son = document.querySelector('.son');
//1. 可以得到元素的偏移 位置 返回的不带单位的数值
console.log(father.offsetTop); //100
console.log(father.offsetLeft); //100
//以带有定位的父亲为准,若父无定位,以body
console.log(son.offsetTop); //100
console.log(son.offsetLeft); //145
//加定位后分别为0、45
//2. 可以得到元素的大小和宽高
//包含padding, border, width
var box = document.querySelector('.box');
console.log(box.offsetWidth);
console.log(box.offsetHeight);
//3. 返回带有定位的父亲,否则返回body
console.log(son.offsetParent); //必须带定位的父亲
console.log(son.parentNode); //返回最近一级的父亲(亲爸爸) 不管有无定位
console.log(box.offsetParent); //<body></>
</script>
offset和style区别
offset | style |
---|---|
offset可以得到任意样式表中的样式值 | style只能得到行内样式表中的样式值 |
offset系列获得的数值是没有单位的 | style.width获得的是带有单位的字符串 |
offsetWidth包含padding+border+width | style.width获得不包含padding和border的值 |
offsetWidth等属性是只读属性,只能获取不能赋值 | style.width是可读写属性,可以获取也可以赋值 |
所以,我们想要获取元素大小位置,用offset更合适 | 所以,我们想要给元素更改值,则需要用style改变 |
获取鼠标位置案例
<style>
.box {
width: 300px;
height: 300px;
background-color: pink;
margin: 20px;
}
</style>
<body>
<div class="box"></div>
<script>
var box = document.querySelector('.box');
box.addEventListener('mousemove', function(e) {
// console.log(e.pageX);
// console.log(e.pageY);
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
this.innerHTML = 'x:' + x + ',y:' + y;
})
</script>
拖拽模态框案例
<style>
.login {
display: none;
width: 513px;
height: 280px;
position: fixed;
border: #ebebeb solid 1px;
left: 50%;
top: 50%;
background-color: #fff;
box-shadow: 0 0 20px #ddd;
z-index: 9999;
transform: translate(-50%, -50%);
}
.login-bg {
display: none;
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, .3);
}
.title {
width: 100%;
height: 50px;
line-height: 50px;
background-color: #ddd;
font-size: 25px;
text-align: center;
}
</style>
<body>
<div class="login-header">
<a href="javascript:;" id="link">点击弹出登录框</a>
</div>
<div class="login" id="login">
<div class="title" id="title">会员登录
<span><a id="closeBtn" href="javascript:void(0)" class="close-btn">关闭</a></span>
</div>
<div class="uname">
<label>用户名:</label>
<input type="text" placeholder="请输入用户名">
</div>
<div class="pwd">
<label>密码:</label>
<input type="password" placeholder="请输入密码">
</div>
</div>
<!-- 遮罩层 -->
<div id="bg" class="login-bg"></div>
</body>
<script>
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');
link.addEventListener('click', function() {
mask.style.display = 'block';
login.style.display = 'block';
})
closeBtn.addEventListener('click', function() {
mask.style.display = 'none';
login.style.display = 'none';
})
//拖拽
//1.鼠标按下,就获得鼠标在盒子内的坐标
title.addEventListener('mousedown', function(e) {
var x = e.pageX - login.offsetLeft;
var y = e.pageY - login.offsetTop;
//2.鼠标移动的时候,把鼠标在页面中的坐标,减去鼠标在盒子内的坐标就是模态框的left和tp值
//document:任何一个地方
document.addEventListener('mousemove', move)
function move(e) {
login.style.left = e.pageX - x + 'px';
login.style.top = e.pageY - y + 'px';
}
//3.鼠标弹起,移除move事件
document.addEventListener('mouseup', function() {
document.removeEventListener('mousemove', move);
})
})
</script>
放大镜案例
<style>
.preimg {
width: 300px;
height: 150px;
position: relative;
border: 1px solid #ccc;
}
.mask {
width: 100px;
height: 100px;
background-color: goldenrod;
opacity: .3;
position: absolute;
top: 0;
left: 0;
cursor: move;
display: none;
}
.big {
width: 400px;
height: 400px;
background-color: purple;
position: absolute;
left: 310px;
top: 0;
z-index: 999;
display: none;
border: 1px solid #ccc;
overflow: hidden;
}
.bigImg {
position: absolute;
left: 0;
top: 0;
}
</style>
<body>
<div class="preimg">
<img src="../../images/憨桃琳.jpg" alt="" width="300" height="150">
<div class="mask"></div>
</div>
<div class="big">
<img src="../../images/憨桃琳.jpg" class="bigImg" alt="" width="600" height="300">
</div>
</body>
<script>
window.addEventListener('load', function() {
var preimg = document.querySelector('.preimg');
var mask = document.querySelector('.mask');
var big = document.querySelector('.big');
//鼠标经过:显示隐藏
preimg.addEventListener('mouseover', function() {
mask.style.display = 'block';
big.style.display = 'block';
})
preimg.addEventListener('mouseout', function() {
mask.style.display = 'none';
big.style.display = 'none';
})
preimg.addEventListener('mousemove', function(e) {
//先计算出鼠标在盒子内的坐标
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
//mask移动的距离
var maskX = x - (mask.offsetWidth / 2);
var maskY = y - (mask.offsetHeight / 2)
//x<0;停在0
if (maskX <= 0) {
maskX = 0;
} else if (maskX > preimg.offsetWidth - mask.offsetWidth) {
maskX = preimg.offsetWidth - mask.offsetWidth
}
if (maskY <= 0) {
maskY = 0;
} else if (maskY > preimg.offsetHeight - mask.offsetHeight) {
maskY = preimg.offsetHeight - mask.offsetHeight
}
mask.style.left = maskX + 'px';
mask.style.top = maskY + 'px';
//
var bigImg = document.querySelector('.bigImg');
//大图最大移动距离
var bigMaxX = bigImg.offsetWidth - big.offsetWidth;
var bigMaxY = bigImg.offsetWidth - big.offsetWidth;
//大图移动距离
var bigX = maskX * bigMaxX / (preimg.offsetWidth - mask.offsetWidth);
var bigY = maskY * bigMaxY / (preimg.offsetHeight - mask.offsetHeight);
bigImg.style.left = -bigX + 'px';
bigImg.style.top = -bigY + 'px';
})
})
</script>
元素可视区client系列
client翻译过来就是客户端,我们使用client系列的相关属性来获取元索可视区的相关信息。通过client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
client系列属性 | 作用 |
---|---|
element.clientTop | 返回边框上边框的大小 |
element.clientLeft | 返回元素左边框的大小 |
element.clientWidth | 返回自身包括padding、内容区的高度,不含边框,返回数值不带单位 |
element.clientHeight | 返回自身包括padding、内容区的高度,不含边框,返回数值不带单位 |
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
<body>
<div></div>
<script>
//client宽度
var div = document.querySelector('div');
console.log(div.clientLeft); //0
console.log(div.clientTop); //0
console.log(div.clientHeight); //200
console.log(div.clientWidth); //200
</script>
立即执行函数
//1. 立即执行函数:不需要调用,立马能够自己执行的函数
//2.写法
// (function(){})() 或者 (function(){}())
//也可以传参
(function(a, b) {
console.log(a + b); //3
})(1, 2); //第二个()可以看作是调用函数
(function sum(a, b) {
console.log(a + b); //7
}(3, 4))
//3.立即执行函数最大的作用就是 独立创建一个作用域
淘宝flexibleJS源码分析
(function flexible(window, document) {
// 获取的html 的根元素
var docEl = document.documentElement
// dpr 物理像素比
var dpr = window.devicePixelRatio || 1
// adjust body font size 设置我们body 的字体大小
function setBodyFontSize() {
// 如果页面中有body 这个元素 就设置body的字体大小
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
} else {
// 如果页面中没有body 这个元素,则等着 我们页面主要的DOM元素加载完毕再去设置body
// 的字体大小
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10 设置我们html 元素的文字大小
function setRemUnit() {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize 当我们页面尺寸大小发生变化的时候,要重新设置下rem 的大小
window.addEventListener('resize', setRemUnit)
// pageshow 是我们重新加载页面触发的事件
window.addEventListener('pageshow', function(e) {
// e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小(针对火狐)
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))
元素滚动scroll系列
我们使用scroll系列的相关属性可以动痞的得到该元素的大小、滚动距离等。
scroll系列属性 | 作用 |
---|---|
element.scrollTop | 返回被卷去的上侧距离,返回数值不带单位 |
element.scrollLeft | 返回被卷去的左侧距离,返回数值不带单位 |
element.scrollWidth | 返回自身实际的宽度,不含边框,返回数值不带单位 |
element.scrollHeight | 返回自身实际的高度,不含边框,返回数值不带单位 |
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
<body>
<div>
hello
</div>
<script>
var div = document.querySelector('div');
console.log(div.scrollHeight); //实际内容高度
console.log(div.clientHeight);
//scroll: 滚动条发生变化触发的事件
div.addEventListener('scroll', function() {
console.log(div.scrollTop);
})
</script>
固定侧边栏
页面被卷去的头部:可以通过window.pageYOffset 获得︰如果是被卷去的左侧window.pageXOffset
注意:元素被卷去的头部是element.scrollTop ,如果是页面被卷去的头部则是window.pageYOffset
<style>
html,
body {
scroll-behavior: smooth;
}
.header {
width: 80%;
height: 100px;
background-color: pink;
margin: 0 auto;
}
.banner {
width: 80%;
height: 200px;
background-color: purple;
margin: 20px auto;
}
.main {
width: 80%;
height: 1000px;
background-color: skyblue;
margin: 20px auto;
}
.w {
text-align: center;
font-size: 30px;
font-weight: bold;
}
.slider-bar {
width: 50px;
height: 200px;
background-color: red;
position: absolute;
right: 0;
top: 300px;
}
span {
display: none;
position: absolute;
bottom: 0;
}
</style>
<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>
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
var span = document.querySelector('.goBack');
var main = document.querySelector('.main');
var mainTop = main.offsetTop;
//侧边栏固定后应该变化的值
var bannerTop = banner.offsetTop;
//被卷去头部的大小
var sliderbarTop = sliderbar.offsetTop - bannerTop;
//2.页面滚动事件scroll 事件源是document
document.addEventListener('scroll', function() {
// console.log(window.pageYOffset);
if (window.pageYOffset >= bannerTop) {
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderbarTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
//4.到main显示goback
if (window.pageYOffset >= mainTop) {
span.style.display = 'block';
} else {
span.style.display = 'none';
}
});
span.addEventListener('click', function() {
window.scrollTo(0, 0);
})
</script>
三大系列总结
大小对比 | 作用 |
---|---|
element.offsetWidth | 返回自身包括padding、边框,内容区的宽度,返回值不带单位 |
element.clientWidth | 返回自身包括padding、内容区的宽度,不含边框,返回值不带单位 |
element.scrollWidth | 返回自身实际的宽度,不含边框,返回值不带单位 |
主要用法:
- offset系列经常用于获得元素位置offsetLeft ,offsetTop
- client经常用于获取元素大小clientwidth ,clientHeight
- scroll经常用于获取滚动距离 scrollTop ,scrollLeft
- 注意页面滚动的距离通过window. pagexoffset获得
mouseenter和mouseover的区别
mouseenter鼠标事件
- 鼠标移动到元素上时就会触发
- 类似 mouseover,它们两者之间的差别是
- mouseover鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发
- 因为mouseenter不会冒泡
- 跟mouseenter搭配鼠标离开mouseleave同样不会冒泡
<style>
.father {
width: 300px;
height: 300px;
background-color: pink;
}
.son {
width: 200px;
height: 200px;
background-color: purple;
}
</style>
<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);
})
//over在son也触发,enter不触发
</script>
动画函数封装
动画实现原理
**核心原理:**通过定时器setInterval()不断移动盒子位置
实现步骤:
- 获得盒子当前位置
- 让盒子在当前位置上加上一个移动距离
- 利用定时器不断重复这个操作
- 加一个结束定时器的条件
- 注意此元素需要添加定位,才能使用element.style.left
封装
<style>
div {
width: 200px;
height: 200px;
background-color: pink;
position: absolute;
}
span {
width: 300px;
height: 300px;
background-color: purple;
position: absolute;
top: 300px;
}
</style>
<body>
<div></div>
<span>feliks</span>
<script>
var div = document.querySelector('div');
var span = document.querySelector('span');
//动画封装obj:目标对象,target:目标位置
function animate(obj, target) {
var timer = setInterval(function() {
if (obj.offsetLeft >= target) {
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 5 + "px";
}, 30)
}
animate(div, 200);
animate(span, 300);
</script>
优化
<button>点击我才走</button>
<script>
var div = document.querySelector('div');
var span = document.querySelector('span');
var btn = document.querySelector('button');
//动画封装obj:目标对象,target:目标位置
function animate(obj, target) {
//不断点击按钮,速度会越来越快,因为开启了太多定时器
//解决:先清除之前的定时器
obj.timer = setInterval(function() {
if (obj.offsetLeft >= target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 5 + "px";
}, 30)
}
animate(div, 200);
btn.addEventListener('click', function() {
animate(span, 300);
})
</script>
缓动效果原理
让元素运动速度有所变化,最常见的时让速度慢慢停下来
思路:
- 让盒子每次移动的距离慢慢变小
- 算法:(目标值-现在的值)/10 作为每次移动的距离步长
- 停止条件:当前位置=目标位置
<div></div>
<span>feliks</span>
<button>点击我才走</button>
<script>
var div = document.querySelector('div');
var span = document.querySelector('span');
var btn = document.querySelector('button');
//动画封装obj:目标对象,target:目标位置
function animate(obj, target) {
clearInterval(obj.timer);
//不断点击按钮,速度会越来越快,因为开启了太多定时器
//解决:先清除之前的定时器
obj.timer = setInterval(function() {
//步长写在定时器里里面
var step = (target - obj.offsetLeft) / 10;
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
}
//把每次加 改成慢慢变小的值
obj.style.left = obj.offsetLeft + step + "px";
}, 15)
}
animate(div, 400);
btn.addEventListener('click', function() {
animate(span, 500);
})
</script>
- 匀速动画 就是 盒子当前的位置 + 固定的值
- 缓动动画 就是 盒子当前的位置 + 变化的值(目标-当前)/10
动画函数多个目标值之间移动
可让动画函数从800移动到500
点击时,判断步长是正还是负
- 正,向上取整
- 负,向下取整
<div></div>
<span>feliks</span>
<button class="btn500">点击走500</button>
<button class="btn800">点击走800</button>
<script>
var div = document.querySelector('div');
var span = document.querySelector('span');
var btn500 = document.querySelector('.btn500');
var btn800 = document.querySelector('.btn800');
//动画封装obj:目标对象,target:目标位置
function animate(obj, target) {
//不断点击按钮,速度会越来越快,因为开启了太多定时器
//解决:先清除之前的定时器
clearInterval(obj.timer);
obj.timer = setInterval(function() {
//步长写在定时器里里面
//步长改为整数
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
}
//把每次加 改成慢慢变小的值
obj.style.left = obj.offsetLeft + step + "px";
}, 15)
}
animate(div, 400);
btn500.addEventListener('click', function() {
animate(span, 500);
})
btn800.addEventListener('click', function() {
animate(span, 800);
})
</script>
动画函数添加回调函数
**回调函数原理:**函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。
<button class="btn800">点击走800</button>
<script>
var div = document.querySelector('div');
var span = document.querySelector('span');
var btn500 = document.querySelector('.btn500');
var btn800 = document.querySelector('.btn800');
//动画封装obj:目标对象,target:目标位置
function animate(obj, target, callback) {
//先清除之前的定时器
clearInterval(obj.timer);
obj.timer = setInterval(function() {
//步长写在定时器里里面 步长改为整数
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
//回调函数写在定时器结束里面
if (callback) {
callback();
}
}
//把每次加 改成慢慢变小的值
obj.style.left = obj.offsetLeft + step + "px";
}, 15)
}
animate(div, 400);
btn500.addEventListener('click', function() {
animate(span, 500);
})
btn800.addEventListener('click', function() {
animate(span, 800, function() {
span.style.backgroundColor = 'skyblue';
});
})
</script>
动画函数封装到单独JS文件里
并使用
<script src="animate.js"></script>
</head>
<style>
.sliderbar {
width: 40px;
height: 40px;
background-color: pink;
position: relative;
float: right;
z-index: 99;
}
.con {
width: 200px;
height: 40px;
background-color: purple;
position: absolute;
top: 0;
left: 0;
z-index: -1;
text-align: center;
}
</style>
<body>
<div class="sliderbar">
<span>👈</span>
<div class="con">问题反馈</div>
</div>
<script>
var sliderbar = document.querySelector('.sliderbar');
var con = document.querySelector('.con');
sliderbar.addEventListener('mouseenter', function() {
animate(con, -160, function() {
sliderbar.children[0].innerHTML = '👉';
});
})
sliderbar.addEventListener('mouseleave', function() {
animate(con, 0, function() {
sliderbar.children[0].innerHTML = '👈';
});
})
</script>
</body>
常见网页特效案例
轮播图
节流阀
利用回调函数,添加一个变量,锁住函数和解锁函数