一、首先,我们先了解一下三大家族及其区别(offset/scroll/client)
1.offset
1.1.offsetWidth = width+padding+border;
1.2.offsetHeight = Height+padding+border;
1.3.offsetTop:检测距离带有定位的父盒子的上面的距离,返回值不带有px
1.4.offsetLeft:检测距离带有定位的父盒子的左边的距离,返回值不带有px
1.5.offsetParent:检测最近的带有定位的父盒子的节点,返回值是一个节点。(若父盒子都没有定位,则返回body)
2.scroll
2.1.scrollWidth = width+padding;
2.2.scrollHeight = Height+padding;如果文字超出了盒子,高度为超出盒子的内容的高。不超出是盒子本身高度。IE8以下(不包括IE8) 为盒子本身内容的高度。
2.3.scrollTop:特指网页被卷去的头部的高度(这个属性的获取有兼容问题,下文将阐述)
2.4.scrollLeft:特指网页左边被卷去的宽度(一般不用)
3.client
3.1.clientWidth = width+padding;
3.2.clientHeight = Height+padding;
3.3.clientTop:获取盒子的上border的宽度
3.4.clientLeft:获取盒子的左侧border的宽度
3.5.clientX:event调用,鼠标距离浏览器可视区域的左边距离
3.6.clientY:event调用,鼠标距离浏览器可视区域的顶部距离
二、各种函数封装(动画、获取值)
1.匀速动画封装
function animate(ele,target){
//要用定时器,先清除定时器
//一个盒子只能有一个定时器,这样儿的话,不会和其他盒子出现定时器冲突
//而定时器本身讲成为盒子的一个属性
clearInterval(ele.timer);
//我们要求盒子既能向前又能向后,那么我们的步长就得有正有负
//目标值如果大于当前值取正,目标值如果小于当前值取负
var speed = target>ele.offsetLeft?10:-10;
ele.timer = setInterval(function () {
//在执行之前就获取当前值和目标值之差
var val = target - ele.offsetLeft;
ele.style.left = ele.offsetLeft + speed + "px";
//目标值和当前值只差如果小于步长,那么就不能在前进了
//因为步长有正有负,所有转换成绝对值来比较
if(Math.abs(val)<Math.abs(speed)){
ele.style.left = target + "px";
clearInterval(ele.timer);
}
},30)
}
2.缓动动画封装
function animate(ele,target) {
//要用定时器,先清定时器
clearInterval(ele.timer);
//定义定时器
ele.timer = setInterval(function () {
//获取步长
//步长应该是越来越小的,缓动的算法。
var step = (target-ele.offsetLeft)/10;
//对步长进行二次加工(大于0向上取整,小于0向下取整)
step = step>0?Math.ceil(step):Math.floor(step);
//动画原理: 目标位置 = 当前位置 + 步长
ele.style.left = ele.offsetLeft + step + "px";
//检测缓动动画有没有停止
//console.log(1);
if(Math.abs(target-ele.offsetLeft)<=Math.abs(step)){
//处理小数赋值
ele.style.left = target + "px";
clearInterval(ele.timer);
}
},30);
}
3.缓动动画封装(单个属性)(这里有一个获取元素样式的封装函数getStyle,后文将给出)
//参数变为3个
function animate(ele,attr,target){
//先清定时器
clearInterval(ele.timer);
ele.timer = setInterval(function () {
//四部
var leader = parseInt(getStyle(ele,attr)) || 0;
//1.获取步长
var step = (target - leader)/10;
//2.二次加工步长
step = step>0?Math.ceil(step):Math.floor(step);
leader = leader + step;
//3.赋值
ele.style[attr] = leader + "px";
//4.清除定时器
if(Math.abs(target-leader)<=Math.abs(step)){
ele.style[attr] = target + "px";
clearInterval(ele.timer);
}
},25);
}
4.缓动动画封装(多个属性,多个属性将用json来实现)(这里有一个获取元素样式的封装函数getStyle,后文将给出)
function animate(ele,json){
//先清定时器
clearInterval(ele.timer);
ele.timer = setInterval(function () {
//开闭原则
var bool = true;
//遍历属性和值,分别单独处理json
//attr == k(键) target == json[k](值)
for(var k in json){
//四部
var leader = parseInt(getStyle(ele,k)) || 0;
//1.获取步长
var step = (json[k] - leader)/10;
//2.二次加工步长
step = step>0?Math.ceil(step):Math.floor(step);
leader = leader + step;
//3.赋值
ele.style[k] = leader + "px";
//4.清除定时器
//判断: 目标值和当前值的差大于步长,就不能跳出循环
//不考虑小数的情况:目标位置和当前位置不相等,就不能清除定时器。
if(json[k] !== leader){
bool = false;
}
}
console.log(1);
//只有所有的属性都到了指定位置,bool值才不会变成false;
if(bool){
clearInterval(ele.timer);
}
},25);
}
5.缓动动画封装(回调函数)(这里有一个获取元素样式的封装函数getStyle,后文将给出)
//参数变为3个
function animate(ele,json,fn){
//先清定时器
clearInterval(ele.timer);
ele.timer = setInterval(function () {
//开闭原则
var bool = true;
//遍历属性和值,分别单独处理json
//attr == k(键) target == json[k](值)
for(var k in json){
//四部
var leader = parseInt(getStyle(ele,k)) || 0;
//1.获取步长
var step = (json[k] - leader)/10;
//2.二次加工步长
step = step>0?Math.ceil(step):Math.floor(step);
leader = leader + step;
//3.赋值
ele.style[k] = leader + "px";
//4.清除定时器
//判断: 目标值和当前值的差大于步长,就不能跳出循环
//不考虑小数的情况:目标位置和当前位置不相等,就不能清除清除定时器。
if(json[k] !== leader){
bool = false;
}
}
console.log(1);
//只有所有的属性都到了指定位置,bool值才不会变成false;
if(bool){
clearInterval(ele.timer);
//所有程序执行完毕了,现在可以执行回调函数了
//只有传递了回调函数,才能执行
if(fn){
fn();
}
}
},25);
}
6.获取元素样式封装
function getStyle(ele,attr){
if(window.getComputedStyle){
return window.getComputedStyle(ele,null)[attr];
}
return ele.currentStyle[attr];//兼容IE
}
7.获取顶部或左边被卷去部分的高度或宽度封装(返回值是一个json,可以用.top获取)
function scroll(){
//简单封装(实际工作使用)
return {
"top": window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop,
"left": window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft
}
}
8.获取屏幕可视区域的宽高的封装(返回值是一个json,可以用.width获取)
//获取屏幕可视区域的宽高
function client(){
if(window.innerHeight !== undefined){
return {
"width": window.innerWidth,
"height": window.innerHeight
}
}else if(document.compatMode === "CSS1Compat"){//判断是否有dtd约束
return {
"width": document.documentElement.clientWidth,
"height": document.documentElement.clientHeight
}
}else{
return {
"width": document.body.clientWidth,
"height": document.body.clientHeight
}
}
}
三、event对象的属性
1.获取event对象:在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事件有关的信息event对象要在事件的驱动函数里面获取,如:
document.onclick = function (event) {
//兼容写法,兼容IE浏览器
event = event || window.event;
}
2.event的属性:
特别注意pageY/X这两个属性,它是表示鼠标位于整个网页页面的顶部和左侧部分的距离。
但是IE不支持这两个属性,于是就有了pageY和pageX的兼容写法:
var pagey = event.pageY || scroll().top + event.clientY;
var pagex = event.pageX || scroll().left + event.clientX;
//鼠标在页面的位置=被卷去的部分+看得见的部分
四、事件和冒泡/取消冒泡
1.事件和一些特殊属性
1.滚动事件:window.onscroll
2.屏幕滚动到具体位置:window.scrollTo(x,y)//屏幕滚动到坐标x,y
3.鼠标按下:onmousedown 鼠标弹起:onmouseup
4.鼠标移动:onmousemove //如果事件是鼠标按下并移动,那这个事件的调用者应该是document(如拖拽一个盒子、模拟滚动条)
5.浏览器大小改变:window.onresize
6.检测屏幕宽度和高度:window.screen.width/window.screen.height
2.冒泡、取消冒泡、捕获、事件委托
1.冒泡就是当子盒子和父盒子都绑定了同一个事件时,如果子盒子的事件被触发,那么父盒子相应的事件会在之后触发。
2.捕获刚好和冒泡相反,子盒子的事件被触发时,父盒子相应的事件会先触发,子盒子的事件再触发。想要实现捕获可以用addEventListenner(参数1,参数2,参数3)这个方法。参数1:事件去掉on ;参数2 :调用的函数;参数3:可有可无。(不写或是false是冒泡,True是捕获。)
3.取消冒泡:
元素.事件= function (event) {
//阻止冒泡
event = event || window.event;
if(event && event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;//兼容IE浏览器
}
}
4.事件委托:事件委托就是普通的事件绑定没有办法为新创建的元素绑定事件。所以我们要使用冒泡的特性来实现事件委托。比如我们新创建了一个li标签,我们想给这个标签绑定一个事件,就可以给他的父类ul绑定一个事件,然后用event.target来获取事件触动的时候传递过来的对象,判断之后再进行操作,给出一个样例代码:
//事件委托
ul.onclick = function (event) {
//获取事件触动的时候传递过来的值
event = event || window.event;
var a = event.target?event.target:event.srcElement;//兼容IE写法
//判断标签名,如果是li标签就绑定驱动函数
if(a.tagName === "LI"){
alert("我是li");
}
}