JavaScript 进阶知识 - 特效篇(二)

9. 三大系列

本篇一开始我们已经学了三大系列中的 offset系列,三大系列分别是 offset系列、 scroll系列、 client系列。学习这些有什么用呢?在后面的特效案例中,会大量的使用到获取元素的宽度、获取元素内部的宽度、获取元素距离顶部的距离等。这时候就需要用到三大系列,下面为大家一一讲解三大系列的用法。

9.1 offset 系列

第一章已经讲过了,详见第一章。

9.2 scroll 系列

scroll 是用来获取盒子内容的大小和位置。 scroll家族有: scrollWidthscrollHeightscrollLeftscrollTop

1、onscroll 事件

前面DOM的时候,我们知道了触发事件,这里讲下onscroll事件。

对于有滚动条的盒子,可以使用onscroll注册滚动事件,每滚动一像素,就会触发该事件。

示例代码: [31-scroll系列-onscroll事件.html]





我是内容我是内容我是内容我是内容我是内容我是内容我是内容 ... ... ... 我是内容我是内容我是内容我是内容我是内容我是内容我是内容

效果图:

2、scrollWidth 和 scrollHeight

scrollWidthscrollHeight是盒子内容的真实的宽度和高度。与和盒子大小无关,仅仅与盒子的内容有关系,不包括 bordermargin,包括 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系列用的最多的地方就是用来获取页面被卷去的宽度和高度,非常的常用。

scrollTopscrollLeft存在兼容性

示例代码: [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,和html100%,将背景的索引与左边导航栏绑定
  • 创建一个缓动动画框架,目标距离target,就是当前索引背景距离顶部的距离,leader就是滚动条此时的位置

  • 鞋子区域
  • 袜子区域
  • 裤子区域
  • 裙子区域
  • 帽子区域
  1. 鞋子
  2. 袜子
  3. 裤子
  4. 裙子
  5. 帽子

效果图:

9.3 client 系列

client家族用于获取盒子可视区的大小。 client家族有 clientWidthclientHeightclientLeftclientTop

1、clientWidth 和 clientHeight

  • clientWidth :获取网页可视区域宽度 ;clientHeight :获取网页可视区域高度;
  • 调用者不同,意义不同:

    • 盒子调用,指盒子本身;
    • html/body调用:可视区域大小
  • 不包括bordermargin

图解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.widthwindow.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;
  • offsetWidth/offsetHeight

    • offsetWidth = width + padding + border;
    • offsetHeight = heigth + padding + border;
  • scrollWidth/scrollHeight

    • scrollWidth = 内容宽度(不包含border);
    • scrollHeight = 内容高度(不包含border);

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对象我们可以看到如下信息:

我们可以看到一个鼠标按下的时候,它的事件对象里面有这么多属性,但是最常用的也就是鼠标位置信息和键盘码相关的信息。

记录了鼠标位置信息的相关属性:

  • screenXscreenY:光标相对于屏幕左上角的水平位置与垂直位置。
  • clientXclientY:光标相对于可视区左上角的水平位置和垂直位置。
  • pageXpageY:光标相对于网页(文档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的兼容性

在鼠标事件中,记录鼠标位置信息的属性有很多,使用最多的还是 pageXpageY这两个属性,但是 pageXpageY存在浏览器兼容性问题。

在现代浏览器中: 直接通过事件对象就可以获得pageXpageY

document.onclick = function (event) {
    event = event || window.event;
    console.log(event.pageX+","+event.pageY);
}

在IE678中: 并没有pageXpageY,但是我们可以通过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/YscreenX/YpageX/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 的时候,显示 maskbigBox
  • 当鼠标离开 smallBox 的时候,隐藏 maskbigBox
  • 获取鼠标在 smallBox 里面的位置;
  • 获得鼠标在 smallBox 里面的位置后,要减去 mask 一半的宽高,否则鼠标不在 mask 中间显示;
  • 判断x的值限定 mask 的位置 :

    • mask 在小盒子里面能够移动最大的宽度和高度 0
    • mask 在小盒子里面能够移动最大的宽度 = smallBox的宽度 - mask的宽度
  • 设定 mask 的位置;
  • 让大图片等比例的跟着动 :

    • bigImg 能够移动的距离 / mask 能移动的距离 = 大图片移动的距离 / mask移动的距离

思路图解:

示例代码: [ 48-事件对象-放大镜效果.html ]





效果图:

11. 注册事件

前面我们已经知道了许多触发事件的名称,但是我们只知道了一种注册事件的方式,就是" on + 事件名称",下面会为大家再介绍一种注册事件的方式: addEventListener

11.1 on + 事件名称 方式

onclickonmouseover这种 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不支持 addEventListenerremoveEventListen两个方法,但是支持 attachEventdetachEvnet

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值就是高度最小列的offsetLefttop值就是:第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篇

你可能感兴趣的:(javascript,ViewUI)