一、元素偏移量offset系列
offset相关属性可以动态得到该元素的位置(偏移)、大小等。
- 获得元素距离带有定位的父元素的位置
- 获得元素自身大小(宽度和高度)
- 返回的数值都不带单位
offset系列常用属性:
offset系列属性 | 作用 |
---|
e.offsetParent | 返回作为该元素带有定位的父级元素,如果父级没有定位则返回body | e.offsetTop | 返回元素相对带有定位父元素上方的偏移 | e.offsetLeft | 返回元素相对带有定位父元素左边框的偏移 | e.offsetWidth | 返回自身包括padding、border、content的宽度(返回值不带单位) | e.offsetHeight | 返回自身包括padding、border、content的高度(返回值不带单位) |
1、得到元素带有定位的父元素
console.log(son.offsetParent);
console.log(son.parentNode);
- offsetParent返回的是带有定位的祖先元素
- parentNode返回的是最近一级的父元素,不管父元素是否有定位
2、得到元素的偏移位置
返回值不带单位,是相对于带定位的父元素的偏移。
console.log(father.offsetTop);
console.log(father.offsetLeft);
3、得到元素的大小
返回值不带单位,且包含元素的content+padding+border
console.log(w.offsetWidth);
console.log(w.offsetHeight);
4、offset和style的区别
(1)offset
- offset可以得到任意样式表中的样式值
- 获取的数组没有单位
- offsetWidth包含padding+border+content
- offsetWidth等属性是只读属性,只能获取不能赋值(修改)
- 所以,如果想要获取元素的大小和位置,使用offset更合适
(2)style
- style只能得到行内样式表中的样式值
- style.width获得的是带有单位的字符串
- style.width不包含padding和border
- style.width是可读写属性,可以获取也可以赋值
- 所以,想要修改元素,则需要style改变
5、获取鼠标在盒子中的坐标demo
无法直接获取鼠标在盒子中的坐标,但是可以先获取到鼠标在页面中的坐标,以及盒子相对于页面的位置。用鼠标在页面中的坐标减掉盒子相对于body的位置就可以得到鼠标在盒子中的坐标了。
var box = document.querySelector('.box');
var bx = box.offsetLeft;
var by = box.offsetTop;
box.addEventListener('mousemove', function (e) {
var px = e.pageX;
var py = e.pageY;
var x = px - bx;
var y = py - by;
console.log(x, y);
})
6、模态框拖拽demo
弹出框,也称为,模态框。
在页面中拖拽的原理:鼠标按下并移动,之后松开鼠标。触发事件是鼠标按下mousedown,鼠标移动mousemove和鼠标松开mouseup。在鼠标移动过程中,获得新的值赋给模态框的left和top,这样模态框就可以跟着鼠标走了。
模态框的真正位置就是 鼠标在页面中的坐标-鼠标在盒子中的坐标
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 (e) {
document.removeEventListener('mousemove', move);
})
})
二、元素可视区client系列
1、client系列
client客户端,client系列相关属性可以获取元素可视区的相关信息,通过client系列的相关属性可以动态得到该元素的边框大小、元素大小等。
client系列常用属性:
client系列属性 | 作用 |
---|
e.clientTop | 返回元素的上边框大小 | e.clientLeft | 返回元素左边框大小 | e.clientWidth | 返回元素content+padding宽度,返回值不带单位 | e.clientHeight | 返回元素content+padding高度,返回值不带单位 |
与offset系列最大的区别就是client返回的宽高不包含border
2、flexible.js源码分析
(1)立即执行函数
不需要进行调用就可以执行的函数。
(function (a, b) {
console.log(a + b);
})(1, 2);
(function (a, b) {
console.log(a + b);
}(2, 3));
(function sum(a, b) {
console.log(a + b);
}(2, 3));
立即执行函数最大的作用就是创建了一个独立的作用域。里面所有的变量都是局部变量,不存在变量命名冲突问题。
(2)flexible.js传参
(function flexible(window, document) {
js code...;
} (window, document))
传入两个参数window和document
(3)load事件和pageshow事件
以下三种情况都会刷新页面,触发load事件,也就是说会重新设置字体大小。
a标签 的超链接- F5或者刷新按钮(强制刷新)
- 前进或后退按钮
但是火狐中,有个特点,“往返缓存”。这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。所以此时后退按钮不能刷新页面,那么就可以使用pageshow 事件来触发。
该事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load触发之后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件。注意该事件要给window添加
(4)flexible.js源码分析
(function flexible(window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
function setBodyFontSize() {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
} else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
function setRemUnit() {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
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系列
1、scroll系列
scroll滚动,使用scroll系列的相关属性可以动态获取元素大小、滚动距离等。
scroll系列常用属性:
SCROLL系列属性 | 作用 |
---|
e.scrollTop | 返回被卷去的上侧距离 | e.scrollLeft | 返回被卷去的左侧距离 | e.scrollWidth | 返回自身实际内容的宽度,不包含border | e.scrollHeight | 返回自身实际内容的高度,不包含border |
注意返回值没有单位。
scrollWidth和scrollHeight都是返回实际内容的大小,如果内容超出盒子的宽高,会按照内容的实际宽高返回;如果没超出,跟clientWidth和clientHeight差不多,都是返回content+padding的。
2、页面被卷去的头部
如果浏览器的宽高不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上方被隐藏掉的高度,就称为页面被卷去的头部。滚动条在滚动时会触发onscroll事件。
仿淘宝固定右侧侧边栏demo
- 页面被卷去的头部可以通过
window.pageYOffset 获取,被卷去的左侧window.pageXOffset - 元素被卷去的头部是
element.scrollTop
var sliderBar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var bannerTop = banner.offsetTop;
var sliderBarTop = sliderBar.offsetTop;
var mainTop = main.offsetTop;
document.addEventListener('scroll', function () {
if (window.pageYOffset >= bannerTop) {
sliderBar.style.position = 'fixed';
sliderBar.style.top = sliderBarTop - bannerTop + 'px';
} else {
sliderBar.style.position = 'absolute';
sliderBar.style.top = sliderBarTop + 'px';
}
if (window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
})
需要注意的是,页面被卷去的头部有兼容性问题。因此被卷去的头部通常有如下几种写法:
- 声明了DTD,使用
document.documentElement.scrollTop - 未声明DTD,使用
document.body.scrollTop - 新方法:
window.pageYOffset 和window.pageXOffset ,IE9开始支持
3、offset、client、scroll三大系列总结
(1)offset系列常用于获取元素位置 offsetTop offsetLeft (2)client系列常用于获取元素大小 clientWidth clientHeight (3)scroll系列常用于获取滚动距离 scrollTop scrollLeft
4、mouseenter和mouseover区别
mouseover 鼠标经过自身盒子会触发事件,经过子盒子还会触发mouseenter 只有经过自身盒子才会触发事件
<div class="father">
<div class="son"></div>
</div>
<script>
var father = document.querySelector('.father');
var son = document.querySelector('.son');
father.addEventListener('mouseenter', function () {
console.log(11);
})
</script>
这是因为mouseenter 事件不会冒泡。而对于mouseover 来说,经过父盒子会触发,鼠标经过子盒子的时候,从子盒子冒泡到父盒子,又触发事件。鼠标离开mouseleave 同样也不会冒泡~
四、动画
1、动画实现原理
核心原理:通过定时器setInterval() 不断移动盒子位置。
实现步骤:
1、获得盒子当前位置 2、让盒子在当前位置基础上,向右移动5px 3、利用定时器不断重复这个操作 4、加一个结束定时器的条件 5、注意此元素需要添加定位,才能使用element.style.left改变位置
2、动画函数简单封装
(1)封装
注意函数需要传递两个参数,动画对象和移动到的距离。
function animate(obj, target) {
var timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 5 + 'px';
}, 30);
}
不同元素,要使用动画时调用函数即可。
(2)优化
每次调用函数都会声明一个timer变量,开辟一个内存空间。而且,多个不同元素的定时器都叫timer,容易产生冲突。所以,可以将timer添加给obj对象作为obj的属性。注意:停止动画时,也要清除的是obj.timer
function animate(obj, target) {
obj.timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 5 + 'px';
}, 30);
}
(3)修改bug
如果添加一个按钮,点击按钮时,才执行动画函数。当我们不断点击按钮的时候,元素会越走越快。
这是因为每次调用都会添加一个新的定时器,为了解决这个问题,需要先清除之前的定时器,只保留当前一个定时器。
function animate(obj, target) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 5 + 'px';
}, 30);
}
var div = document.querySelector('div');
var span = document.querySelector('span');
var btn = document.querySelector('button');
animate(div, 400);
btn.addEventListener('click', function () {
animate(span, 300);
})
3、缓动动画原理
缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来。
思路:
1.让盒子每次移动的距离慢慢变小,速度就会慢慢降下来。 2.核心算法:每次移动的步长 = (目标值 - 现在的位置) / 10 3.停止的条件:当前位置 = 目标位置,就停止定时器
匀速动画:盒子位置 = 盒子当前位置 + 固定值; 缓动动画:盒子位置 = 盒子当前位置 + 变化的步长[(目标值 - 现在的位置) / 10]
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';
}, 30);
}
但盒子无法达到准确位置(有小数),所以我们将步长值修改为整数: var step = Math.ceil((target - obj.offsetLeft) / 10);
要注意判断步长的正负(因为有可能倒着走):
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
4、动画函数添加回调函数
回调函数原理:函数可以作为一个参数,将这个函数作为参数传入另一个函数里面,当那个函数执行完之后,再执行传入的函数,这个过程称为回调。
回调函数写到定时器结束之后:
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';
}, 30);
}
在函数调用的时候,传递实参:
btn500.addEventListener('click', function () {
animate(span, 500, function () {
span.style.backgroundColor = 'red';
});
})
5、动画函数封装到js文件中
因为要经常使用这个动画函数,可以单独封装到一个js文件中,使用时直接引用这个js文件。 使用方法:
1.新建一个js文件,存放animate函数 2.在html文件中引入animate.js文件,并调用animate函数
引入animate.js :<script src="animate.js"></script>
<div class="sliderbar">
<span>←</span>
<div class="con">问题反馈</div>
</div>
<script>
var sliderbar = document.querySelector('.sliderbar');
var con = document.querySelector('.con');
var span = document.querySelector('span');
sliderbar.addEventListener('mouseenter', function () {
animate(con, -160, function () {
span.innerHTML = '→';
});
})
sliderbar.addEventListener('mouseleave', function () {
animate(con, 0, function () {
span.innerHTML = '←';
});
})
</script>
|