9. 三大系列
本篇一开始我们已经学了三大系列中的offset系列
,三大系列分别是offset
系列、scroll
系列、client
系列。学习这些有什么用呢?在后面的特效案例中,会大量的使用到获取元素的宽度、获取元素内部的宽度、获取元素距离顶部的距离等。这时候就需要用到三大系列,下面为大家一一讲解三大系列的用法。
9.1 offset 系列
第一章已经讲过了,详见第一章。
9.2 scroll 系列
scroll
是用来获取盒子内容的大小和位置。scroll
家族有:scrollWidth
、scrollHeight
、scrollLeft
、scrollTop
。
1、onscroll 事件
前面DOM的时候,我们知道了触发事件,这里讲下onscroll事件。
对于有滚动条的盒子,可以使用onscroll
注册滚动事件,每滚动一像素,就会触发该事件。
示例代码: [31-scroll系列-onscroll事件.html]
我是内容我是内容我是内容我是内容我是内容我是内容我是内容
...
...
...
我是内容我是内容我是内容我是内容我是内容我是内容我是内容
效果图:
2、scrollWidth 和 scrollHeight
scrollWidth
与scrollHeight
是盒子内容的真实的宽度和高度。与和盒子大小无关,仅仅与盒子的内容有关系,不包括border
、margin
,包括padding
。
scrollWidth = padding + width;
// 如果盒子里面的内容超出盒子高度的时候,这里的scrollHeight获取的就是内容的高度了
scrollHeight = padding + height;
示例代码: [32-scroll系列-scrollWidth&scrollHeight.html]
杨柳青青江水平,闻郎江上踏歌声。东边日出西边雨,道是无晴却有晴。
杨柳青青江水平,闻郎江上踏歌声。东边日出西边雨,道是无晴却有晴。
效果图:
如果盒子里面的内容超出盒子高度的时候,这里的scrollHeight
获取的就是内容的高度了
注意:
- 在现代高版本浏览器中,
scrollHeight
,在内容没有超度盒子的情况下,获取到的高度是height+padding
- 但是在
IE8
以下的时候,即使内容没有超出盒子,获取到的高度也是内容的高度。这里就不演示了,scrollHeight
很少用到。
3、scrollTop 和 scrollLeft
scrollTop
是盒子内容被滚动条卷去的头部的高度。scrollLeft
是盒子内容被滚动条卷去的左侧的宽度。通常来说,scroll
系列用的最多的地方就是用来获取页面被卷去的宽度和高度,非常的常用。
scrollTop
和 scrollLeft
存在兼容性
示例代码: [33-scroll系列-scrollTop&scrollLeft.html]
效果图:
完整版封装函数: [34-scroll系列-scrollTop&scrollLeft兼容性封装.html]
function getScroll() {
// 返回的是一个对象,调用的时候 getScroll().top 获取页面被卷去的头部的距离
return {
left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0,
top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
}
}
返回值是一个对象,需要获得卷去头部的距离,只需要调用getScroll.top
即可。
scroll 系列图解:
示例代码:固定导航栏 [ 35-scroll系列-固定导航栏.html ]
- 通过
offsetHeight
获取导航栏上部元素自身的高度,判断scrollTop
的高度大于等于上部元素高度的时候,说明scrollTop
到导航栏的位置了; - 到达导航栏位置,给导航栏绝对定位在页面顶部,同时因为固定定位,导航栏脱标,所以要获得导航栏下部的元素,将其
margin-top
设置为导航栏的高度,将位置空出来; - 当
scrollTop
小于上部元素高度的时候,导航栏去掉固定定位,同时将下部元素的margin-top
设置为0
。
为什么一开始不直接拿导航栏到顶部的距离跟 scrollTop
比较呢?,因为导航栏固定定位之后位置就变了,恢复原来位置时的判断就不生效了
顶部广告栏
内容1
内容2
内容3
效果图:
示例代码:两侧跟随小广告 [ 36-offset系列-两侧跟随小广告.html ]
- 需求:屏幕滚动多少,两侧广告缓动等距离
- 将两张图片绝对定位在屏幕两侧的中间
- 通过滚动事件,实时获取
scrollTop
的值 - 将获取到的
scrollTop
的值,通过缓动动画设置给两侧图片(需要将之前top
的高度加上去)
内 容
. . .
. . .
效果图:
示例代码:返回顶部 [ 37-offset系列-返回顶部.html ]
window.scrollTo(0, 0);
让滚动条回到(0,0)
位置。这是回到顶部的主要原理
- 注册滚动条滚动事件,实时获取
scrollTop
的位置,判断当它距离大于等于800
的时候,让回到顶部的按钮,缓动的显示出来。当scrollTop
位置小于800
的时候,让回到顶部的按钮,缓动的隐藏起来。 - 给回到顶部按钮注册点击事件,当点击的时候,页面缓动的回到顶部。这里就要用到刚刚提到的知识点:
window.scrollTo()
- 重新创建一个缓动动画框架,目标位置
target
就是scrollTo(0,target)
,所以,target
的值为0
; - 实现原理与最基本的缓动框架基本一样,只是将设置的值改为:
window.scrollTo(0,leader)
;此时的leader
还是一个未知数,leader
其实就是当前滚动条的位置,所以,在滚动事件里,只要将leader
实时获取滚动条位置即可。
...
内容
...
效果图:
示例代码:楼层跳跃 [ 38-offset系列-楼层跳跃.html ]
- 其实这里的案例跟上面的返回顶部很类似,同样的运用到的是
window.scrollTo()
- 页面布局,背景继承
body
,和html
的100%
,将背景的索引与左边导航栏绑定 - 创建一个缓动动画框架,目标距离
target
,就是当前索引背景距离顶部的距离,leader
就是滚动条此时的位置
- 鞋子区域
- 袜子区域
- 裤子区域
- 裙子区域
- 帽子区域
- 鞋子
- 袜子
- 裤子
- 裙子
- 帽子
效果图:
9.3 client 系列
client
家族用于获取盒子可视区的大小。client
家族有clientWidth
、clientHeight
、clientLeft
、clientTop
。
1、clientWidth 和 clientHeight
-
clientWidth
:获取网页可视区域宽度 ;clientHeight
:获取网页可视区域高度; -
调用者不同,意义不同:
- 盒子调用,指盒子本身;
-
html/body
调用:可视区域大小
- 不包括
border
和margin
图解clientWidth和clientHeight:
clientWidth 和 clientHeight 兼容性封装:
function getClient() {
return {
width: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0,
height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight || 0
};
}
onresize事件:
onresize
事件会在窗口被调整大小的时候发生。
window.onresize = function(){
//事件处理程序
}
示例代码:模仿响应式布局 [ 39-client系列-模拟响应式.html ]
// 页面一进来的时候就执行一次,确定浏览器可视区域的宽度
responsive();
// 浏览器窗口调整触发事件
window.onresize = function() {
responsive();
};
// 获取浏览器宽度
function responsive() {
var pageWidth = getClientWidth();
if (pageWidth >= 960) {
//说明是pc
document.body.style.backgroundColor = "#B7F5DE";
} else if (pageWidth >= 640) {
//说明是平板
document.body.style.backgroundColor = "#CBF078";
} else {
// 说明是手机
document.body.style.backgroundColor = "#F59292";
}
}
// clientWidth 兼容性处理
function getClientWidth() {
return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0;
}
效果图:
2、clientX 和 clientY
-
clientX
:鼠标距离可视区域左侧距离(event
调用)event
:事件对象,下面会讲 -
clientY
:鼠标距离可视区域上侧距离(event
调用)
事件对象的时候,单独讲解
3、clientTop 和 clientLeft
-
clientTop
:盒子的上部border
的宽度 -
clientleft
:盒子的左部border
的宽度
用的很少很少,基本不会用到
9.4 screen 系列
clientWidth
获取的其实是浏览器窗口的宽度,想要获取用户显示的分辨率怎么办呢?
获取用户显示器分辨率有专门的方法:window.screen.width
和window.screen.Height
示例代码:获取显示器分辨率 [ 40-screen系列-获取显示器分辨率.html ]
document.write("屏幕分辨率为:" + window.screen.width + "*" + window.screen.height);
效果图:
1280*720 分辨率的情况下:
1920*1080 分辨率的情况下:
9.5 三大系列的区别
图解三大系列区别:
width 和 height:
-
clientWidth
/clientHeight
:- clientWidth =
width
+padding
; - clientHeight =
height
+padding
;
- clientWidth =
-
offsetWidth
/offsetHeight
:- offsetWidth =
width
+padding
+border
; - offsetHeight =
heigth
+padding
+border
;
- offsetWidth =
-
scrollWidth
/scrollHeight
:- scrollWidth = 内容宽度(不包含
border
); - scrollHeight = 内容高度(不包含
border
);
- scrollWidth = 内容宽度(不包含
top 和 left:
-
offsetTop
/offsetLeft
:- 调用者:任意元素。(盒子为主)
- 作用 :获取距离父系盒子中带有定位的距离。
-
scrollTop
/scrollLeft
:(盒子也可以调用,必须有滚动条)- 调用者:
document.body.scrollTop/.....(window)
- 作用:浏览器无法显示的部分(被卷去的部分)。
- 调用者:
-
clientY
/clientX
:(clientTop
/clientLeft
值是border
)- 调用者:
event.clientX(event)
- 作用:鼠标距离浏览器可视区域的距离(左、上)。
- 调用者:
10. 事件对象
10.1 事件对象的概述
在触发某个事件的时候,都会产生一个事件对象 Event
,这个对象中包含所有与事件相关的一些信息,包括触发事件的元素,事件的类型以及其他与事件相关的信息。
比如:
- 鼠标事件触发时,事件对象中会包含鼠标的位置信息。
- 键盘事件触发时,事件对象中会包含按下的键相关的信息。
10.2 获取事件对象
既然事件对象中存储了这么多的信息,我们首先需要做的就是获取到这个事件对象。获取事件对象的时候,存在浏览器的兼容问题。
现代浏览器:
获取事件对象非常的简单,只需要在注册事件的时候,指定一个形参即可。这个形参就是我们想要获取到的事件对象。
btn.onclick = function(event){
// event就是事件对象,里面包含了事件触发时的一些信息。
// 触发事件的时候,事件是由浏览器调用,生成一个事件对象,里面包含了一些信息,当成实参传递进来了。
console.log(event);
}
IE678:
获取事件对象则是另一种方式,在事件里面,通过window.event
来获取事件对象
btn.onclick = function(){
// IE678通过window.event获取事件对象
// IE678浏览器在触发的事件的时候,生成一个事件对象,但是呢,并没有当成实参传过来。会给window.event这个属性。
var event = window.event;
console.log(event);
}
兼容性封装: [ 41-事件对象Event兼容性.html ]
btn.onclick = function(event){
//只要用到了事件对象,就要记得处理浏览器兼容性
event = event || window.event;
}
10.3 事件对象的常用属性
事件对象中有很多很多的属性,但是很多属性并不常用。我们经常用到的是鼠标位置信息 和键盘码 相关的信息。
打印event对象我们可以看到如下信息:
我们可以看到一个鼠标按下的时候,它的事件对象里面有这么多属性,但是最常用的也就是鼠标位置信息和键盘码相关的信息。
记录了鼠标位置信息的相关属性:
-
screenX
与screenY
:光标相对于屏幕左上角的水平位置与垂直位置。 -
clientX
与clientY
:光标相对于可视区左上角的水平位置和垂直位置。 -
pageX
与pageY
:光标相对于网页(文档document
)左上角的水平位置与垂直位置(推荐使用)
[ 42-事件对象-鼠标三种获取位置的属性.html ]
document.onclick = function(e) {
var e = e || window.event;
//获取鼠标的位置,相对的是可视区最左上角的点。(忽略滚动的距离)
console.log("client(" + e.clientX + "," + e.clientY + ")");
//获取鼠标的位置,相对的页面最左上角的位置 (计算滚动的距离)
console.log("page(" + e.pageX + "," + e.pageY + ")");
//获取鼠标的位置,相对的是屏幕最左上角的那个点
console.log("screen(" + e.screenX + "," + e.screenY + ")");
}
图解:
记录了键盘码的属性:
-
event.keyCode
:键盘按下的那个键的键盘码
10.4 pageX与pageY的兼容性
在鼠标事件中,记录鼠标位置信息的属性有很多,使用最多的还是pageX
与pageY
这两个属性,但是pageX
和pageY
存在浏览器兼容性问题。
在现代浏览器中: 直接通过事件对象就可以获得pageX
与pageY
document.onclick = function (event) {
event = event || window.event;
console.log(event.pageX+","+event.pageY);
}
在IE678中: 并没有pageX
与pageY
,但是我们可以通过scrollTop + clientY
的方式进行计算来获得pageY
。
document.onclick = function (event) {
event = event || window.event;
// 在IE678中使用document.documentElement.scrollTop就可以获取到scrollTop的值
alert(event.clientY + document.documentElement.scrollTop);
}
pageX与pageY的兼容性封装:
function getPage(event) {
return {
//在IE678中使用document.documentElement.scrollLeft就可以获取到scrollLeft的值
x:event.pageX || event.clientX + document.documentElement.scrollLeft,
y:event.pageY || event.clientY + document.documentElement.scrollTop
}
}
调用时:
getPage(event).x;
getPage(event).y;
示例代码:兼容性封装测试 [ 43-事件对象-pageX&PageY兼容性处理.html ]
10.5 案例:鼠标跟随
- 鼠标跟随,指的就是,鼠标后面有一张图片,会在页面中一直跟随鼠标
- 通过事件对象的属性,我们知道了有三种方法获取鼠标的位置信息,我们只要把鼠标的位置,赋值给后面跟随图片的位置,就可以实现图片一直跟随鼠标移动了
- 我们知道
clientX/Y
、screenX/Y
、pageX/Y
,都可以获取鼠标的位置,但是各有优劣,我们先使用pageX/Y
获取,上面我们已经处理pageX/Y
的兼容性了,所以这里直接使用 - 我们只需将获得的鼠标位置,赋值给定位后的图片,将图片绝对定位,再给页面注册鼠标移动事件,图片就会一直跟着鼠标移动。
[ 44-事件对象-跟随鼠标移动.html ]
效果图:
10.6 案例:拖拽效果
1、获取鼠标在盒子中的位置
当在盒子里面点击鼠标的时候,怎么获得这个鼠标在盒子中的位置呢?
没有直接的方法能够获取,但是我们可以通过:
获取鼠标的位置 - 盒子距离顶部和左边的距离 = 鼠标在盒子里面的距离
[ 46-事件对象-获取鼠标在盒子中的位置.html ]
效果图:
2、拖拽效果
拖拽效果在网页很常见,比如一个注册框,弹出来的时候,你可以拖动它的位置。
新事件:
-
onmousedown
:当鼠标按下的时候触发 -
onmouseup
:当鼠标弹起的时候触发
实现思路:
- 给盒子注册按下鼠标(
onmousedown
)事件,获取鼠标在盒子里的位置; - 然后在里面注册页面移动鼠标(
onmousemove
)事件,鼠标移动时,将此时的鼠标距浏览器的距离减去鼠标在盒子中的距离后,赋值给盒子的top/left
- 给页面注册松开鼠标(
onmouseup
)事件,盒子应该停在那个位置,所以清除移动事件。
[ 47-事件对象-拖拽效果.html ]
效果图:
注意:
- 拖拽的时候,可能里面会有文字,当移动的时候,不小心获取文字焦点的时候,就不能清除鼠标移动事件了。
解决方法:
清除选中的文字
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
10.7 案例:放大镜
放大镜在开发中是一个很常见的特效,但是所有的放大镜的实现效果都是一样。
图解放大镜原理:
实现思路:
- 当鼠标经过
smallBox
的时候,显示mask
和bigBox
; - 当鼠标离开
smallBox
的时候,隐藏mask
和bigBox
; - 获取鼠标在
smallBox
里面的位置; - 获得鼠标在
smallBox
里面的位置后,要减去 mask 一半的宽高,否则鼠标不在mask
中间显示; -
判断x的值限定
mask
的位置 :-
mask
在小盒子里面能够移动最大的宽度和高度0
; -
mask
在小盒子里面能够移动最大的宽度 =smallBox
的宽度 -mask
的宽度
-
- 设定 mask 的位置;
-
让大图片等比例的跟着动 :
-
bigImg
能够移动的距离 /mask
能移动的距离 = 大图片移动的距离 /mask
移动的距离
-
思路图解:
示例代码: [ 48-事件对象-放大镜效果.html ]
效果图:
11. 注册事件
前面我们已经知道了许多触发事件的名称,但是我们只知道了一种注册事件的方式,就是"on
+ 事件名称",下面会为大家再介绍一种注册事件的方式:addEventListener
。
11.1 on + 事件名称 方式
onclick
、onmouseover
这种on
+ 事件名称的方式注册事件几乎所有的浏览器都支持。
注册事件:
box.onclick = function(){
//事件处理程序
}
移除事件:
box.onclick = null;
on + 事件名称注册事件的缺点:
同一个元素同一类型的事件,只能注册一个,如果注册了多个,会出现覆盖问题。
document.onclick = function(){
console.log("呵呵");
}
document.onclick = function(){
console.log("哈哈"); // 最后打印的是 "哈哈",呵呵会被覆盖掉
}
11.2 addEventListener 方式
现代浏览器支持的注册事件的新方式,这种方式注册的事件不会出现覆盖问题。以后在手机端就用这种注册事件的方式。
1、addEventListener 注册事件的语法:
// add:添加 Event:事件 Listener:监听器
// 三个参数:
// 1. type:事件类型 "click","mouseover"... 不要再加 on了
// 2. 函数:事件触发的时候要执行的程序
// 3. useCapture(是否使用事件捕获) :true & false 默认是false
document.addEventListener(type,function(){
// 事件处理程序
},useCapture);
之前我们说过window.onload
,只能注册一个就是这个原因,因为"on +
"注册方式会覆盖。所以如果真的需要执行两个window.onload
事件的时候,我们就可以使用addEventListener
注册:
window.addEventListener("load",function(){
// 预加载函数 1
});
window.addEventListener("load",function(){
// 预加载函数 2
});
示例代码: [ 49-注册事件-addEventListener.html ]
// 给页面注册点击事件后,会同时打印 "呵呵呵","哈哈哈"
document.addEventListener("click", function() {
console.log("呵呵呵");
});
document.addEventListener("click", function() {
console.log("哈哈哈");
});
2、removeEventListener 移除事件的语法:
// remove:移除 Event:事件 Listener:监听器
// 三个参数:
// 1. type:事件类型 "click","mouseover"
// 2. 函数名:要移除的那个函数
// 3. useCapture(是否使用事件捕获) :true & false 默认是false
document.addEventListener(type,fn,useCapture);
注意:
要想一个事件能够被移除,在它注册事件的时候,执行函数必须要有函数名,不能是匿名函数。因为移除事件的时候,就是移除的这个函数名。
示例代码: [ 50-移除事件-removeEventListener.html ]
// 第二个点击事件就被移除了
document.addEventListener("click", fn1);
document.addEventListener("click", fn2);
function fn1() {
console.log("呵呵呵");
};
function fn2() {
console.log("哈哈哈");
}
// 移除第二个点击事件
document.removeEventListener("click", fn2);
3、IE678兼容性问题:
IE678
不支持addEventListener
与removeEventListen
两个方法,但是支持attachEvent
与detachEvnet
。
attachEvent注册事件的语法:
// attach :附上;系上;贴上
// 参数:
// 1. type:事件类型 需要加上on "onclick","onmouseenter"...
// 2. 函数fn:需要执行的那个事件函数
attachEvent(type, function(){
// 事件处理程序
});
attach注册时间的时候,事件类型要加上on
,没有为什么,IE就这样
detachEvent的用法:
// detach :脱离
// 参数:
// 1. type:事件类型 需要加上on "onclick","onmouseenter"...
// 2. 函数名: 需要执行的那个事件函数名
detachEvent(type, fn);
示例代码: [ 51-注册事件-IE678方法.html ]
// IE678 下运行
document.attachEvent("onclick", fn1);
document.attachEvent("onclick", fn2);
function fn1() {
alert("123");
};
function fn2() {
alert("456");
};
// 移除第一个注册事件
document.detachEvent("onclick", fn1);
4、兼容性处理:
注册事件的新方式的解决了事件覆盖的问题,但是存在浏览器兼容性问题,因此可以进行兼容性封装。
// 添加事件兼容性封装
function addEvent(element, type, fn) {
// 能力检测
if (element.addEventListener) {
element.addEventListener(type, fn);
} else if (element.attachEvent) {
element.attachEvent("on" + type, fn);
} else {
//如果都不行,那就用on方式
element["on" + type] = fn;
}
};
//移除事件兼容性封装
function removeEvent(element, type, fn) {
if (element.removeEventListener) {
element.removeEventListener(type, fn);
} else if (element.detachEvent) {
element.detachEvent("on" + type, fn);
} else {
element["on" + type] = null;
}
}
示例代码: [ 52-注册事件-封装兼容性.html ]
// 添加事件兼容性封装
function addEvent(element, type, fn) {
// 能力检测
if (element.addEventListener) {
element.addEventListener(type, fn);
} else if (element.attachEvent) {
element.attachEvent("on" + type, fn);
} else {
//如果都不行,那就用on方式
element["on" + type] = fn;
}
};
//移除事件兼容性封装
function removeEvent(element, type, fn) {
if (element.removeEventListener) {
element.removeEventListener(type, fn);
} else if (element.detachEvent) {
element.detachEvent("on" + type, fn);
} else {
element["on" + type] = null;
}
}
function fn1() {
alert("呵呵");
}
function fn2() {
alert("哈哈");
}
addEvent(document, "click", fn1);
addEvent(document, "click", fn2);
removeEvent(document, "click", fn1);
12. 事件冒泡和事件捕获
事件冒泡和事件捕获其实可以理解成一样东西,就是当父级元素和子元素都具有点击事件的时候,点击触发子级元素的时候父级元素也会被触发。事件冒泡是IE678
在处理事件间机制的一种说法,它执行的顺序是由内向外的,就是从子元素一直到window
。 事件捕获是火狐在处理机制时的一种说法,它执行的顺序是由外向内的,就是window
一直到子元素。
图解事件冒泡和事件捕获:
12.1 事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡。说白了就是:当父元素和子元素都设置了点击事件的时候,触发子盒子点击事件的时候,父盒子的点击事件也会被执行。
示例代码: [ 53-事件冒泡.html ]
效果图:
我们会发现,当点击中间小盒子的时候,他的父级元素,只要有点击事件的,都被触发了,这就是事件冒泡。
12.2 阻止事件冒泡
正常情况下,我们肯定不想,点击子元素触发事件的时候,父元素事件也跟着触发,所以我们就要知道一个知识点:阻止事件冒泡。在阻止事件冒泡中是存在兼容性的:
正常浏览器:
前面我们知道了事件触发的时候,会有一个事件对象,我们只要给事件对象加上:stopPropagation
方法即可。stopPropagation
方法不仅可以阻止事件冒泡,还可以阻止事件委托
element.onclick = function (e) {
e = event || window.event;
//stop :停止 propagation:传播
e.stopPropagation();
}
IE678浏览器:
ie是给事件对象的属性cancelBubble
赋值
element.onclick = function (e) {
e = event || window.event;
e.cancelBubble = true;
}
兼容性处理:
// 能力检测
element.onclick = function (e) {
e = event || window.event;
if(e.stopPropagation){
e.stopPropagation();
}else {
e.cancelBubble = true;
}
}
12.3 事件捕获
事件捕获(capture
)是火狐浏览器提出来的,IE678
不支持事件捕获(基本上,我们都是用事件冒泡)。事件的处理将从DOM
层次的根开始,而不是从触发事件的目标元素开始,事件被从目标元素的所有祖先元素依次往下传递。
当addEventListener
第三个参数为true时,表示事件捕获:
element.addEventListener("click", function () {
console.log("哈哈哈");
},true);
12.4 事件流
事件流就是事件的三个阶段,首先发生的是捕获阶段,然后是目标阶段,最后才是冒泡阶段,对于捕获和冒泡,我们只能干预其中的一个,通常来说,我们可能会干预事件冒泡阶段,而不去干预事件捕获阶段。
- 事件的捕获阶段
- 事件的目标阶段
- 事件的冒泡阶段
注意:
其实这三个阶段在执行是都会发生,但是冒泡和捕获只能执行一个,所以通过usecaptrue = false
可以让捕获阶段执行但是不触发。
12.5 键盘事件
对于鼠标事件,事件对象中有一系列的XY
记录了鼠标的位置信息。而键盘事件中,事件对象有一个event.keyCode
属性,记录了按下去的键的键盘码。 [ 54-键盘事件-键盘码.html ]
document.onkeydown = function (e) {
// 键盘按下的时候触发的事件对象
console.log(e);
// keyCode: 键盘码
console.log(e.keyCode);
}
键盘码对应值:
常见的键盘事件:
-
onkeydown
:键盘按下时触发 -
onkeyup
:键盘弹起时触发
示例代码: [ 55-键盘事件-ESC键关闭遮罩层弹出框.html ]
// 点击登陆按钮
var btn = document.getElementById('btn');
// 登陆框
var login = document.getElementById('login');
// 遮罩层
var bg = document.getElementById('bg');
btn.addEventListener("click", function() {
login.style.display = "block";
bg.style.display = "block";
});
document.addEventListener("keyup", function(e) {
e = e || window.event;
// ESC 键的键盘码是27
if (e.keyCode == 27) {
login.style.display = "none";
bg.style.display = "none";
}
});
效果图:
12.6 案例:弹幕效果
我们都看过直播,都知道弹幕的效果,下面我们就模拟直播中的弹幕做个小案例。
实现步骤:
- 获取输入框的的
value
值;并生成span
标签 - 将
span
标签添加到 页面中,随机颜色 随机高度span
动画从右向左 - 到达最左边的时候删除
span
标签(不删除会随着输入的内容越来越多影响性能)
示例代码:
吐槽
效果图:
13. 瀑布流
瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。
1、首先瀑布流所有的图片应该保持宽度一致,高度是由内容决定。
左浮动的话,我们可以看到第6
个盒子直接就在第4
个盒子旁边停下了,因为第4
个高度最高,挡住了它左浮动的去路。第6
个盒子是第2
行的最后一个,所以第7
个盒子只能在第3
行排列了。当排到第12
个盒子的时候,盒子会以第11
个盒子的位置为基础左浮动(这就是第12
个盒子为什么没有‘跳到’第9
个盒子下面的原因),碰到第8
个盒子后又被挡住了。
通过定位的方式是我们实现瀑布流的最基本的原理,只要我们动态的设置它的top
值、left
值,就能让它排列。
2、定位后确定浏览器显示区域内,一行能放多少列图片盒子。
- 获取页面的宽度
- 获取图片盒子的宽度
- 显示的列数 = 页面宽度/图片盒子宽度
column = pageWidth / itemWidth
3、为了美观我们可以加上一个空隙
- 显示的列数 = 页面宽度/(图片盒子宽度+间隙);
column = pageWidth / (itemWidth + gap);
4、 确定列数之后,排列第一行
- 下面还有很多图片盒子,我们先要排列第
1
行,所以在for
循环里就要判断一下,当i
(所有图片盒子的索引) <column
(显示列数)的时候,说明在第1
行; - 知道在第
1
行之后,动态设置每个图片盒子的left
值就能排好第1
行。 left = i * ( itemWidth + gap );
5、第1行排列好之后,获取第1行所有图片盒子的高度
- 需要定义一个数组
arr
,将获取到的高度存在数组中,因为第2
行排列的时候需要考虑top
值,此时只能根据第1
行图片盒子的高度来设置; - 获取图片高度的时候要注意,程序必须写在入口函数
onload
里面,因为图片的加载特性是:等页面都加载完之后才去请求加载,所以不写在入口函数里可能会出现高度获取不到的情况。
6、排列第2行
- 获取到刚刚数组中,高度最小的那一列,将第
2
行的第1
个图片盒子放置在它的下方; - 此时的
left
值就是高度最小列的offsetLeft
;top
值就是:第1
行高度最小列的高度(为了布局美观可以加上上下间隙gap
)。 - 记录下高度最小列的索引
index
,后面计算会用到; - 设置完成之后,会发现后面所有的图片都叠在这个高度最小列的下面,原因就是此时的最小列高度没有改变,应该加上下面图片的高度,得出一个新高度。
7、改变最小列当前高度
- 此时的这一列高度其实已经发生改变了,所以需要将新高度赋值给数组
- 当前高度最小列的高度 = 当前高度最小列的高度 + 间隙 + 下面图片盒子的高度
8、触发resize事件
- 将整个设置样式的部分封装成一个函数,在
onload
里面注册一个resize
事件,只要页面一发生改变,就触发样式部分的代码。 - 实时改变
pageWidth
的宽度,这样瀑布流就会是一个响应式的效果了
9、懒加载效果
- 目前我们用的是
30
张图片,假如一个页面中有几百张图片的时候,我们不可能等到它都加载完再显示,所有这里引入一个懒加载的概念,我们规定第30
张为显示的最后一张图片,当滚动条滚动到30
张的时候,应该加载下一批图片。 - 即页面可视区高度+滚动条卷去的高度 = 第
30
图片的offsetTop
;的时候加载下面的图片。
完整代码:
.
.
.
效果图:
14. 正则表达式
正则表达式:用于匹配规律规则的表达式,正则表达式最初是科学家对人类神经系统的工作原理的早起研究,现在在编程语言中有广泛的应用,经常用于表单校验,高级搜索等。
14.1 创建正则表达式
js中的正则表达式用RegExp对象表示,可以通过RegExp()构造函数来创建RegExp对象,不过更多的是通过字面量语法来创建。
/.../
正则表达式必须要有斜杠,它表示的是正则构成
构造函数的方式:
var regExp = new RegExp(/abc/); // 判断是否包含字符abc
正则字面量:/.../
var regExp = /abc/;
正则的使用:
正则表达式有一个方法:test();
有一个返回值,是布尔类型。决定参数是否符合正则表达式
console.log(/abc/.test("abc")); // true
示例代码: [ 57-正则表达式-创建正则表达式.html ]
var reg = new RegExp(/abc/);
console.log(reg.test("abc")); // ture
console.log(reg.test("efg")); // false
console.log(reg.test("abcd")); // true 只要包含abc就正确 后面会细讲
console.log(/123/.test(123)); // true
14.2 元字符
正则表达式由一些普通字符
和元字符
组成,普通字符包括大小写字母、数字等,而元字符则具有特殊的意义。元字符:^
...
14.3 正则内部类
1、预定义类
正则表达式中具有特殊意义的字符。
预定义类 | 正则形式 | 释义 |
---|---|---|
. |
[^\n\r] |
除了换行和回车之外的任意字符 |
\d |
[0-9] |
数字字符 |
\D |
[^0-9] |
非数字字符 |
\w |
[a-zA-Z0-9_] |
单词字符(所有的字母数字和'_') |
\W |
[^a-zA-Z0-9_] |
非单词字符 |
\s |
[\f\r\n\t\v] |
不可见字符,包含空格 |
\S |
[^\f\r\n\t\v] |
可见字符 |
示例代码: [ 59-正则表达式-预定义类.html ]
console.log("----------------'.'---------------");
console.log(/./.test('\n')); // false
console.log(/./.test('2s#2')) // true
console.log("----------------'\\d'---------------");
console.log(/\d/.test(123)); // true
console.log(/\d/.test('123abc')); // true
console.log(/\d/.test('abc')); // false
console.log("----------------'\\D'---------------");
console.log(/\D/.test(123)); // false
console.log(/\D/.test('123abc')); // true
console.log(/\D/.test('abc')); // true
console.log("----------------'\\w'---------------");
console.log(/\w/.test(123)); // true
console.log(/\w/.test('123abc_')); // true
console.log(/\w/.test(' ')); // false
console.log("----------------'\\W'---------------");
console.log(/\W/.test(123)); // false
console.log(/\W/.test('123abc_')); // false
console.log(/\W/.test(' ')); // true
console.log("----------------'\\s'---------------");
console.log(/\s/.test(123)); // false
console.log(/\s/.test('123abc_')); // false
console.log(/\s/.test(' ')); // true
console.log("----------------'\\S'---------------");
console.log(/\S/.test(123)); // true
console.log(/\S/.test('123abc_')); // true
console.log(/\S/.test(' ')); // false
2、简单类 [ 60-正则表达式-简单类.html ]
/ /
中什么特殊符号也不写,就是简单类
直接字符: 必须是完整的包含正则选项,只能多不能少
console.log(/levi/.test('levi')); // true
console.log(/levi/.test('le')); // false
console.log(/levi/.test('levi_lxh')); // true
只要完整的包含了“levi”即可(有他就行)
加上[]
: 只要包含里面任何一个即可 比如/[abcd]/
=> a,b,c,d
只要匹配项的里面有任意一项符合就返回true
console.log(/[levi]/.test("le")); // true
console.log(/[levi]/.test("less")); // true
console.log(/[levi]/.test("kill")); // true
console.log(/[levi]/.test("ss")); // false
3、负向类
元字符^
必须出现在中括号内,表示非、取反的意思[^]
。
示例代码: [ 61-正则表达式-负向类.html ]
console.log(/[^levi]/.test("l")); // false
console.log(/[^levi]/.test("le")); // false
console.log(/[^levi]/.test("ec")); // true
console.log(/[^levi]/.test("levi")); // false
console.log(/[^levi]/.test("levi-lxh")); // true
console.log(/[^levi]/.test("lxh")); // true
条件项[^levi]
,表示不能有l,e,v, i任意组合,当匹配项小于等于条件项并且包含条件项的时候,返回false
,当返回项不完全包含条件项的时候,返回true
4、范围类:
有时候匹配的东西过多,而且类型又相同,全部输入太麻烦,我们可以在中间加个横线 -
。
示例代码: [ 62-正则表达式-范围类.html ]
console.log(/[a-d]/.test("a")); // true
console.log(/[a-d]/.test("ac123")); // true
console.log(/[a-d]/.test("efg")) // false
console.log(/[a-d]/.test("123")) // false
5、组合类
用中括号匹配不同类型的单个字符串
示例代码: [ 63-正则表达式-组合类.html ]
console.log(/[a-f1-6]/.test('abs')); // true
console.log(/[a-f1-6]/.test('12')); // true
console.log(/[a-f1-6]/.test('sg8')); // false
14.4 正则边界
我们前面学习的正则只要有满足的条件的就会返回true,并不能做到精确的匹配。正则边界就是以什么开始,以什么结束,进行精确匹配。
1、以什么开始:
^
元字符在//
里面的时候,表示的是必须以...开始
,^
在中括号[]
内才表示取反、非的意思。
console.log(/^levi/.test('lxhlevi')); // false
console.log(/^levi/.test('levilxh')); // true
console.log(/^levi/.test('lxhlevilxh')); // false
console.log(/^levi/.test('levilevi')); // true
2、以什么结尾:
$ 元字符表示的是必须以...结尾
console.log(/levi$/.test('lxhlevi')); // true
console.log(/levi$/.test('levilxh')); // false
console.log(/levi$/.test('lxhlevilxh')); // false
console.log(/levi$/.test('levilevi')); // true
3、精确匹配:
^...$
表示的是精确匹配,匹配项必须是^、$
之间的内容
console.log(/^levi$/.test('lxhlevi')); // false
console.log(/^levi$/.test('levilxh')); // false
console.log(/^levi$/.test('lxhlevilxh')); // false
console.log(/^levi$/.test('levilevi')); // false
console.log(/^levi$/.test('levi')); // true
console.log(/^\d$/.test('111')); // false \d表示的是0-9当中的一位数
console.log(/^\d$/.test('1')); // true
[ 64-正则表达式-正则边界.html ]
14.5 量词
量词用来控制出现的次数,一般来说量词和边界会一起使用
1、量词 *
:
表示能够出现0
次,或者跟多的次数,x >= 0
。
// 可以出现0次或者多次 要么不出现 要么只能出现 a
console.log(/^a*$/.test('abc')); // false
console.log(/^a*$/.test('bbb')); // false
console.log(/^a*$/.test('aab')); // false
console.log(/^a*$/.test('aaa')); // true
console.log(/^a*$/.test('a')); // true
console.log(/^a*$/.test('')); // true
2、量词 +
:
表示能够出现1
次或者多次,x >= 1
。
// +表示 可以出现1次或者1次以上
console.log(/^a+$/.test("a")); //true
console.log(/^a+$/.test("")); //false
console.log(/^a+$/.test("b")); //false
console.log(/^a+$/.test("aa")); //true
console.log(/^a+$/.test("aab")); //false
3、量词 ?
:
表示能够出现0
次或者1
次,x=0
或者x=1
。
// ? 表示可以出现0次或者1次
console.log(/^a?$/.test("a")); //true
console.log(/^a?$/.test("")); //true
console.log(/^a?$/.test("b")); //false
console.log(/^a?$/.test("aa")); //false
console.log(/^a?$/.test("aab")); //false
4、量词 {n}
:
表示能够出现 n
次
// * ==> {0,}
console.log(/^a{0,}$/.test('a')); // true
console.log(/^a{0,}$/.test('aa')); // true
console.log(/^a{0,}$/.test('')); // true
console.log(/^a{0,}$/.test('abc')); // fasle
console.log(/^a{0,}$/.test('aaab')); // fasle
5、量词 {n,}
:
表示能够出现n
次或者n
次以上
// + ==> {1,}
console.log(/^a{1,}$/.test('a')); // true
console.log(/^a{1,}$/.test('aa')); // true
console.log(/^a{1,}$/.test('')); // fasle
console.log(/^a{1,}$/.test('abc')); // fasle
console.log(/^a{1,}$/.test('aaab')); // fasle
6、量词 {n,m}
:
表示能够出现n-m次
// ? ==> {0,1}
console.log(/^a{0,1}$/.test("a")); //true
console.log(/^a{0,1}$/.test("")); //true
console.log(/^a{0,1}$/.test("b")); //false
console.log(/^a{0,1}$/.test("aa")); //false
console.log(/^a{0,1}$/.test("aab")); //false
[ 65-正则表达式-量词.html ]
14.6 括号总结
1、{}
大括号限定出现的次数
// 表示的是 n 重复两次
console.log(/chuan{2}/.test("chuanchuan")); // false
console.log(/chuan{2}/.test("chuann")); // true
console.log(/chuan{2}/.test("chuann123123")); // true
2、[]
表示一个字符出现的位置
console.log(/^[fb]oot$/.test("foot")); // true
console.log(/^[fb]oot$/.test("boot")); // true
3、()
用来提升优先级
console.log(/^(chuan){2}$/.test("chuanchuan")); // true
14.7 正则表达式综合案例
1、验证座机号码: [ 67-正则案例-验证座机号码.html ]
- 直辖市座机号码:
021-88888888
; - 普通地区做急哦号码:
0515-12345678
; - 前区可以是三位也可以是四位,第一位必须是
0
,后区必须是8
位;
var reg = /^0\d{2,3}-\d{8}$/;
console.log(reg.test('021-12345678')); // true
console.log(reg.test('0515-88888888')); // true
console.log(reg.test('0515-888880888')); // false
2、验证中文姓名 [ 68-正则案例-验证中文姓名.html ]
- 只能是汉字
- 长度
2-6
位之间 - 汉字范围
[\u4e00-\u9fa5]
-
unicode
编码:万国码,其中\u4e00-\u9fa5
表示的就是包含所有汉字的unicode
编码
var nameReg = /^[\u4e00-\u9fa5]{2,6}$/;
console.log(nameReg.test('莫')); // false
console.log(nameReg.test('小泽玛利亚')); // true
console.log(nameReg.test('柯南')); // true
3、验证邮箱 [ 69-正则案例-验证邮箱.html ]
- 前面是字母或者数字
- 必须有
@
-
@
后面是字母或者数字 - 必须有
.
-
.
后面是字母或者数字
var mailBoxReg = /^\w+@\w+(\.\w+)+$/;
console.log(mailBoxReg.test('[email protected]')); // true
4、验证手机号码 [ 70-正则案例-验证手机号码.html ]
-
11
位数字组成 - 号段
13[0-9]
147
15[0-9]
177[0178]
18[0-9]
var mobileReg = /^(13[0-9]|147|15[0-9]|17[0178]|18[0-9])\d{8}$/;
console.log(mobileReg.test(15812345678)); // true
5、验证QQ [ 71-正则案例-验证qq.html ]
- 只能是数字
- 开头不能是
0
- 长度为
5-11
位
var qqReg = /^[1-9]\d{4,10}$/;
console.log(qqReg.test(18888888)); // true
6、完整版表单验证 [ 72-正则案例-表单验证综合案例.html ]
效果图:
14.8 正则补充知识点
这里要补充几个正则的小知识点,比如在正则里‘|
’,表示的是或。‘g
’,表示的是global
:全局,全部。‘i
’,表示的是ignore
:忽视大小写。
1、或‘|’ :
var mobileReg = /^(13[0-9]|14[57]|15[0-25-9]|17[0137]|18[0-9])\d{8}$/;
我们可以看到在判断手机号码前三位的时候,我们就用到了或:“|”
2、全部‘g
’:
var str = '123123123123';
// 找到所有的1 替换成3
var newStr = str.replace(/1/g, 3);
console.log(newStr); // 323323323
3、忽略大小写‘i
’:
var str2 = 'abcdAAAA';
var newStr2 = str2.replace(/a/gi, 'e');
console.log(newStr2); // ebcdeeee
上一篇:JavaScript 进阶知识 - 特效篇(一)
下一篇:JavaScript 进阶知识 - Ajax篇