firemail
标题: 02号课程【Javascript第一季初级视频教程】 (必修) [打印本页]
作者: jimu 时间: 2015-12-20 19:35
标题: 02号课程【Javascript第一季初级视频教程】 (必修)
本帖最后由 jimu 于 2015-12-26 20:40 编辑
说明:
分享周期:计划每月会有一些新的东西加入,忘大家抓紧时间学习
分享内容组织:视频会包含单个的文件,方便直接在线观看,也可能【根据文件问题大小,太大就不提供了】会包含一个含有所有资源的压缩包文件,方便一次性下载后离线观看。
分享方式:百度网盘、
视频分享------ 02
Javascript第一季初级视频教程【李炎恢老师】
链接:http://pan.baidu.com/s/1qWZQcSc 密码:r4j4
作者: jimu 时间: 2016-4-14 23:01
第10章 正则表达式
学习要点:
1.什么是正则表达式
2.创建正则表达式
3.获取控制
4.常用的正则
假设用户需要在HTML表单中填写姓名、地址、出生日期等。那么在将表单提交到服务器进一步处理前,JavaScript程序会检查表单以确认用户确实输入了信息并且这些信息是符合要求的。
一.什么是正则表达式
正则表达式(regular expression)是一个描述字符模式的对象。ECMAScript的RegExp类表示正则表达式,而String和RegExp都定义了使用正则表达式进行强大的模式匹配和文本检索与替换的函数。
正则表达式主要用来验证客户端的输入数据。用户填写完表单单击按钮之后,表单就会被发送到服务器,在服务器端通常会用PHP、ASP.NET等服务器脚本对其进行进一步处理。因为客户端验证,可以节约大量的服务器端的系统资源,并且提供更好的用户体验。
二.创建正则表达式
创建正则表达式和创建字符串类似,创建正则表达式提供了两种方法,一种是采用new运算符,另一个是采用字面量方式。
1.两种创建方式
var box = new RegExp('box'); //第一个参数字符串
var box = new RegExp('box', 'ig'); //第二个参数可选模式修饰符
模式修饰符的可选参数
var box = /box/; //直接用两个反斜杠
var box = /box/ig; //在第二个斜杠后面加上模式修饰符
2.测试正则表达式
RegExp对象包含两个方法:test()和exec(),功能基本相似,用于测试字符串匹配。test()方法在字符串中查找是否存在指定的正则表达式并返回布尔值,如果存在则返回true,不存在则返回false。exec()方法也用于在字符串中查找指定正则表达式,如果exec()方法执行成功,则返回包含该查找字符串的相关信息数组。如果执行失败,则返回null。
RegExp对象的方法
/*使用new运算符的test方法示例*/
var pattern = new RegExp('box', 'i'); //创建正则模式,不区分大小写
var str = 'This is a Box!'; //创建要比对的字符串
alert(pattern.test(str)); //通过test()方法验证是否匹配
/*使用字面量方式的test方法示例*/
var pattern = /box/i; //创建正则模式,不区分大小写
var str = 'This is a Box!';
alert(pattern.test(str));
/*使用一条语句实现正则匹配*/
alert(/box/i.test('This is a Box!')); //模式和字符串替换掉了两个变量
/*使用exec返回匹配数组*/
var pattern = /box/i;
var str = 'This is a Box!';
alert(pattern.exec(str)); //匹配了返回数组,否则返回null
PS:exec方法还有其他具体应用,我们在获取控制学完后再看。
3.使用字符串的正则表达式方法
除了test()和exec()方法,String对象也提供了4个使用正则表达式的方法。
String对象中的正则表达式方法
方 法 | |
| |
replace(pattern, replacement) | |
| |
| |
/*使用match方法获取获取匹配数组*/
var pattern = /box/ig; //全局搜索
var str = 'This is a Box!,That is a Box too';
alert(str.match(pattern)); //匹配到两个Box,Box
alert(str.match(pattern).length); //获取数组的长度
/*使用search来查找匹配数据*/
var pattern = /box/ig;
var str = 'This is a Box!,That is a Box too';
alert(str.search(pattern)); //查找到返回位置,否则返回-1
PS:因为search方法查找到即返回,也就是说无需g全局
/*使用replace替换匹配到的数据*/
var pattern = /box/ig;
var str = 'This is a Box!,That is a Box too';
alert(str.replace(pattern, 'Tom')); //将Box替换成了Tom
/*使用split拆分成字符串数组*/
var pattern = / /ig;
var str = 'This is a Box!,That is a Box too';
alert(str.split(pattern)); //将空格拆开分组成数组
RegExp对象的静态属性
/*使用静态属性*/
var pattern = /(g)oogle/;
var str = 'This is google!';
pattern.test(str); //执行一下
alert(RegExp.input); //Thisis google!
alert(RegExp.leftContext); //This is
alert(RegExp.rightContext); //!
alert(RegExp.lastMatch); //google
alert(RegExp.lastParen); //g
alert(RegExp.multiline); //false
PS:Opera不支持input、lastMatch、lastParen和multiline属性。IE不支持multiline属性。
所有的属性可以使用短名来操作
RegExp.input可以改写成RegExp['$_'],依次类推。但RegExp.input比较特殊,它还可以写成RegExp.$_。
RegExp对象的实例属性
/*使用实例属性*/
var pattern = /google/ig;
alert(pattern.global); //true,是否全局了
alert(pattern.ignoreCase); //true,是否忽略大小写
alert(pattern.multiline); //false,是否支持换行
alert(pattern.lastIndex); //0,下次的匹配位置
alert(pattern.source); //google,正则表达式的源字符串
var pattern = /google/g;
var str = 'google google google';
pattern.test(str); //google,匹配第一次
alert(pattern.lastIndex); //6,第二次匹配的位
PS:以上基本没什么用。并且lastIndex在获取下次匹配位置上IE和其他浏览器有偏差,主要表现在非全局匹配上。lastIndex还支持手动设置,直接赋值操作。
三.获取控制
正则表达式元字符是包含特殊含义的字符。它们有一些特殊功能,可以控制匹配模式的方式。反斜杠后的元字符将失去其特殊含义。
字符类:单个字符和数字
字符类:空白字符
字符类:锚字符
字符类:重复字符
字符类:替代字符
字符类:记录字符
/*使用点元字符*/
var pattern = /g..gle/; //.匹配一个任意字符
var str = 'google';
alert(pattern.test(str));
/*重复匹配*/
var pattern = /g.*gle/; //.匹配0个一个或多个
var str = 'google'; //*,?,+,{n,m}
alert(pattern.test(str));
/*使用字符类匹配*/
var pattern = /g[a-zA-Z_]*gle/; //[a-z]*表示任意个a-z中的字符
var str = 'google';
alert(pattern.test(str));
var pattern = /g[^0-9]*gle/; //[^0-9]*表示任意个非0-9的字符
var str = 'google';
alert(pattern.test(str));
var pattern = /[a-z][A-Z]+/; //[A-Z]+表示A-Z一次或多次
var str = 'gOOGLE';
alert(pattern.test(str));
/*使用元符号匹配*/
var pattern = /g\w*gle/; //\w*匹配任意多个所有字母数字_
var str = 'google';
alert(pattern.test(str));
var pattern = /google\d*/; //\d*匹配任意多个数字
var str = 'google444';
alert(pattern.test(str));
var pattern = /\D{7,}/; //\D{7,}匹配至少7个非数字
var str = 'google8';
alert(pattern.test(str));
/*使用锚元字符匹配*/
var pattern = /^google$/; //^从开头匹配,$从结尾开始匹配
var str = 'google';
alert(pattern.test(str));
var pattern =/goo\sgle/; //\s可以匹配到空格
var str = 'google';
alert(pattern.test(str));
var pattern =/google\b/; //\b可以匹配是否到了边界
var str ='google';
alert(pattern.test(str));
/*使用或模式匹配*/
var pattern = /google|baidu|bing/; //匹配三种其中一种字符串
var str = 'google';
alert(pattern.test(str));
/*使用分组模式匹配*/
var pattern = /(google){4,8}/; //匹配分组里的字符串4-8次
var str = 'googlegoogle';
alert(pattern.test(str));
var pattern = /8(.*)8/; //获取8..8之间的任意字符
var str = 'This is 8google8';
str.match(pattern);
alert(RegExp.$1); //得到第一个分组里的字符串内容
var pattern = /8(.*)8/;
var str = 'This is 8google8';
var result =str.replace(pattern,'<strong>$1</strong>'); //得到替换的字符串输出
document.write(result);
var pattern = /(.*)\s(.*)/;
var str = 'google baidu';
var result = str.replace(pattern, '$2 $1'); //将两个分组的值替换输出
document.write(result);
/*关于贪婪和惰性*/
var pattern = /[a-z]+?/; //?号关闭了贪婪匹配,只替换了第一个
var str = 'abcdefjhijklmnopqrstuvwxyz';
var result = str.replace(pattern, 'xxx');
alert(result);
var pattern = /8(.+?)8/g; //禁止了贪婪,开启的全局
var str = 'This is 8google8, That is 8google8,There is 8google8';
var result =str.replace(pattern,'<strong>$1</strong>');
document.write(result);
var pattern = /8([^8]*)8/g; //另一种禁止贪婪
var str = 'This is 8google8, That is 8google8,There is 8google8';
var result =str.replace(pattern,'<strong>$1</strong>');
document.write(result);
/*使用exec返回数组*/
var pattern = /^[a-z]+\s[0-9]{4}$/i;
var str = 'google 2012';
alert(pattern.exec(str)); //返回整个字符串
var pattern = /^[a-z]+/i; //只匹配字母
var str = 'google 2012';
alert(pattern.exec(str)); //返回google
var pattern = /^([a-z]+)\s([0-9]{4})$/i; //使用分组
var str = 'google 2012';
alert(pattern.exec(str)[0]); //google2012
alert(pattern.exec(str)[1]); //google
alert(pattern.exec(str)[2]); //2012
/*捕获性分组和非捕获性分组*/
var pattern = /(\d+)([a-z])/; //捕获性分组
var str = '123abc';
alert(pattern.exec(str));
var pattern = /(\d+)(?:[a-z])/; //非捕获性分组
var str = '123abc';
alert(pattern.exec(str));
/*使用分组嵌套*/
var pattern = /(A?(B?(C?)))/; //从外往内获取
var str = 'ABC';
alert(pattern.exec(str));
/*使用前瞻捕获*/
var pattern = /(goo(?=gle))/; //goo后面必须跟着gle才能捕获
var str = 'google';
alert(pattern.exec(str));
/*使用特殊字符匹配*/
var pattern = /\.\[\/b\]/; //特殊字符,用\符号转义即可
var str = '.';
alert(pattern.test(str));
/*使用换行模式*/
var pattern = /^\d+/mg; //启用了换行模式
var str = '1.baidu\n2.google\n3.bing';
var result = str.replace(pattern, '#');
alert(result);
四.常用的正则
1.检查邮政编码
var pattern = /[1-9][0-9]{5}/; //共6位数字,第一位不能为0
var str = '224000';
alert(pattern.test(str));
2.检查文件压缩包
var pattern = /[\w]+\.zip|rar|gz/; //\w表示所有数字和字母加下划线
var str = '123.zip'; //\.表示匹配.,后面是一个选择
alert(pattern.test(str));
3.删除多余空格
var pattern = /\s/g; //g必须全局,才能全部匹配
var str = '111 222 333';
var result = str.replace(pattern,''); //把空格匹配成无空格
alert(result);
4.删除首尾空格
var pattern = /^\s+/; //强制首
var str = ' goo gle ';
var result = str.replace(pattern, '');
pattern = /\s+$/; //强制尾
result = result.replace(pattern, '');
alert('|' + result + '|');
var pattern = /^\s*(.+?)\s*$/; //使用了非贪婪捕获
var str = ' google ';
alert('|' + pattern.exec(str)[1] + '|');
var pattern = /^\s*(.+?)\s*$/;
var str = ' google ';
alert('|' + str.replace(pattern, '$1') + '|'); //使用了分组获取
5.简单的电子邮件验证
var pattern = /^([a-zA-Z0-9_\.\-]+)@([a-zA-Z0-9_\.\-]+)\.([a-zA-Z]{2,4})$/;
var str = 'yc60.com@gmail.com';
alert(pattern.test(str));
var pattern =/^([\w\.\-]+)@([\w\.\-]+)\.([\w]{2,4})$/;
var str = 'yc60.com@gmail.com';
alert(pattern.test(str));
PS:以上是简单电子邮件验证,复杂的要比这个复杂很多,大家可以搜一下。
作者: jimu 时间: 2016-4-14 23:04
第16章 匿名函数和闭包
学习要点:
1.匿名函数
2.闭包
匿名函数就是没有名字的函数,闭包是可访问一个函数作用域里变量的函数。声明:本节内容需要有面向对象和少量设计模式基础,否则无法听懂.(所需基础15章的时候已经声明过了)。
一.匿名函数
//普通函数
function box() { //函数名是box
return'Lee';
}
//匿名函数
function () { //匿名函数,会报错
return'Lee';
}
//通过表达式自我执行
(function box() { //封装成表达式
alert('Lee');
})(); //()表示执行函数,并且传参
//把匿名函数赋值给变量
var box = function () { //将匿名函数赋给变量
return'Lee';
};
alert(box()); //调用方式和函数调用相似
//函数里的匿名函数
function box () {
returnfunction () { //函数里的匿名函数,产生闭包
return 'Lee';
}
}
alert(box()()); //调用匿名函数
二.闭包
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见的方式,就是在一个函数内部创建另一个函数,通过另一个函数访问这个函数的局部变量。
//通过闭包可以返回局部变量
function box() {
varuser = 'Lee';
returnfunction () { //通过匿名函数返回box()局部变量
return user;
};
}
alert(box()()); //通过box()()来直接调用匿名函数返回值
var b = box();
alert(b()); //另一种调用匿名函数返回值
使用闭包有一个优点,也是它的缺点:就是可以把局部变量驻留在内存中,可以避免使用全局变量。(全局变量污染导致应用程序不可预测性,每个模块都可调用必将引来灾难,所以推荐使用私有的,封装的局部变量)。
//通过全局变量来累加
var age = 100; //全局变量
function box() {
age++; //模块级可以调用全局变量,进行累加
}
box(); //执行函数,累加了
alert(age); //输出全局变量
//通过局部变量无法实现累加
function box() {
varage = 100;
age++; //累加
returnage;
}
alert(box()); //101
alert(box()); //101,无法实现,因为又被初始化了
//通过闭包可以实现局部变量的累加
function box() {
varage = 100;
returnfunction () {
age ++;
return age;
}
}
var b = box(); //获得函数
alert(b()); //调用匿名函数
alert(b()); //第二次调用匿名函数,实现累加
PS:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的时候才使用闭包。
作用域链的机制导致一个问题,在循环中里的匿名函数取得的任何变量都是最后一个值。
//循环里包含匿名函数
function box() {
vararr = [];
for(var i = 0; i < 5; i++) {
arr = function () {
returni;
};
}
returnarr;
}
var b = box(); //得到函数数组
alert(b.length); //得到函数集合长度
for (var i = 0; i < b.length; i++) {
alert(b()); //输出每个函数的值,都是最后一个值
}
上面的例子输出的结果都是5,也就是循环后得到的最大的i值。因为b调用的是匿名函数,匿名函数并没有自我执行,等到调用的时候,box()已执行完毕,i早已变成5,所以最终的结果就是5个5。
//循环里包含匿名函数-改1,自我执行匿名函数
function box() {
vararr = [];
for(var i = 0; i < 5; i++) {
arr = (function (num) { //自我执行
returnnum;
})(i); //并且传参
}
returnarr;
}
var b = box();
for (var i = 0; i < b.length; i++) {
alert(b); //这里返回的是数组,直接打印即可
}
改1中,我们让匿名函数进行自我执行,导致最终返回给a的是数组而不是函数了。最终导致b[0]-b[4]中保留了0,1,2,3,4的值。
//循环里包含匿名函数-改2,匿名函数下再做个匿名函数
function box() {
vararr = [];
for(var i = 0; i < 5; i++) {
arr = (function (num) {
returnfunction () { //直接返回值,改2变成返回函数
return num; //原理和改1一样
}
})(i);
}
returnarr;
}
var b = box();
for (var i = 0; i < b.length; i++) {
alert(b()); //这里通过b()函数调用即可
}
改1和改2中,我们通过匿名函数自我执行,立即把结果赋值给a。每一个i,是调用方通过按值传递的,所以最终返回的都是指定的递增的i。而不是box()函数里的i。
关于this对象
在闭包中使用this对象也可能会导致一些问题,this对象是在运行时基于函数的执行环境绑定的,如果this在全局范围就是window,如果在对象内部就指向这个对象。而闭包却在运行时指向window的,因为闭包并不属于这个对象的属性或方法。
var user = 'The Window';
var obj = {
user: 'The Object',
getUserFunction: function () {
return function () { //闭包不属于obj,里面的this指向window
returnthis.user;
};
}
};
alert(obj.getUserFunction()()); //The window
//可以强制指向某个对象
alert(obj.getUserFunction().call(obj)); //TheObject
//也可以从上一个作用域中得到对象
getUserFunction: function () {
var that = this; //从对象的方法里得对象
return function () {
returnthat.user;
};
}
内存泄漏
由于IE的JScript对象和DOM对象使用不同的垃圾收集方式,因此闭包在IE中会导致一些问题。就是内存泄漏的问题,也就是无法销毁驻留在内存中的元素。以下代码有两个知识点还没有学习到,一个是DOM,一个是事件。
function box() {
varoDiv = document.getElementById('oDiv'); //oDiv用完之后一直驻留在内存
oDiv.onclick= function () {
alert(oDiv.innerHTML); //这里用oDiv导致内存泄漏
};
}
box();
那么在最后应该将oDiv解除引用来避免内存泄漏。
function box() {
varoDiv = document.getElementById('oDiv');
var text =oDiv.innerHTML;
oDiv.onclick= function () {
alert(text);
};
oDiv = null; //解除引用
}
PS:如果并没有使用解除引用,那么需要等到浏览器关闭才得以释放。
模仿块级作用域
JavaScript没有块级作用域的概念。
function box(count) {
for(var i=0; i<count; i++) {}
alert(i); //i不会因为离开了for块就失效
}
box(2);
function box(count) {
for(var i=0; i<count; i++) {}
vari; //就算重新声明,也不会前面的值
alert(i);
}
box(2);
以上两个例子,说明JavaScript没有块级语句的作用域,if () {} for () {}等没有作用域,如果有,出了这个范围i就应该被销毁了。就算重新声明同一个变量也不会改变它的值。
JavaScript不会提醒你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(如果初始化了,当然还会执行的)。使用模仿块级作用域可避免这个问题。
//模仿块级作用域(私有作用域)
(function () {
//这里是块级作用域
})();
//使用块级作用域(私有作用域)改写
function box(count) {
(function() {
for (var i = 0; i<count; i++) {}
})();
alert(i); //报错,无法访问
}
box(2);
使用了块级作用域(私有作用域)后,匿名函数中定义的任何变量,都会在执行结束时被销毁。这种技术经常在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。一般来说,我们都应该尽可能少向全局作用域中添加变量和函数。在大型项目中,多人开发的时候,过多的全局变量和函数很容易导致命名冲突,引起灾难性的后果。如果采用块级作用域(私有作用域),每个开发者既可以使用自己的变量,又不必担心搞乱全局作用域。
(function () {
varbox = [1,2,3,4];
alert(box); //box出来就不认识了
})();
在全局作用域中使用块级作用域可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了。
私有变量
JavaScript没有私有属性的概念;所有的对象属性都是公有的。不过,却有一个私有变量的概念。任何在函数中定义的变量,都可以认为是私有变量,因为不能在函数的外部访问这些变量。
function box() {
varage = 100; //私有变量,外部无法访问
}
而通过函数内部创建一个闭包,那么闭包通过自己的作用域链也可以访问这些变量。而利用这一点,可以创建用于访问私有变量的公有方法。
function Box() {
varage = 100; //私有变量
functionrun() { //私有函数
return '运行中...';
}
this.get= function () { //对外公共的特权方法
return age + run();
};
}
var box = new Box();
alert(box.get());
可以通过构造方法传参来访问私有变量。
function Person(value) {
varuser = value; //这句其实可以省略
this.getUser= function () {
return user;
};
this.setUser= function (value) {
user = value;
};
}
但是对象的方法,在多次调用的时候,会多次创建。可以使用静态私有变量来避免这个问题。
静态私有变量
通过块级作用域(私有作用域)中定义私有变量或函数,同样可以创建对外公共的特权方法。
(function () {
varage = 100;
functionrun() {
return '运行中...';
}
Box= function () {}; //构造方法
Box.prototype.go= function () { //原型方法
return age + run();
};
})();
var box = new Box();
alert(box.go());
上面的对象声明,采用的是Box = function () {} 而不是function Box(){} 因为如果用后面这种,就变成私有函数了,无法在全局访问到了,所以使用了前面这种。
(function () {
varuser = '';
Person= function (value) {
user = value;
};
Person.prototype.getUser = function () {
return user;
};
Person.prototype.setUser = function(value) {
user = value;
}
})();
使用了prototype导致方法共享了,而user也就变成静态属性了。(所谓静态属性,即共享于不同对象中的属性)。
模块模式
之前采用的都是构造函数的方式来创建私有变量和特权方法。那么对象字面量方式就采用模块模式来创建。
var box = { //字面量对象,也是单例对象
age: 100, //这是公有属性,将要改成私有
run: function () { //这时公有函数,将要改成私有
return '运行中...';
};
};
私有化变量和函数:
var box = function () {
varage = 100;
functionrun() {
return '运行中...';
}
return{ //直接返回对象
go : function () {
returnage + run();
}
};
}();
上面的直接返回对象的例子,也可以这么写:
var box = function () {
varage = 100;
functionrun() {
return '运行中...';
}
varobj = { //创建字面量对象
go : function () {
returnage + run();
}
};
returnobj; //返回这个对象
}();
字面量的对象声明,其实在设计模式中可以看作是一种单例模式,所谓单例模式,就是永远保持对象的一个实例。
增强的模块模式,这种模式适合返回自定义对象,也就是构造函数。
function Desk() {};
var box = function () {
varage = 100;
functionrun() {
return '运行中...';
}
vardesk = new Desk(); //可以实例化特定的对象
desk.go= function () {
return age + run();
};
returndesk;
}();
alert(box.go());
作者: jimu 时间: 2016-4-14 23:05
第24章 事件入门
学习要点:
1.事件介绍
2.内联模型
3.脚本模型
4.事件处理函数
JavaScript事件是由访问Web页面的用户引起的一系列操作,例如:用户点击。当用户执行某些操作的时候,再去执行一系列代码。
一.事件介绍
事件一般是用于浏览器和用户操作进行交互。最早是IE和NetscapeNavigator中出现,作为分担服务器端运算负载的一种手段。直到几乎所有的浏览器都支持事件处理。而DOM2级规范开始尝试以一种复合逻辑的方式标准化DOM事件。IE9、Firefox、Opera、Safari和Chrome全都已经实现了“DOM2级事件”模块的核心部分。IE8之前浏览器仍然使用其专有事件模型。
JavaScript有三种事件模型:内联模型、脚本模型和DOM2模型。
二.内联模型
这种模型是最传统接单的一种处理事件的方法。在内联模型中,事件处理函数是HTML标签的一个属性,用于处理指定事件。虽然内联在早期使用较多,但它是和HTML混写的,并没有与HTML分离。
//在HTML中把事件处理函数作为属性执行JS代码
<input type="button" value="按钮"onclick="alert('Lee');" /> //注意单双引号
//在HTML中把事件处理函数作为属性执行JS函数
<input type="button" value="按钮"onclick="box();" /> //执行JS的函数
PS:函数不得放到window.onload里面,这样就看不见了。
三.脚本模型
由于内联模型违反了HTML与JavaScript代码层次分离的原则。为了解决这个问题,我们可以在JavaScript中处理事件。这种处理方式就是脚本模型。
var input =document.getElementsByTagName('input')[0]; //得到input对象
input.onclick = function () { //匿名函数执行
alert('Lee');
};
PS:通过匿名函数,可以直接触发对应的代码。也可以通过指定的函数名赋值的方式来执行函数(赋值的函数名不要跟着括号)。
input.onclick = box; //把函数名赋值给事件处理函数
四.事件处理函数
JavaScript可以处理的事件类型为:鼠标事件、键盘事件、HTML事件。
JavaScript事件处理函数及其使用列表
PS:所有的事件处理函数都会都有两个部分组成,on+ 事件名称,例如click事件的事件处理函数就是:onclick。在这里,我们主要谈论脚本模型的方式来构建事件,违反分离原则的内联模式,我们忽略掉。
对于每一个事件,它都有自己的触发范围和方式,如果超出了触发范围和方式,事件处理将失效。
1.鼠标事件,页面所有元素都可触发
click:当用户单击鼠标按钮或按下回车键时触发。
input.onclick= function () {
alert('Lee');
};
dblclick:当用户双击主鼠标按钮时触发。
input.ondblclick= function () {
alert('Lee');
};
mousedown:当用户按下了鼠标还未弹起时触发。
input.onmousedown= function () {
alert('Lee');
};
mouseup:当用户释放鼠标按钮时触发。
input.onmouseup= function () {
alert('Lee');
};
mouseover:当鼠标移到某个元素上方时触发。
input.onmouseover= function () {
alert('Lee');
};
mouseout:当鼠标移出某个元素上方时触发。
input.onmouseout= function () {
alert('Lee');
};
mousemove:当鼠标指针在元素上移动时触发。
input.onmousemove = function () {
alert('Lee');
};
2.键盘事件
keydown:当用户按下键盘上任意键触发,如果按住不放,会重复触发。
onkeydown =function () {
alert('Lee');
};
keypress:当用户按下键盘上的字符键触发,如果按住不放,会重复触发。
onkeypress =function () {
alert('Lee');
};
keyup:当用户释放键盘上的键触发。
onkeyup =function () {
alert('Lee');
};
3.HTML事件
load:当页面完全加载后在window上面触发,或当框架集加载完毕后在框架集上触发。
window.onload = function () {
alert('Lee');
};
unload:当页面完全卸载后在window上面触发,或当框架集卸载后在框架集上触发。
window.onunload = function () {
alert('Lee');
};
select:当用户选择文本框(input或textarea)中的一个或多个字符触发。
input.onselect= function () {
alert('Lee');
};
change:当文本框(input或textarea)内容改变且失去焦点后触发。
input.onchange= function () {
alert('Lee');
};
focus:当页面或者元素获得焦点时在window及相关元素上面触发。
input.onfocus= function () {
alert('Lee');
};
blur:当页面或元素失去焦点时在window及相关元素上触发。
input.onblur =function () {
alert('Lee');
};
submit:当用户点击提交按钮在<form>元素上触发。
form.onsubmit= function () {
alert('Lee');
};
reset:当用户点击重置按钮在<form>元素上触发。
form.onreset= function () {
alert('Lee');
};
resize:当窗口或框架的大小变化时在window或框架上触发。
window.onresize = function () {
alert('Lee');
};
scroll:当用户滚动带滚动条的元素时触发。
window.onscroll= function () {
alert('Lee');
};
作者: jimu 时间: 2016-4-14 23:06
第25章 事件对象
学习要点:
1.事件对象
2.鼠标事件
3.键盘事件
4.W3C与IE
JavaScript事件的一个重要方面是它们拥有一些相对一致的特点,可以给你的开发提供更多的强大功能。最方便和强大的就是事件对象,他们可以帮你处理鼠标事件和键盘敲击方面的情况,此外还可以修改一般事件的捕获/冒泡流的函数。
一.事件对象
事件处理函数的一个标准特性是,以某些方式访问的事件对象包含有关于当前事件的上下文信息。
事件处理三部分组成:对象.事件处理函数=函数。例如:单击文档任意处。
document.onclick= function () {
alert('Lee');
};
PS:以上程序的名词解释:click表示一个事件类型,单击。onclick表示一个事件处理函数或绑定对象的属性(或者叫事件监听器、侦听器)。document表示一个绑定的对象,用于触发某个元素区域。function()匿名函数是被执行的函数,用于触发后执行。
除了用匿名函数的方法作为被执行的函数,也可以设置成独立的函数。
document.onclick = box; //直接赋值函数名即可,无须括号
function box(){
alert('Lee');
}
this关键字和上下文
在面向对象那章我们了解到:在一个对象里,由于作用域的关系,this代表着离它最近对象。
var input =document.getElementsByTagName('input')[0];
input.onclick= function () {
alert(this.value); //HTMLInputElement,this表示input对象
};
从上面的拆分,我们并没有发现本章的重点:事件对象。那么事件对象是什么?它在哪里呢?当触发某个事件时,会产生一个事件对象,这个对象包含着所有与事件有关的信息。包括导致事件的元素、事件的类型、以及其它与特定事件相关的信息。
事件对象,我们一般称作为event对象,这个对象是浏览器通过函数把这个对象作为参数传递过来的。那么首先,我们就必须验证一下,在执行函数中没有传递参数,是否可以得到隐藏的参数。
function box() { //普通空参函数
alert(arguments.length); //0,没有得到任何传递的参数
}
input.onclick = function () { //事件绑定的执行函数
alert(arguments.length); //1,得到一个隐藏参数
};
通过上面两组函数中,我们发现,通过事件绑定的执行函数是可以得到一个隐藏参数的。说明,浏览器会自动分配一个参数,这个参数其实就是event对象。
input.onclick = function () {
alert(arguments[0]); //MouseEvent,鼠标事件对象
};
上面这种做法比较累,那么比较简单的做法是,直接通过接收参数来得到即可。
input.onclick = function (evt) { //接受event对象,名称不一定非要event
alert(evt); //MouseEvent,鼠标事件对象
};
直接接收event对象,是W3C的做法,IE不支持,IE自己定义了一个event对象,直接在window.event获取即可。
input.onclick = function (evt) {
vare = evt || window.event; //实现跨浏览器兼容获取event对象
alert(e);
};
二.鼠标事件
鼠标事件是Web上面最常用的一类事件,毕竟鼠标还是最主要的定位设备。那么通过事件对象可以获取到鼠标按钮信息和屏幕坐标获取等。
1.鼠标按钮
只有在主鼠标按钮被单击时(常规一般是鼠标左键)才会触发click事件,因此检测按钮的信息并不是必要的。但对于mousedown和mouseup事件来说,则在其event对象存在一个button属性,表示按下或释放按钮。
非IE(W3C)中的button属性
IE中的button属性
PS:在绝大部分情况下,我们最多只使用主次中三个单击键,IE给出的其他组合键一般无法使用上。所以,我们只需要做上这三种兼容即可。
function getButton(evt) { //跨浏览器左中右键单击相应
vare = evt || window.event;
if(evt) { //Chrome浏览器支持W3C和IE
return e.button; //要注意判断顺序
}else if (window.event) {
switch(e.button) {
case1 :
return 0;
case4 :
return 1;
case2 :
return 2;
}
}
}
document.onmouseup= function (evt) { //调用
if (getButton(evt) == 0) {
alert('按下了左键!');
} else if (getButton(evt) == 1) {
alert('按下了中键!');
} else if (getButton(evt) == 2) {
alert('按下了右键!' );
}
};
2.可视区及屏幕坐标
事件对象提供了两组来获取浏览器坐标的属性,一组是页面可视区左边,另一组是屏幕坐标。
坐标属性
document.onclick= function (evt) {
var e = evt || window.event;
alert(e.clientX + ',' + e.clientY);
alert(e.screenX + ',' + e.screenY);
};
3.修改键
有时,我们需要通过键盘上的某些键来配合鼠标来触发一些特殊的事件。这些键为:Shfit、Ctrl、Alt和Meat(Windows中就是Windows键,苹果机中是Cmd键),它们经常被用来修改鼠标事件和行为,所以叫修改键。
修改键属性
function getKey(evt) {
vare = evt || window.event;
varkeys = [];
if(e.shiftKey) keys.push('shift'); //给数组添加元素
if(e.ctrlKey) keys.push('ctrl');
if(e.altKey) keys.push('alt');
return keys;
}
document.onclick= function (evt) {
alert(getKey(evt));
};
三.键盘事件
用户在使用键盘时会触发键盘事件。“DOM2级事件”最初规定了键盘事件,结果又删除了相应的内容。最终还是使用最初的键盘事件,不过IE9已经率先支持“DOM3”级键盘事件。
1.键码
在发生keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符集,keyCode属性的值与ASCII码中对应小写字母或数字的编码相同。字母中大小写不影响。
document.onkeydown = function (evt) {
alert(evt.keyCode); //按任意键,得到相应的keyCode
};
不同的浏览器在keydown和keyup事件中,会有一些特殊的情况:
在Firefox和Opera中,分号键时keyCode值为59,也就是ASCII中分号的编码;而IE和Safari返回186,即键盘中按键的键码。
PS:其他一些特殊情况由于浏览器版本太老和市场份额太低,这里不做补充。
2.字符编码
Firefox、Chrome和Safari的event对象都支持一个charCode属性,这个属性只有在发生keypress事件时才包含值,而且这个值是按下的那个键所代表字符的ASCII编码。此时的keyCode通常等于0或者也可能等于所按键的编码。IE和Opera则是在keyCode中保存字符的ASCII编码。
function getCharCode(evt) {
vare = evt || window.event;
if(typeof e.charCode == 'number') {
return e.charCode;
}else {
return e.keyCode;
}
}
PS:可以使用String.fromCharCode()将ASCII编码转换成实际的字符。
keyCode和charCode区别如下:比如当按下“a键(重视是小写的字母)时,
在Firefox中会获得
keydown: keyCode is65 charCode is 0
keyup: keyCode is 65 charCode is 0
keypress: keyCode is0 charCode is 97
在IE中会获得
keydown: keyCode is65 charCode is undefined
keyup: keyCode is 65 charCode is undefined
keypress: keyCode is97 charCode is undefined
而当按下shift键时,在Firefox中会获得
keydown:keyCode is16 charCode is 0
keyup: keyCode is16 charCode is 0
在IE中会获得
keydown:keyCode is16 charCode is undefined
keyup: keyCode is16 charCode is undefined
keypress:不会获得任何的charCode值,因为按shift并没输入任何的字符,并且也不会触发keypress事务
PS:在keydown事务里面,事务包含了keyCode – 用户按下的按键的物理编码。
在keypress里,keyCode包含了字符编码,即默示字符的ASCII码。如许的情势实用于所有的浏览器 – 除了火狐,它在keypress事务中的keyCode返回值为0。
四.W3C与IE
在标准的DOM事件中,event对象包含与创建它的特定事件有关的属性和方法。触发的事件类型不一样,可用的属性和方法也不一样。
W3C中event对象的属性和方法
属性/方法 | | | |
| | | |
| | | |
| | | |
| | | |
| | | 调用事件处理程序的阶段:1表示捕获阶段,2表示“处理目标”,3表示冒泡阶段 |
| | | 取消事件的默认行为。如果cancelabel是true,则可以使用这个方法 |
| | | 取消事件的进一步捕获或冒泡。如果bubbles为true,则可以使用这个方法 |
| | | |
| | | |
| | | 与事件关联的抽象视图。等同于发生事件的window对象 |
IE中event对象的属性
属性 | | | |
| | | 默认值为false,但将其设置为true就可以取消事件冒泡 |
| | | 默认值为true,但将其设置为false就可以取消事件的默认行为 |
| | | |
| | | |
在这里,我们只看所有浏览器都兼容的属性或方法。首先第一个我们了解一下W3C中的target和IE中的srcElement,都表示事件的目标。
function getTarget(evt) {
vare = evt || window.event;
returne.target || e.srcElement; //兼容得到事件目标DOM对象
}
document.onclick = function (evt) {
vartarget = getTarget(evt);
alert(target);
};
事件流
事件流是描述的从页面接受事件的顺序,当几个都具有事件的元素层叠在一起的时候,那么你点击其中一个元素,并不是只有当前被点击的元素会触发事件,而层叠在你点击范围的所有元素都会触发事件。事件流包括两种模式:冒泡和捕获。
事件冒泡,是从里往外逐个触发。事件捕获,是从外往里逐个触发。那么现代的浏览器默认情况下都是冒泡模型,而捕获模式则是早期的Netscape默认情况。而现在的浏览器要使用DOM2级模型的事件绑定机制才能手动定义事件流模式。
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.gif
document.onclick= function () {
alert('我是document');
};
document.documentElement.onclick= function () {
alert('我是html');
};
document.body.onclick= function () {
alert('我是body');
};
document.getElementById('box').onclick= function () {
alert('我是div');
};
document.getElementsByTagName('input')[0].onclick= function () {
alert('我是input');
};
在阻止冒泡的过程中,W3C和IE采用的不同的方法,那么我们必须做一下兼容。
function stopPro(evt) {
vare = evt || window.event;
window.event? e.cancelBubble = true : e.stopPropagation();
}
作者: jimu 时间: 2016-4-14 23:06
第26章 事件绑定及深入
学习要点:
1.传统事件绑定的问题
2.W3C事件处理函数
3.IE事件处理函数
4.事件对象的其他补充
事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定(DOM2级模型)。现代事件绑定在传统绑定上提供了更强大更方便的功能。
一.传统事件绑定的问题
传统事件绑定有内联模型和脚本模型,内联模型我们不做讨论,基本很少去用。先来看一下脚本模型,脚本模型将一个函数赋值给一个事件处理函数。
var box = document.getElementById('box'); //获取元素
box.onclick = function () { //元素点击触发事件
alert('Lee');
};
问题一:一个事件处理函数触发两次事件
window.onload = function () { //第一组程序项目或第一个JS文件
alert('Lee');
};
window.onload = function () { //第二组程序项目或第二个JS文件
alert('Mr.Lee');
};
当两组程序或两个JS文件同时执行的时候,后面一个会把前面一个完全覆盖掉。导致前面的window.onload完全失效了。
解决覆盖问题,我们可以这样去解决:
window.onload = function () { //第一个要执行的事件,会被覆盖
alert('Lee');
};
if (typeof window.onload == 'function') { //判断之前是否有window.onload
varsaved = null; //创建一个保存器
saved= window.onload; //把之前的window.onload保存起来
}
window.onload = function () { //最终一个要执行事件
if(saved) saved(); //执行之前一个事件
alert('Mr.Lee'); //执行本事件的代码
};
问题二:事件切换器
box.onclick = toBlue; //第一次执行boBlue()
function toRed() {
this.className= 'red';
this.onclick= toBlue; //第三次执行toBlue(),然后来回切换
}
function toBlue() {
this.className= 'blue';
this.onclick= toRed; //第二次执行toRed()
}
这个切换器在扩展的时候,会出现一些问题:
1.如果增加一个执行函数,那么会被覆盖
box.onclick = toAlert; //被增加的函数
box.onclick = toBlue; //toAlert被覆盖了
2.如果解决覆盖问题,就必须包含同时执行,但又出新问题
box.onclick = function () { //包含进去,但可读性降低
toAlert(); //第一次不会被覆盖,但第二次又被覆盖
toBlue.call(this); //还必须把this传递到切换器里
};
综上的三个问题:覆盖问题、可读性问题、this传递问题。我们来创建一个自定义的事件处理函数,来解决以上三个问题。
function addEvent(obj, type, fn) { //取代传统事件处理函数
varsaved = null; //保存每次触发的事件处理函数
if(typeof obj['on' + type] == 'function') { //判断是不是事件
saved = obj['on' + type]; //如果有,保存起来
}
obj['on'+ type] = function () { //然后执行
if (saved) saved(); //执行上一个
fn.call(this); //执行函数,把this传递过去
};
}
addEvent(window, 'load', function () { //执行到了
alert('Lee');
});
addEvent(window, 'load', function () { //执行到了
alert('Mr.Lee');
});
PS:以上编写的自定义事件处理函数,还有一个问题没有处理,就是两个相同函数名的函数误注册了两次或多次,那么应该把多余的屏蔽掉。那,我们就需要把事件处理函数进行遍历,如果有同样名称的函数名就不添加即可。(这里就不做了)
addEvent(window, 'load', init); //注册第一次
addEvent(window, 'load', init); //注册第二次,应该忽略
function init() {
alert('Lee');
}
用自定义事件函数注册到切换器上查看效果:
addEvent(window, 'load', function () {
varbox = document.getElementById('box');
addEvent(box,'click', toBlue);
});
function toRed() {
this.className= 'red';
addEvent(this,'click', toBlue);
}
function toBlue() {
this.className= 'blue';
addEvent(this,'click', toRed);
}
PS:当你单击很多很多次切换后,浏览器直接卡死,或者弹出一个错误:too much recursion(太多的递归)。主要的原因是,每次切换事件的时候,都保存下来,没有把无用的移除,导致越积越多,最后卡死。
function removeEvent(obj, type) {
if(obj['on'] + type) obj['on' + type] = null; //删除事件处理函数
}
以上的删除事件处理函数只不过是一刀切的删除了,这样虽然解决了卡死和太多递归的问题。但其他的事件处理函数也一并被删除了,导致最后得不到自己想要的结果。如果想要只删除指定的函数中的事件处理函数,那就需要遍历,查找。(这里就不做了)
二.W3C事件处理函数
“DOM2级事件”定义了两个方法,用于添加事件和删除事件处理程序的操作:addEventListener()和removeEventListener()。所有DOM节点中都包含这两个方法,并且它们都接受3个参数;事件名、函数、冒泡或捕获的布尔值(true表示捕获,false表示冒泡)。
window.addEventListener('load', function () {
alert('Lee');
}, false);
window.addEventListener('load', function () {
alert('Mr.Lee');
}, false);
PS:W3C的现代事件绑定比我们自定义的好处就是:1.不需要自定义了;2.可以屏蔽相同的函数;3.可以设置冒泡和捕获。
window.addEventListener('load', init, false); //第一次执行了
window.addEventListener('load', init, false); //第二次被屏蔽了
function init() {
alert('Lee');
}
事件切换器
window.addEventListener('load', function () {
varbox = document.getElementById('box');
box.addEventListener('click',function () { //不会被误删
alert('Lee');
}, false);
box.addEventListener('click',toBlue, false); //引入切换也不会太多递归卡死
}, false);
function toRed() {
this.className= 'red';
this.removeEventListener('click',toRed, false);
this.addEventListener('click',toBlue, false);
}
function toBlue() {
this.className= 'blue';
this.removeEventListener('click',toBlue, false);
this.addEventListener('click',toRed, false);
}
设置冒泡和捕获阶段
之前我们上一章了解了事件冒泡,即从里到外触发。我们也可以通过event对象来阻止某一阶段的冒泡。那么W3C现代事件绑定可以设置冒泡和捕获。
document.addEventListener('click', function (){
alert('document');
}, true); //把布尔值设置成true,则为捕获
box.addEventListener('click', function () {
alert('Lee');
}, true); //把布尔值设置成false,则为冒泡
三.IE事件处理函数
IE实现了与DOM中类似的两个方法:attachEvent()和detachEvent()。这两个方法接受相同的参数:事件名称和函数。
在使用这两组函数的时候,先把区别说一下:1.IE不支持捕获,只支持冒泡;2.IE添加事件不能屏蔽重复的函数;3.IE中的this指向的是window而不是DOM对象。4.在传统事件上,IE是无法接受到event对象的,但使用了attchEvent()却可以,但有些区别。
window.attachEvent('onload', function () {
varbox = document.getElementById('box');
box.attachEvent('onclick',toBlue);
});
function toRed() {
varthat = window.event.srcElement;
that.className= 'red';
that.detachEvent('onclick',toRed);
that.attachEvent('onclick',toBlue);
}
function toBlue() {
varthat = window.event.srcElement;
that.className= 'blue';
that.detachEvent('onclick',toBlue);
that.attachEvent('onclick',toRed);
}
PS:IE不支持捕获,无解。IE不能屏蔽,需要单独扩展或者自定义事件处理。IE不能传递this,可以call过去。
window.attachEvent('onload', function () {
varbox = document.getElementById('box');
box.attachEvent('onclick',function () {
alert(this === window); //this指向的window
});
});
window.attachEvent('onload', function () {
varbox = document.getElementById('box');
box.attachEvent('onclick',function () {
toBlue.call(box); //把this直接call过去
});
});
function toThis() {
alert(this.tagName);
}
在传统绑定上,IE是无法像W3C那样通过传参接受event对象,但如果使用了attachEvent()却可以。
box.onclick = function (evt) {
alert(evt); //undefined
}
box.attachEvent('onclick',function (evt) {
alert(evt); //object
alert(evt.type); //click
});
box.attachEvent('onclick', function (evt) {
alert(evt.srcElement=== box); //true
alert(window.event.srcElement === box); //true
});
最后,为了让IE和W3C可以兼容这个事件切换器,我们可以写成如下方式:
function addEvent(obj, type, fn) { //添加事件兼容
if(obj.addEventListener) {
obj.addEventListener(type, fn);
}else if (obj.attachEvent) {
obj.attachEvent('on' + type, fn);
}
}
function removeEvent(obj, type, fn) { //移除事件兼容
if(obj.removeEventListener) {
obj.removeEventListener(type, fn);
}else if (obj.detachEvent) {
obj.detachEvent('on' + type, fn);
}
}
function getTarget(evt) { //得到事件目标
if(evt.target) {
return evt.target;
}else if (window.event.srcElement) {
return window.event.srcElement;
}
}
PS:调用忽略,IE兼容的事件,如果要传递this,改成call即可。
PS:IE中的事件绑定函数attachEvent()和detachEvent()可能在实践中不去使用,有几个原因:1.IE9就将全面支持W3C中的事件绑定函数;2.IE的事件绑定函数无法传递this;3.IE的事件绑定函数不支持捕获;4.同一个函数注册绑定后,没有屏蔽掉;5.有内存泄漏的问题。至于怎么替代,我们将在以后的项目课程中探讨。
四.事件对象的其他补充
在W3C提供了一个属性:relatedTarget;这个属性可以在mouseover和mouseout事件中获取从哪里移入和从哪里移出的DOM对象。
box.onmouseover= function (evt) { //鼠标移入box
alert(evt.relatedTarget); //获取移入box最近的那个元素对象
} //span
box.onmouseout = function (evt) { //鼠标移出box
alert(evt.relatedTarget); //获取移出box最近的那个元素对象
} //span
IE提供了两组分别用于移入移出的属性:fromElement和toElement,分别对应mouseover和mouseout。
box.onmouseover = function (evt) { //鼠标移入box
alert(window.event.fromElement.tagName); //获取移入box最近的那个元素对象span
}
box.onmouseout = function (evt) { //鼠标移入box
alert(window.event.toElement.tagName); //获取移入box最近的那个元素对象span
}
PS:fromElement和toElement如果分别对应相反的鼠标事件,没有任何意义。
剩下要做的就是跨浏览器兼容操作:
function getTarget(evt) {
vare = evt || window.event; //得到事件对象
if(e.srcElement) { //如果支持srcElement,表示IE
if (e.type == 'mouseover') { //如果是over
returne.fromElement; //就使用from
} else if (e.type == 'mouseout') { //如果是out
returne.toElement; //就使用to
}
}else if (e.relatedTarget) { //如果支持relatedTarget,表示W3C
return e.relatedTarget;
}
}
有时我们需要阻止事件的默认行为,比如:一个超链接的默认行为就点击然后跳转到指定的页面。那么阻止默认行为就可以屏蔽跳转的这种操作,而实现自定义操作。
取消事件默认行为还有一种不规范的做法,就是返回false。
link.onclick = function () {
alert('Lee');
return false; //直接给个假,就不会跳转了。
};
PS:虽然return false;可以实现这个功能,但有漏洞;第一:必须写到最后,这样导致中间的代码执行后,有可能执行不到return false;第二:return false写到最前那么之后的自定义操作就失效了。所以,最好的方法应该是在最前面就阻止默认行为,并且后面还能执行代码。
link.onclick = function (evt) {
evt.preventDefault(); //W3C,阻止默认行为,放哪里都可以
alert('Lee');
};
link.onclick = function (evt) { //IE,阻止默认行为
window.event.returnValue= false;
alert('Lee');
};
跨浏览器兼容
function preDef(evt) {
vare = evt || window.event;
if(e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
}
上下文菜单事件:contextmenu,当我们右击网页的时候,会自动出现windows自带的菜单。那么我们可以使用contextmenu事件来修改我们指定的菜单,但前提是把右击的默认行为取消掉。
addEvent(window, 'load', function () {
vartext = document.getElementById('text');
addEvent(text,'contextmenu', function (evt) {
var e = evt || window.event;
preDef(e);
var menu =document.getElementById('menu');
menu.style.left = e.clientX + 'px';
menu.style.top = e.clientY + 'px';
menu.style.visibility = 'visible';
addEvent(document, 'click', function () {
document.getElementById('myMenu').style.visibility= 'hidden';
});
});
});
PS:contextmenu事件很常用,这直接导致浏览器兼容性较为稳定。
卸载前事件:beforeunload,这个事件可以帮助在离开本页的时候给出相应的提示,“离开”或者“返回”操作。
addEvent(window, 'beforeunload', function (evt){
preDef(evt);
});
鼠标滚轮(mousewheel)和DOMMouseScroll,用于获取鼠标上下滚轮的距离。
addEvent(document, 'mousewheel', function (evt){ //非火狐
alert(getWD(evt));
});
addEvent(document, 'DOMMouseScroll', function(evt) { //火狐
alert(getWD(evt));
});
function getWD(evt) {
vare = evt || window.event;
if(e.wheelDelta) {
return e.wheelDelta;
} else if (e.detail) {
return -evt.detail * 30; //保持计算的统一
}
}
PS:通过浏览器检测可以确定火狐只执行DOMMouseScroll。
DOMContentLoaded事件和readystatechange事件,有关DOM加载方面的事件,关于这两个事件的内容非常多且繁杂,我们先点明在这里,在项目课程中使用的时候详细讨论。
作者: jimu 时间: 2016-4-14 23:07
第32章 JSON
学习要点:
1.JSON语法
2.解析和序列化
前两章我们探讨了XML的结构化数据,但开发人员还是觉得这种微型的数据结构还是过于烦琐、冗长。为了解决这个问题,JSON的结构化数据出现了。JSON是JavaScript的一个严格的子集,利用JavaScript中的一些模式来表示结构化数据。
一.JSON语法
JSON和XML类型,都是一种结构化的数据表示方式。所以,JSON并不是JavaScript独有的数据格式,其他很多语言都可以对JSON进行解析和序列化。
JSON的语法可以表示三种类型的值:
1.简单值:可以在JSON中表示字符串、数值、布尔值和null。但JSON不支持JavaScript中的特殊值undefined。
2.对象:顾名思义。
3.数组:顾名思义。
简单值
100、"Lee" 这两个量就是JSON的表示方法,一个是JSON数值,一个是JSON字符串。布尔值和null也是有效的形式。但实际运用中要结合对象或数组。
对象
JavaScript对象字面量表示法:
var box = {
name: 'Lee',
age: 100
};
而JSON中的对象表示法需要加上双引号,并且不存在赋值运算和分号:
{
"name": "Lee", //使用双引号,否则转换会出错
"age" : 100
}
数组
JavaScript数组字面量表示法:
var box = [100, 'Lee', true];
而JSON中的数组表示法同样没有变量赋值和分号:
[100, "Lee", true]
一般比较常用的一种复杂形式是数组结合对象的形式:
[
{
"title" : "a",
"num" : 1
},
{
"title" : "b",
"num" : 2
},
{
"title" : "c",
"num" : 3
}
PS:一般情况下,我们可以把JSON结构数据保存到一个文本文件里,然后通过XMLHttpRequest对象去加载它,得到这串结构数据字符串(XMLHttpRequest对象将在Aajx章节中详细探讨)。所以,我们可以模拟这种过程。
模拟加载JSON文本文件的数据,并且赋值给变量。
var box = '[{"name" :"a","age" : 1},{"name" :"b","age" : 2}]';
PS;上面这短代码模拟了var box= load('demo.json');赋值过程。因为通过load加载的文本文件,不管内容是什么,都必须是字符串。所以两边要加上双引号。
其实JSON就是比普通数组多了两边的双引号,普通数组如下:
var box = [{name : 'a', age : 1},{name : 'b',age : 2}];
二.解析和序列化
如果是载入的JSON文件,我们需要对其进行使用,那么就必须对JSON字符串解析成原生的JavaScript值。当然,如果是原生的JavaScript对象或数组,也可以转换成JSON字符串。
对于讲JSON字符串解析为JavaScript原生值,早期采用的是eval()函数。但这种方法既不安全,可能会执行一些恶意代码。
var box = '[{"name" :"a","age" : 1},{"name" :"b","age" : 2}]';
alert(box); //JSON字符串
var json = eval(box); //使用eval()函数解析
alert(json); //得到JavaScript原生值
ECMAScript5对解析JSON的行为进行规范,定义了全局对象JSON。支持这个对象的浏览器有IE8+、Firefox3.5+、Safari4+、Chrome和Opera10.5+。不支持的浏览器也可以通过一个开源库json.js来模拟执行。JSON对象提供了两个方法,一个是将原生JavaScript值转换为JSON字符串:stringify();另一个是将JSON字符串转换为JavaScript原生值:parse()。
var box = '[{"name" :"a","age" : 1},{"name" :"b","age" : 2}]'; //特别注意,键要用双引号
alert(box);
var json = JSON.parse(box); //不是双引号,会报错
alert(json);
var box = [{name : 'a', age : 1},{name : 'b',age : 2}]; //JavaScript原生值
var json = JSON.stringify(box); //转换成JSON字符串
alert(json); //自动双引号
在序列化JSON的过程中,stringify()方法还提供了第二个参数。第一个参数可以是一个数组,也可以是一个函数,用于过滤结果。第二个参数则表示是否在JSON字符串中保留缩进。
var box = [{name : 'a', age : 1, height :177},{name : 'b', age : 2, height : 188}];
var json = JSON.stringify(box, ['name', 'age'],4);
alert(json);
PS:如果不需要保留缩进,则不填即可;如果不需要过滤结果,但又要保留缩进,则讲过滤结果的参数设置为null。如果采用函数,可以进行复杂的过滤。
var box = [{name : 'a', age : 1, height :177},{name : 'b', age : 2, height : 188}];
var json = JSON.stringify(box, function (key,value) {
switch(key) {
case 'name' :
return'Mr. ' + value;
case 'age' :
returnvalue + '岁';
default :
returnvalue;
}
}, 4);
alert(json);
PS:保留缩进除了是普通的数字,也可以是字符。
还有一种方法可以自定义过滤一些数据,使用toJSON()方法,可以将某一组对象里指定返回某个值。
var box = [{name : 'a', age : 1, height : 177,toJSON : function () {
returnthis.name;
}},{name : 'b',age : 2, height : 188, toJSON :function () {
returnthis.name;
}}];
var json = JSON.stringify(box);
alert(json);
PS:由此可见序列化也有执行顺序,首先先执行toJSON()方法;如果应用了第二个过滤参数,则执行这个方法;然后执行序列化过程,比如将键值对组成合法的JSON字符串,比如加上双引号。如果提供了缩进,再执行缩进操作。
解析JSON字符串方法parse()也可以接受第二个参数,这样可以在还原出JavaScript值的时候替换成自己想要的值。
var box = '[{"name" :"a","age" : 1},{"name" :"b","age" : 2}]';
var json = JSON.parse(box, function (key, value){
if(key == 'name') {
return 'Mr. ' + value;
}else {
return value;
}
});
alert(json[0].name);
作者: jimu 时间: 2016-4-14 23:07
第33章 Ajax
学习要点:
1.XMLHttpRequest
2.GET与POST
3.封装Ajax
2005年Jesse JamesGarrett发表了一篇文章,标题为:“Ajax:A new Approach toWeb Applications”。他在这篇文章里介绍了一种技术,用他的话说,就叫:Ajax,是AsynchronousJavaScript + XML的简写。这种技术能够想服务器请求额外的数据而无须卸载页面(即刷新),会带来更好的用户体验。一时间,席卷全球。
一.XMLHttpRequest
Ajax技术核心是XMLHttpRequest对象(简称XHR),这是由微软首先引入的一个特性,其他浏览器提供商后来都提供了相同的实现。在XHR出现之前,Ajax式的通信必须借助一些hack手段来实现,大多数是使用隐藏的框架或内嵌框架。
XHR的出现,提供了向服务器发送请求和解析服务器响应提供了流畅的接口。能够以异步方式从服务器获取更多的信息,这就意味着,用户只要触发某一事件,在不刷新网页的情况下,更新服务器最新的数据。
虽然Ajax中的x代表的是XML,但Ajax通信和数据格式无关,也就是说这种技术不一定使用XML。
IE7+、Firefox、Opera、Chrome和Safari都支持原生的XHR对象,在这些浏览器中创建XHR对象可以直接实例化XMLHttpRequest即可。
var xhr = new XMLHttpRequest();
alert(xhr); //XMLHttpRequest
如果是IE6及以下,那么我们必须还需要使用ActiveX对象通过MSXML库来实现。在低版本IE浏览器可能会遇到三种不同版本的XHR对象,即MSXML2.XMLHttp、MSXML2.XMLHttp.3.0、MSXML2.XMLHttp.6.0。我们可以编写一个函数。
function createXHR() {
if(typeof XMLHttpRequest != 'undefined') {
return new XMLHttpRequest();
}else if (typeof ActiveXObject !='undefined') {
var versions = [
'MSXML2.XMLHttp.6.0',
'MSXML2.XMLHttp.3.0',
'MSXML2.XMLHttp'
];
for (var i = 0; i < versions.length; i++) {
try{
return new ActiveXObject(version);
}catch (e) {
//跳过
}
}
}else {
throw new Error('您的浏览器不支持XHR对象!');
}
}
var xhr = new createXHR();
在使用XHR对象时,先必须调用open()方法,它接受三个参数:要发送的请求类型(get、post)、请求的URL和表示是否异步。
xhr.open('get', 'demo.php', false); //对于demo.php的get请求,false同步
PS:demo.php的代码如下:
<?php echo Date('Y-m-d H:i:s')?> //一个时间
open()方法并不会真正发送请求,而只是启动一个请求以备发送。通过send()方法进行发送请求,send()方法接受一个参数,作为请求主体发送的数据。如果不需要则,必须填null。执行send()方法之后,请求就会发送到服务器上。
xhr.send(null); //发送请求
当请求发送到服务器端,收到响应后,响应的数据会自动填充XHR对象的属性。那么一共有四个属性:
属性名 | |
| |
| 如果响应主体内容类型是"text/xml"或"application/xml",则返回包含响应数据的XML DOM文档 |
| |
| |
接受响应之后,第一步检查status属性,以确定响应已经成功返回。一般而已HTTP状态代码为200作为成功的标志。除了成功的状态代码,还有一些别的:
我们判断HTTP状态值即可,不建议使用HTTP状态说明,因为在跨浏览器的时候,可能会不太一致。
addEvent(document, 'click', function () {
varxhr = new createXHR();
xhr.open('get','demo.php?rand=' + Math.random(), false); //设置了同步
xhr.send(null);
if(xhr.status == 200) { //如果返回成功了
alert(xhr.responseText); //调出服务器返回的数据
}else {
alert('数据返回失败!状态代码:' +xhr.status + '状态信息:' + xhr.statusText);
}
});
以上的代码每次点击页面的时候,返回的时间都是时时的,不同的,说明都是通过服务器及时加载回的数据。那么我们也可以测试一下在非Ajax情况下的情况,创建一个demo2.php文件,使用非Ajax。
<script type="text/javascript"src="base.js"></script>
<script type="text/javascript">
addEvent(document,'click', function () {
alert("<?php echo Date('Y-m-dH:i:s')?>");
});
</script>
同步调用固然简单,但使用异步调用才是我们真正常用的手段。使用异步调用的时候,需要触发readystatechange事件,然后检测readyState属性即可。这个属性有五个值:
值 | | |
| | |
| | 已经调用open()方法,但尚未调用send()方法 |
| | |
| | |
| | |
addEvent(document, 'click', function () {
varxhr = new createXHR();
xhr.onreadystatechange= function () {
if (xhr.readyState == 4) {
if(xhr.status == 200) {
alert(xhr.responseText);
}else {
alert('数据返回失败!状态代码:' +xhr.status + '状态信息:'
+ xhr.statusText);
}
}
};
xhr.open('get','demo.php?rand=' + Math.random(), true);
xhr.send(null);
});
PS:使用abort()方法可以取消异步请求,放在send()方法之前会报错。放在responseText之前会得到一个空值。
二.GET与POST
在提供服务器请求的过程中,有两种方式,分别是:GET和POST。在Ajax使用的过程中,GET的使用频率要比POST高。
在了解这两种请求方式前,我们先了解一下HTTP头部信息,包含服务器返回的响应头信息和客户端发送出去的请求头信息。我们可以获取响应头信息或者设置请求头信息。我们可以在Firefox浏览器的firebug查看这些信息。
//使用getResponseHeader()获取单个响应头信息
alert(xhr.getResponseHeader('Content-Type'));
//使用getAllResponseHeaders()获取整个响应头信息
alert(xhr.getAllResponseHeaders());
//使用setRequestHeader()设置单个请求头信息
xhr.setRequestHeader('MyHeader', 'Lee'); //放在open方法之后,send方法之前
PS:我们只可以获取服务器返回回来响应头信息,无法获取向服务器提交的请求头信息,自然自定义的请求头,在JavaScript端是无法获取到的。
GET请求
GET请求是最常见的请求类型,最常用于向服务器查询某些信息。必要时,可以将查询字符串参数追加到URL的末尾,以便提交给服务器。
xhr.open('get', 'demo.php?rand=' +Math.random() + '&name=Koo', true);
通过URL后的问号给服务器传递键值对数据,服务器接收到返回响应数据。特殊字符传参产生的问题可以使用encodeURIComponent()进行编码处理,中文字符的返回及传参,可以讲页面保存和设置为utf-8格式即可。
//一个通用的URL提交函数
function addURLParam(url, name, value) {
url+= (url.indexOf('?') == -1 ? '?' : '&'); //判断的url是否有已有参数
url+= encodeURIComponent(name) + '=' + encodeURIComponent(value);
alert(url);
returnurl;
}
PS:当没有encodeURIComponent()方法时,在一些特殊字符比如“&”,会出现错误导致无法获取。
POST请求
POST请求可以包含非常多的数据,我们在使用表单提交的时候,很多就是使用的POST传输方式。
xhr.open('post', 'demo.php', true);
而发送POST请求的数据,不会跟在URL的尾巴上,而是通过send()方法向服务器提交数据。
xhr.send('name=Lee&age=100');
一般来说,向服务器发送POST请求由于解析机制的原因,需要进行特别的处理。因为POST请求和Web表单提交是不同的,需要使用XHR来模仿表单提交。
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
PS:从性能上来讲POST请求比GET请求消耗更多一些,用相同数据比较,GET最多比POST快两倍。
上一节课的JSON也可以使用Ajax来回调访问。
var url = 'demo.json?rand=' + Math.random();
var box = JSON.parse(xhr.responseText);
三.封装Ajax
因为Ajax使用起来比较麻烦,主要就是参数问题,比如到底使用GET还是POST;到底是使用同步还是异步等等,我们需要封装一个Ajax函数,来方便我们调用。
function ajax(obj) {
varxhr = new createXHR();
obj.url= obj.url + '?rand=' + Math.random();
obj.data= params(obj.data);
if(obj.method === 'get') obj.url = obj.url.indexOf('?') == -1 ?
obj.url + '?' +obj.data : obj.url + '&' + obj.data;
if(obj.async === true) {
xhr.onreadystatechange = function () {
if(xhr.readyState == 4) callback();
};
}
xhr.open(obj.method,obj.url, obj.async);
if(obj.method === 'post') {
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send(obj.data);
} else {
xhr.send(null);
}
if(obj.async === false) {
callback();
}
functioncallback () {
if (xhr.status == 200) {
obj.success(xhr.responseText); //回调
} else {
alert('数据返回失败!状态代码:' + xhr.status + ',
状态信息:' + xhr.statusText);
}
}
}
//调用ajax
addEvent(document, 'click', function () { //IE6需要重写addEvent
ajax({
method : 'get',
url : 'demo.php',
data : {
'name': 'Lee',
'age': 100
},
success : function (text) {
alert(text);
},
async : true
});
});
//名值对编码
function params(data) {
vararr = [];
for(var i in data) {
arr.push(encodeURIComponent(i) + '=' +encodeURIComponent(data));
}
returnarr.join('&');
}
PS:封装Ajax并不是一开始就形成以上的形态,需要经过多次变化而成。
欢迎光临 firemail (http://firemail.wang:8088/) |
Powered by Discuz! X3 |