WEB开发 JavaScript
1,JavaScript简介
web前端有三层:
-
HTML:从语义的角度,描述页面的结构
-
CSS:从审美的角度,描述样式(美化页面)
-
JavaScript:从交互的角度,描述行为(提升用户体验)
JavaSctipt历史背景介绍以及发展:
历史背景介绍
布兰登 艾奇 1995年在网景公司 发明的JavaScript
一开始的JavaScrip叫LiveScript
同一个时期 比如 VBScript,JScript等,但是后来被JavaScript打败了,现在的浏览器只运行一种脚本语言叫JavaScript
JavaScript的发展
2003年之前,JavaScript被认为“牛皮鲜”,用来制作页面上的广告,弹窗、漂浮的广告。什么东西让人烦,什么东西就是JavaScript开发的。所以浏览器就推出了屏蔽广告功能。
2004年,JavaScript命运开始改变,那一年,谷歌公司开始带头使用Ajax技术,Ajax技术就是JavaScript的一个应用。并且,那时候人们逐渐开始提升用户体验了。Ajax有一些应用场景。比如,当我们在百度搜索框搜文字时,输入框下方的智能提示,可以通过Ajax实现。比如,当我们注册网易邮箱时,能够及时发现用户名是否被占用,而不用调到另外一个页面。
2007年乔布斯发布了第一款iPhone,这一年开始,用户就多了上网的途径,就是用移动设备上网。JavaScript在移动页面中,也是不可或缺的。并且这一年,互联网开始标准化,按照W3C规则三层分离,JavaScript越来越被重视。
2010年,人们更加了解HTML5技术,HTML5推出了一个东西叫做Canvas(画布),工程师可以在Canvas上进行游戏制作,利用的就是JavaScript。
2011年,Node.js诞生,使JavaScript能够开发服务器程序了。
React-native inoic
如今,WebApp已经非常流行,就是用网页技术开发手机应用。手机系统有iOS、安卓。比如公司要开发一个“携程网”App,就需要招聘三队人马,比如iOS工程师10人,安卓工程师10人,前端工程师10人。共30人,开发成本大;而且如果要改版,要改3个版本。现在,假设公司都用web技术,用html+css+javascript技术就可以开发App。也易于迭代(网页一改变,所有的终端都变了)。
虽然目前WebApp在功能和性能上的体验远不如Native App,但是“WebApp慢慢取代Native App”很有可能是未来的趋势。
JavaScript的组成
-
ECMAScript 5.0:定义了js的语法标准: 包含变量 、表达式、运算符、函数、if语句 for循环 while循环、内置的函数
-
DOM(Document,文档模型) :操作网页上元素的API。比如让盒子显示隐藏、变色、动画 form表单验证
-
BOM(浏览器对象模型):操作浏览器部分功能的API。比如刷新页面、前进后退、让浏览器自动滚动
2,ECMASctipt 5.0
前端常用开发工具:sublime、visual Studio Code、HBuilder、Webstorm。
那么大家使用的PCharm跟WebStorm是JetBrains公司推出的编辑工具,开发阶段建议使用。
1,JS的引入方式
内接式:
外接式:
注意:
调试语句:
alert(''); 弹出警告框
console.log(''); 控制台输出
js中单行注释的快捷键是ctrl+/. 多行注释的快捷键是ctrl+shift+/
2,变量的声明
在js中使用var关键字进行变量的声明,注意,分号作为一句话代码的结束符。
var a = 100;
- 定义变量:var就是一个关键字,用来定义变量。所谓关键字,就是有特殊功能的小词语。关键字后面一定要有空格隔开。
- 变量的赋值:等号表示赋值,将等号右边的值,赋给左边的变量。
- 变量名:我们可以给变量任意的取名字。
变量要先定义,才能使用。比如,我们不设置变量,直接输出:
//这段代码控制台将会报错。
应该这么写:
var a; //先定义
a = 100; // 后赋值
//也可以直接定义变量+赋值
var a = 100;
如果声明了,但是没有定义,则只会显示undefine,不会报错。
变量的命名规范
只能由英语字母、数字、下划线、美元符号$构成,且不能以数字开头,并且不能是JavaScript保留字。
3,基本数据类型
五种基本数据类型:number,string,boolean,null,undefined
复杂(引用)数据类型:Function,Object,Arrary,Sting,Date(分别类似于python中的def,dict,list,string,time模块)
①number:数值类型
如果一个变量中,存放了数字,那么这个变量就是数值型的
var a = 100; //定义了一个变量a,并且赋值100
console.log(typeof a); //输出a变量的类型 使用typeof函数 检测变量a的数据类型
//特殊情况
var b = 5/0;
console.log(b); //Infinity 无限大
console.log(typeof b); //number 类型
ps:在JavaScript中,只要是数,就是数值型(number)的。无论整浮、浮点数(即小数)、无论大小、无论正负,都是number类型的。
②string:字符串类型
var a = 'qiuma'; //单引号和双引号都可以表示字符串
console.log(typeof a); //srting类型
//对于未声明的字符串,浏览器不会报错,也不会显示类型。
var a = "";
连字符和+号的区别
键盘上的 +
可能是连字符,也可能是数字的加号。如下:
console.log("我" + "爱" + "你"); //连字符,把三个独立的汉字,连接在一起了
console.log("我+爱+你"); //当字符串处理,原样输出
console.log(1+2+3); //输出6
将数值类型转换成字符类型:
var c = 10+'';
console.log(typeof c);
//此时在浏览器控制台中的 c 则是字符串类型了
③boolean:布尔类型
var isShow = flase;
console.log(typeof isShow);
var a = 1==1;
console.log(typeof a);
//这些都是布尔类型,和python中类似
④null:空对象
var c1 = null;//空对象 浏览器显示object对象
console.log(typeof c1);
⑤undefined:未定义
var d1;
//表示变量未定义,值为undefined
console.log(typeof d1);
4,运算符
①赋值运算符
以var x = 12,y=5来演示示例
②算数运算符
var a = 5,b=2;
③比较运算符
var x = 5;
④注意:
== 是比较值的相同。不参与比较数据类型
var x = 5;
var y = '5';
console.log(x==y);
//比较值是相同的,这里返回true
=== 等同于,是比较的是值和数据类型(内存地址)
console.log(x===y);
//这里返回的是false
运算注意点:
var a = 5;
var b = a++;
console.log(a);
console.log(b);
//这里得到的结果是,a=6,b=5;
//在这里,是先将a的值赋值给b输出,然后再对a++进行运算,然后此时先得出b=5,a=6。
var b = ++a;
console.log(a);
console.log(b);
//此时结果为:a=6,b=6;
//这里是先将a++进行运算,再将a输出
//++在前,先进行运算后赋值,++在后面,后进行运算。
在自增中适用此运算法则,在自减中同样适用。
5,数据类型转换
①将number类型转换成string类型
隐式转换
一个数字加上字符串,默认转换成字符串类型
var n1 = 123;
var n2 = '123';
var n3 = n1+n2;
// 隐式转换
console.log(typeof n3);
强制转换
// 强制类型转换String(),toString()
var n1 = 123;
var str1 = String(n1);
console.log(typeof str1); //返回了一个字符串类型
var num = 234;
var numStr = num.toSting();
console.log(typeof numStr); //返回一个字符串类型
②将string类型转换成number类型
var srtingNum = '121212';
var num = Number(srtingNum);
console.log(num); //返回121212
console.log(typeof num); //返回number
var stringNum = '789.123wadjhkd';
var num2 = Number(stringNum);
console.log(num2); //返回NaN 表示Not a Number 但是一个number类型
console.log(typeof num2); //返回number
// parseInt()可以解析一个字符串,并且返回一个整数,注意只保留开始前面的那一段整数
console.log(parseInt(stringNum)); //789
// parseFloat()可以解析一个字符串,返回一个浮点类型,保留开始前面那段数字
console.log(parseFloat(stringNum)); //789.123
6,流程控制
①if
var age = 20;
if(age>18){
//{}相当于作用域
console.log('可以去会所');
}
alert('qiuma'); //下面的代码照样执行
②if-else
var age = 20;
if(age>18){
//{}相当于作用域
console.log('可以去会所');
}else{
console.log('好好学js,年纪够了再去会所');
}
alert('qiuma'); //下面的代码照样执行
③if-else if -else
var age = 18;
if(age==18){
//{}相当于作用域
console.log('可以去会所');
}else if(age==30){
console.log('该娶媳妇了!!');
}else{
console.log('随便你了')
}
alert('qiuma'); //下面的代码照样执行
④逻辑与 &&,逻辑或 ||
逻辑与&&、 逻辑或|| //1.模拟 如果总分 >400 并且数学成绩 >89分 被清华大学录入 //逻辑与&& 两个条件都成立的时候 才成立 if(sum>400 && math>90){ console.log('清华大学录入成功') }else{ alert('高考失利') } //2.模拟 如果总分>400 或者你英语大于85 被复旦大学录入 //逻辑或 只有有一个条件成立的时候 才成立 if(sum>500 || english>85){ alert('被复旦大学录入') }else{ alert('高考又失利了') }
⑤switch(开关)语句
var gameScore = 'better';
switch(gameScore){
//case表示一个条件 满足这个条件就会输出,知道遇到break跳出。break终止循环。
//如果某个条件中不写 break,那么直到该程序遇到下一个break停止,这个就是'case穿透'
case 'good':
console.log('玩的很好')
//break表示退出
break;
case 'better':
console.log('玩的老牛逼了')
break;
case 'best':
console.log('恭喜你 吃鸡成功')
break;
default:
console.log('很遗憾')
}
//注意:switch相当于if语句 但是玩switch的小心case穿透
//default相当于else
⑥while循环
任何语言的循环离不开这三步:
- 初始化循环变量
- 判断循环条件
- 更新循环变量
// 例子:打印 1~9之间的数
var i = 1; //初始化循环变量
while(i<=9){ //判断循环条件
console.log(i);
i = i+1; //更新循环条件
}
⑦do while
var i = 3;
do{
console.log(i);
i+=1;
}while(i<10);
//用途不大,就是不管条件如何,一上来先做一次,然后再去循环。
⑧for循环
for循环遍历列表,是最常用的对数据的操作
//输出1-10之间的数
for(var i = 1;i <=10;i++){
console.log(i)
}
//输入1-100之间的所有数之和
var sum = 0;
for(var j = 1;j<=100;j++){
sum = sum + j
}
console.log(sum);
//输入1-100内的所有偶数
for(var i = 1; i <= 100;i++){
if(i%2==0){
console.log(i)
}
}
双重for循环
document.write(" ");往页面上写入内容
for(var i=1;i<=3;i++){
for(var j=0;j<6;j++){ //控制星星的数量
document.write('*')
}
document.write('
') //控制换行
}
在浏览器中输出直三角形:
for(var i = 1;i <= 6;i++){ for(var j = 1;j <= i;j++) { document.write('*'); } document.write('
'); }
在浏览器中输出等腰三角形:
for(var i=1;i<=6;i++){ //控制行数,一共显示6行 记得换行 document.write('
'); //控制我们的空格数 for(var s=i;s<6;s++){ document.write(' '); } //控制每行的输出*数 for(var j=1;j<=2*i-1;j++){ document.write('*'); } document.write('
'); }
7,复杂数据类型
五种基本数据类型:number,string,boolean,null,undefined
复杂(引用)数据类型:Function,Object,Arrary,Sting,Date(分别类似于python中的def,dict,list,string,time模块)
常用内置对象(复杂数据类型),所谓内置对象就是ECMAScript提供出来的一些方法,且都有相应的属性和方法。
1,Array: 数组
js中的数组和python中的列表类似
①数组的创建
字面量方式创建,简单粗暴
var colors = ['red','green','yellow'];
js中,所有的变量都挂载到了全局对象window中。
我们在浏览器开发者工具的console中,这里可以作为浏览器的一个解释器来使用。
当直接在下面输入window.colors同样等于在解释器中的代码console.log(colors); 会显示相同的结果。
在解释器中,console.log(colors); = console.log(window.colors); 只是在平时,这个window我们一般省略不写。
使用构造函数的方式创建
使用new关键词对构造函数(js中创建对象)的方式来创建,构造函数与面向对象有关系。
var colors = new Array();
console.log(colors);
//通过下标进行赋值
colors[0] = 'red';
colors[1] = 'green';
colors[2] = 'yellow';
console.log(colors);
遍历列表:
for(var i = 0;i < colors.length;i++){
console.log(i,colors[i]);
}
②数组的常用方法
下面为具体方法:
⑴concat(); 数组的合并
var north = ['北京','山东','天津'];
var south = ['东莞','深圳','上海'];
var newCity = north.concat(south);
console.log(newCity)
⑵join(' '); 连接
将数组中元素使用指定的字符串连接起来,它会形成一个新的字符串
var score = [98,78,76,100,0];
var str = score.join('|');
console.log(str);//"98|78|76|100|0"
⑶slice(); 返回数组的一段记录
slice(start,end); 返回数组的一段记录,相当于切片,顾头不顾尾
var arr = ['张三','李四','王文','赵六'];
var newArr = arr.slice(1,3);
console.log(newArr);//["李四", "王文"]
⑷pop 移除数组的最后一个元素
var arr = ['张三','李四','王文','赵六'];
arr.pop();
console.log(arr);//["张三", "李四","王文"]
⑸push() 向数组最后添加一个元素
var arr = ['张三','李四','王文','赵六'];
arr.push('小马哥');
console.log(arr);//["张三", "李四","王文","赵六","小马哥"]
⑹reverse() 反转数据
var names = ['alex','xiaoma','tanhuang','angle'];
names.reverse();
console.log(names);
⑺sort() 对数组排序
var names = ['alex','xiaoma','tanhuang','abngel'];
names.sort();
console.log(names);// ["alex", "angle", "tanhuang", "xiaoma"]
⑻isArray() 判断是否为数组,返回为 true | false
//布尔类型值 = Array.isArray(被检测的值) ;
var iArray = Array.isArray(iArray)
if(iArray){
console.log('是数组')
}else{
console.log('不是数组')
}
//是数组
⑼forEach(fn) 回调函数
forEach中的函数是一个匿名函数,同时也称为一个回调函数。可以用在遍历函数。
这是一个数组方法,是一个回调函数,匿名函数,通过forEach遍历数组的每一项内容,回调函数中的参数,第一个参数为item(每一项内容)第二个参数是数组的索引。
var names = ['qiuma', 'zhengmi', 'touji']
names.forEach(function(item, index){
console.log(item);
console.log(index);
})
//执行结果
qiuma
0
zhengmi
1
touji
2
2,string: 字符串
⑴chartAt() 返回指定索引的位置的字符
var str = 'qiuma';
var charset = str.charAt(3);
console.log(charset);//m
⑵concat 返回字符串值,表示两个或多个字符串的拼接
var str1 = 'qiu';
var str2 = 'ma';
console.log(str1.concat(str2,'|',str1,str2));//qiuma|qiuma
⑶replace(a,b) 将字符串b替换成字符串a
var a = '1234567755';
var newStr = a.replace("4567","****");
console.log(newStr);//123****755
⑷indexof() 查找字符的下标,如果找到返回字符串的下标,找不到则返回-1 。跟seach()方法用法一样
var str = 'alex';
console.log(str.indexOf('e'));//2
console.log(str.indexOf('p'));//-1
⑸slice(start,end) 左闭右开 分割字符串
var str = '小马哥';
console.log(str.slice(1,2));//马
⑹split('a',1) 以字符串a为分割字符串,并返回新的数组。如果第二个参数没写,表示返回整个数组,如果定义了个数,则返回相应个数的数组(从左往右数),如果是0,则返回一个空的数组,如果大于实际数字个数,则返回全部数组。
var str = '我的天呢,a是嘛,你在说什么呢?a哈哈哈';
console.log(str.split('a'));//["我的天呢,", "是嘛,你在说什么呢?", "哈哈哈"]
⑺substr(statr,end) 左闭右开
var str = '我的天呢,a是嘛,你在说什么呢?a哈哈哈';
console.log(str.substr(0,4));//我的天呢
⑻toLowerCase()转小写
var str = 'XIAOMAGE';
console.log(str.toLowerCase());//xiaomage
⑼toUpperCase()转大写
var str = 'xiaomage';
console.log(str.toUpperCase());//XIAOMAGE
特别:
//1.将number类型转换成字符串类型
var num = 132.32522;
var numStr = num.toString()
console.log(typeof numStr)
//四舍五入
var newNum = num.toFixed(2)
console.log(newNum)//132.32
//去除字符串中的前后空格
var str1 = ' qiuma ';
console.log(str1.trim());//qiuma
//创建一个空的字符串
var str = new String();
str[0] = 'a';
console.log(str);
不能用var str = ''; 创建
8,Math内置对象
⑴Math.ceil() 向上取整,'天花板函数'
var x = 1.234;
//天花板函数 表示大于等于 x,并且与它最接近的整数是2
var a = Math.ceil(x);
console.log(a);//2
//对页面进行分页设置
var = pageNum = 12345/30;
console.log(Math.ceil(pageNum));//如未取整,则得到的是一个小数
⑵Math.floor 向下取整,'地板函数'
var x = 1.234;
// 小于等于 x,并且与它最接近的整数 1
var b = Math.floor(x);
console.log(b);//1
⑶求两个数的最大值和最小值
//求 两个数的最大值 最小值
console.log(Math.max(2,5));//5
console.log(Math.min(2,5));//2
⑷随机数 Math.random(),取值范围是[0,1)。求min-max之间的随机数 min + Math.random()*(max-min);
var ran = Math.random();
console.log(ran);
//求200-500的数
console.log(200+Math.random()*300); //200加上300和随机数的乘积。
9,函数的使用
函数:就是把将一些语句进行封装,然后通过调用的形式,执行这些语句。命令打包,然后方便调用。
函数的作用:
- 解决大量的重复性的语句
- 简化编程,让编程模块化
# python 中声明并调用函数
def add(x,y):
return x+y
print(add(1,2))
//js中声明并调用函数
function add(x,y){
return x+y;
}
console.log(add(1,2));
具体解释:
函数对象:
var add = function(x, y){
return x + y;
}
console.log(typeof add);
console.log(add(1,2));
//function
//3
10,arguments 伪数组
arguments代表的是实参。实际上不是一个数组,只是一个伪数组,它有数组的属性,如length属性,但是没有数组的方法。
有个讲究的地方是:arguments只在函数中使用。
fn(2,4);
fn(2,4,6);
fn(2,4,6,8);
function fn(a,b,c) {
console.log(arguments);
console.log(fn.length); //获取形参的个数
console.log(arguments.length); //获取实参的个数
console.log("----------------");
}
11,Object 对象
1.使用Object或对象字面量创建对象
2.工厂模式创建对象
3.构造函数模式创建对象
4.原型模式创建对象
(总结:了解字面量创建对象,prototype方法调用对象的父类,继承。function关键字创建类,new关键字创建对象)
①使用Object或字面量方式创建对象
JS中最基本创建对象的方式:
var student = new Object();
console.log(student); //{}
console.log(typeof student); //object
student.name = "easy";
student.age = "20";
这样,一个student对象就创建完毕,拥有2个属性name
以及age
,分别赋值为"easy"
和20
。
如果你嫌这种方法有一种封装性不良的感觉。来一个对象字面量方式创建对象。
var sutdent = {
name : "easy",
age : 20
};
这样看起来似乎就完美了。但是马上我们就会发现一个十分尖锐的问题:当我们要创建同类的student1,student2,…,studentn时,我们不得不将以上的代码重复n次....
var sutdent1 = { name : "easy1", age : 20 }; var sutdent2 = { name : "easy2", age : 20 }; ... var sutdentn = { name : "easyn", age : 20 };
②工厂模式创建对象
JS中没有类的概念,那么我们不妨就使用一种函数将以上对象创建过程封装起来以便于重复调用,同时可以给出特定接口来初始化对象。
python中使用class来创建类,而js中使用function来创建类。
function createStudent(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
return obj;
}
var student1 = createStudent("easy1", 20);
var student2 = createStudent("easy2", 20);
...
var studentn = createStudent("easyn", 20);
检测student1是否是Object
console.log(student1 instanceof Object);
//true
这样一来我们就可以通过createStudent函数源源不断地”生产”对象了。看起来已经高枕无忧了,但贪婪的人类总有不满足于现状的天性:我们不仅希望”产品”的生产可以像工厂车间一般源源不断,我们还想知道生产的产品究竟是哪一种类型的。
比如说,我们同时又定义了”生产”水果对象的createFruit()函数:
function createFruit(name, color) {
var obj = new Object();
obj.name = name;
obj.color = color;
return obj;
}
var v1 = createStudent("easy1", 20);
var v2 = createFruit("apple", "green");
对于以上代码创建的对象v1、v2,我们用instanceof操作符去检测,他们统统都是Object类型。我们的当然不满足于此,我们希望v1是Student类型的,而v2是Fruit类型的。为了实现这个目标,我们可以用自定义构造函数的方法来创建对象
③构造函数模式创建对象
在上面创建Object这样的原生对象的时候,我们就使用过其构造函数:
var obj = new Object();
在创建原生数组Array类型对象时也使用过其构造函数:
var arr = new Array(10); //构造一个初始长度为10的数组对象
在进行自定义构造函数创建对象之前,我们首先了解一下构造函数
和普通函数
有什么区别。
1、实际上并不存在创建构造函数的特殊语法,其与普通函数唯一的区别在于调用方法。对于任意函数,使用new操作符调用,那么它就是构造函数;不使用new操作符调用,那么它就是普通函数。
2、按照惯例,我们约定构造函数名以大写字母开头,普通函数以小写字母开头,这样有利于显性区分二者。例如上面的new Array(),new Object()。
3、使用new操作符调用构造函数时,会经历(1)创建一个新对象;(2)将构造函数作用域赋给新对象(使this指向该新对象);(3)执行构造函数代码;(4)返回新对象;4个阶段。
ok,了解了构造函数
和普通函数
的区别之后,我们使用构造函数将工厂模式
的函数重写,并添加一个方法属性:
function Student(name, age) {
this.name = name;
this.age = age;
this.alertName = function(){
alert(this.name)
};
}
var p1 = new Student('qiuma',20);
p1.alertName();
function Fruit(name, color) {
this.name = name;
this.color = color;
this.alertName = function(){
alert(this.name)
};
}
这样我们再分别创建Student和Fruit的对象:
var v1 = new Student("easy", 20);
var v2 = new Fruit("apple", "green");
这时我们再来用instanceof操作符来检测以上对象类型就可以区分出Student以及Fruit了:
alert(v1 instanceof Student); //true
alert(v2 instanceof Student); //false
alert(v1 instanceof Fruit); //false
alert(v2 instanceof Fruit); //true
alert(v1 instanceof Object); //true 任何对象均继承自Object
alert(v2 instanceof Object); //true 任何对象均继承自Object
这样我们就解决了工厂模式
无法区分对象类型的尴尬。那么使用构造方法来创建对象是否已经完美了呢?使用构造器函数通常在js中我们来创建对象。
我们会发现Student和Fruit对象中共有同样的方法,当我们进行调用的时候这无疑是内存的消耗。
我们完全可以在执行该函数的时候再这样做,办法是将对象方法移到构造函数外部:
this指的是当前对象,和python中的self类似。
function Student(name, age) {
this.name = name;
this.age = age;
this.alertName = alertName;
}
function alertName() {
alert(this.name);
}
var stu1 = new Student("easy1", 20);
var stu2 = new Student("easy2", 20);
在调用stu1.alertName()时,this对象才被绑定到stu1上。
我们通过将alertName()函数定义为全局函数,这样对象中的alertName属性则被设置为指向该全局函数的指针。由此stu1和stu2共享了该全局函数,解决了内存浪费的问题
但是,通过全局函数的方式解决对象内部共享的问题,终究不像一个好的解决方法。如果这样定义的全局函数多了,我们想要将自定义对象封装的初衷便几乎无法实现了。更好的方案是通过原型对象模式来解决。
④原型的模式创建对象
原型链甚至原型继承,是整个JS中最难的一部分也是最不好理解的一部分,可以去查阅一下相关JS原型的一些知识点。
function Student() {
this.name = 'easy';
this.age = 20;
}
Student.prototype.alertName = function(){ //使用prototype方法调用(原型,属于对象的父类),继承
alert(this.name);
};
var stu1 = new Student();
var stu2 = new Student();
stu1.alertName(); //easy
stu2.alertName(); //easy
alert(stu1.alertName == stu2.alertName); //true 二者共享同一函数
12,Date日期对象
创建日期对象只有构造函数一种方式,使用new关键字
//创建了一个date对象
var myDate = new Date();
//返回本地时间
console.log(myDate.toLocalString());
//获取本地时间,月份中的第几天(1~31)
console.log(myDate.getDate());
13,JSON
①概念简介
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式。同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包。
在JSON中,有两种结构:对象和数组。
- 对象(对象中的key必须是带有双引号)
var packJSON= {"name":"alex", "password":"123"};
一个对象以“{”开始,“}”结束,“key/value”之间运用 “,”分隔。
- 数组
var packJSON = [{"name":"alex", "password":"123"}, {"name":"wusir", "password":"456"}]
数组是值的有序集合。一个数组以“[”开始,“]”结束。值之间运用 “,”分隔。
②JSON对象和JSON字符串转换
在数据传输过程中,JSON是以字符串的形式传递的,而JS更喜欢操作JSON对象,所以,JSON对象和JSON字符串之间的相互转换是关键。例如:
1,JSON字符串:
var jsonStr ='{"name":"qiuma", "password":"123"}' ;
2,JSON对象:
var jsonObj = {"name":"qiuma", "password":"123"};
3,JSON字符串 → JSON对象
var jsonobject = JSON.parse(jsonStr); //name:qiuma password:123
var jsonObject = jQuery.parseJSON(jsonstr);
4,JSON对象 → JSON字符串
var jsonstr =JSON.stringify(jsonObject ); //{"name":"qiuma","password":"123"}
③遍历JSON对象和JSON数组
1,遍历JSON对象代码如下:(for in 循环)
var packJSON = {"name":"qiuma", "password":"123"} ;
for(var k in packJSON){
//遍历packJSON 对象的每个key/value队,k为key,指的是键值的索引
console.log(k + " " + packJSON[k]);
}
// qiuma 123
2,遍历JSON数组代码如下
var packJSON = [{"name":"qiuma", "password":"123"}, {"name":"qiumawu", "password":"456"}];
for(var i in packJSON){
//遍历packJson 数组时,i为索引
console.log(i+ ' ' + packJSON[i].name + " " + packJSON[i].password);
}
3,正则表达式
对象:RegExp
正则表达式:检索字符串,用来规定文本搜索的内容
1,正则表达式的创建
①构造函数创建
new RegExp(“检测的文本”,“修饰符”)匹配模式常用g(全局匹配:找到所有匹配,而不是匹配后停止)和i(忽略大小写),不能有空格。
var str = 'hello world';
var reg1 = new RegExp('l', 'ig');
console.log(reg1);
②字面量创建(常用)
var reg2 = /o/gi;
检测字符e,不区分大小写,全局匹配
2,正则对象提供的检索方式
①test() 测试
var str = 'hello world';
var reg2 = /o/ig;
console.log(reg2.test(str)); //true
test() 检测字符串中是否包含定义字符模式,返回布尔
要检索的字符在字符串str中 返回true
②exec()
exec() 方法检索字符串中的正则表达式的匹配
如果匹配到了那么就返回一个存放有结果的数组,如果没有匹配到就返回一个null
var str = 'hello World';
var reg3 = /o/ig;
console.log(reg3.lastIndex); // 0 保存一次匹配时开始的下标,这里还未有匹配,得到0
console.log(reg3.exec(str)); //["o", index: 4, input: "hello world"]
console.log(reg3.lastIndex); //5 上一级index是4,这里得到下一次开始的下标5
lastIndex是保存一次匹配时开始的下标,还没有匹配就是保存0。
3,常用的方法
①match
使用正则表达式模式对字符执行查找,并将包含查找的结果作为数组返回。
字符串.match(正则)
var str = 'hello world';
var reg = /o/g;
console.log(str.match(reg)); //["o", "o"]
②replace 替换
replace(被替换的,替换的)
var str = 'hello world';
var reg = /o/g;
console.log(str.replace(reg,"*")); //hell* w*rld
③search() 查找下标
查找字符串再字符串中出现的位置 下标
var str = 'hello world';
var reg = /o/g;
console.log(str.search(reg)); //4
④split() 分割
以匹配的规则分割
var str = 'hello world';
var reg = /o/g;
console.log(str.split(reg)); //["hell", " w", "rld"]
4,元字符(匹配的规则)
(1)单个字符和数字
① . (匹配除换行符 \a以外的任意字符)
var str = 'hello world';
var reg = /./g;
console.log(reg.exec(str)); //h
如果不想让字符有其他意义,可以使用转义字符 \ 。比如/www\....\.com/g; 这里 \. 表示匹配 . 。
var str = 'www.baidu.com';
var reg = /www\......\.com/g;
console.log(reg.exec(str)); //www.baidu.com
② [ ] (匹配[ ]里面的任意一个字符)
var str1 = 'hello';
var reg1 = /[a-zA-Z0-9]/g; //匹配字母还是数字
console.log(reg1.exec(str1)); //h
var str2 = 'a1314';
var reg2 = /[0-9][0-9][0-9]/g;
console.log(reg2.exec(str2)); //131
③ [^] (所有不在这个范围内的字符)
var str3 = 'abd123';
var reg3 = /[^a-z][^A-Z]/g; //匹配除字母意外的任意字符
console.log(reg3.exec(str3)); //12
④ \d和\D (\d:匹配数字,\D:匹配非数字)
var str4 = 'web';
var reg4 = /\d/g; //匹配数字
var reg5 = /\D/g; //匹配非数字
console.log(reg4.exec(str4)); //null
console.log(reg5.exec(str4)); //w
⑤ \w和\W (\w:匹配数字,字母下划线 \W:匹配除数字,字母,下划线以外的任意字符)
var str4 = 'web';
var reg6 = /\w/g; //匹配数字,字母,下划线
var reg7 = /\W/g; //匹配除数字,字母,下划线意外的任意字符
console.log(reg6.exec(str4)); //w
console.log(reg7.exec(str4)); //null
⑥ \s和\S (\s:匹配空格 \S:匹配非空白空格)
trim() :去除字符串前后空白方法
var str5 = ' qiuma';
var reg8 = /\s/g; //匹配空格
var reg9 = /\S/g; //匹配非空白字符
console.log(reg8.exec(str5)); //" "
console.log(reg9.exec(str5)); //q
⑦ ^ ,$ (^:以什么开头,$:以什么结尾)
var str = 'www.baidu.com';
var reg1 = /^www/g; //以www开头
var reg2 = /^www\......\.com$/g; //以www开头,com结尾
console.log(reg1.exec(str)); //www
console.log(reg2.exec(str)); //www.baidu.com
(2) 重复多个字符
① ? (匹配前面的字符0个或1个)
② * (匹配0个或任意多个字符,尽可能多的匹配)
③ + (至少匹配一次)
④ {} ({10}:匹配连续的10个字符,{1,4}:匹配最少1个字符,最多4个字符)
⑤ || (或者)
⑥ () (分组)
//1,?:匹配前面的字符0个或者一个
var str = '123webr445jkj';
var reg1 = /[0-9]?/g;
console.log(reg1.exec(str)); //1
//2,*:匹配0个或者任意多个字符,尽可能多的匹配
var reg2 = /[a-z]*/g;
console.log(reg2.exec(str)); //" "
//3,+:至少匹配一次
var reg3 = /\d+/g;
console.log(reg3.exec(str)); //123
//4,{10}:匹配连续的十个字符
var str2 = '11274567890';
var reg4 = /^1\d{10}$/g; //匹配连续的10个数字
console.log(reg4.exec(str2)); //11274567890
//5,{min,max} 最少min个,最多max个字符
var str3 = 'edge';
var reg5 = /^[a-zA-Z]{2,3}/g;
console.log(reg5.exec(str3)); //edg
//6,| :或者
var str4 = 'www.google.com';
var reg6 = /www.baidu|google|sogo/g;
console.log(reg6.exec(str4)); //google
//7,():分组
var str5 = 'www.google.com';
var reg7 = /(baidu)|(google)|(sogo)/g;
console.log(reg7.exec(str5)); //google
//获取匹配的字符串,下面三个分别为空,google,空
console.log(RegExp.$1);
console.log(RegExp.$2);
console.log(RegExp.$3);
var str6 = 'helloworld';
var reg8 = /(hello)(world)/g;
console.log(reg8.exec(str6));
console.log(RegExp.$1); //hello
//将分组好的匹配调换顺序
console.log(str5.replace(reg8,"$2 $1")); //world hello
(3)相关实例
①检索字符串中是否不包含字母
var str = '12';
var reg1 = /[^a-zA-Z]/g;
if (reg1.test(str)){
console.log('不包含');
} else {
console.log('包含');
} //不包含
②去除字符串首尾空格
var str = ' hello world! ';
var reg = /^\s+/ig;
var str1 = str.replace(reg, '');
var reg2 = /\s+$/ig;
var str2 = str1.replace(reg2,'');
console.log('|' + str2 + '|'); //|hello world!|
③检查用户账号
function checkUser(str){
var re = /^[a-zA-Z]\w{3,15}$/;
if(re.test(str)){
console.log('正确');
}else{
console.log('错误');
}
}
checkUser('qiuma_haha'); //调用
④匹配11位手机号码
function checkMobile(str){
var re = /^1\d{10}$/
if(re.test(str)){
console.log('正确');
}else{
console.log('错误');
}
}
checkMobile('13800138000'); //调用
checkMobile('1398888888889'); //错误示例
⑤匹配电话号码
验证规则:区号+好吗,区号以0开头,3位或者4位
号码由7位或8位数字组成,区号与好吗之间可以无连接符,也可以“-”连接
var re = /^0\d{2,3}-?\d{7,8}$/;
if(re.test(str)){
console.log('正确');
} else{
console.log('错误');
}
}
checkPhone('095-57777777'); //调用
//正确
⑥验证邮箱
验证规则:一个邮箱由两部分组成,第一部分@第二部分。
第一部分:由字符,数字,下划线,短线“-”,点号“.”组成
第二部分:为一个域名,域名由字母,数字,短线,域名后缀组成,二域名后缀一般为 .***或者***.***,一区的域名后缀一般为2-4位,如cn, con, net, 现在域名有的也会大于4位。
function checkEmail(str){
var re = /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/
if(re.test(str)){
console.log('正确');
} else{
console.log('错误');
}
}
checkEmail('[email protected]'); //调用
//正确
4,DOM(重点)
DOM文档加载的步骤
- 解析HTML结构。
- 加载外部脚本和样式表文件。
- 解析并执行脚本代码。
- DOM树构建完成。
- 加载图片等外部文件。
- 页面加载完毕。
1,DOM初步认识
(1)概念
所谓DOM,全称 Docuemnt Object Model 文档对象模型,是一个文档对象模型。
DOM 为文档提供了结构化表示,并定义了如何通过脚本来访问文档结构。
目的其实就是为了能让js操作html元素而制定的一个规范。
(2)解析过程
HTML加载完毕,渲染引擎会在内存中把HTML文档,生成一个DOM树,getElementById是获取内中DOM上的元素节点。然后操作的时候修改的是该元素的属性。
(3)DOM树(一切皆是节点)
上图可知,在HTML当中,一切都是节点:(非常重要)
- 元素节点:HMTL标签。
- 文本节点:标签中的文字(比如标签之间的空格、换行)
- 属性节点::标签的属性
整个html文档就是一个文档节点。所有的节点都是Object。元素,节点,他表示的就是标签对象
(4)DOM可以做什么
1,找对象(元素节点,标签对象)(获取DOM)
2,设置标签的属性值(对于标签属性的操作)
3,设置标签的样式(对于样式属性的操作)
4,设置标签值的操作
5,动态创建和删除元素(对于DOM的建增删改查)
6,事件的触发响应:找到事件源、事件、事件的驱动程序(js事件,ECMAScript相关知识对DOM进行操作)
用手(事件源)去按开关(事件),其中事件的驱动程序是灯的开和关。
(5)获取DOM的结构
- 获取文档对象:document
- 获取html:document.documentElement
- 获取body: document.body
2,获取DOM(事件源)
//方式一:通过id获取单个标签
var oDiv1 = document.getElementById("box1");
//方式二:通过 标签名 获得 标签数组,所以有s
var oDivs2 = document.getElementsByTagName("div")[0];
//方式三:通过 类名 获得 标签数组,所以有s
var oDivs3 = document.getElementsByClassName("box")[0];
注意:
通过方式二和方式三到的HTMLCollection和arguments(实参,伪数组相似),获取的是一个和伪数组类似的东西,因此不能使用数组的方法,但是可以添加索引来取确定的值。
3,事件
JS是事件驱动为核心的一门语言。
(1)事件的三要素
事件源,事件,驱动程序。
比如:网页上面弹出一个广告,我点击右上角的x,广告就关闭了。
这件事情里,事件源是:x。事件是:onclick。事件驱动程序是:广告关闭了。
谁引发的后续事件,谁就是事件源。
总结如下:
- 事件源:引发后续事件的html标签。
- 事件:js已经定义好了(见下图)。
-
事件驱动程序:对样式和html的操作。也就是DOM。
(2)代码书写步骤如下(重要)
①获取事件源:document.getElementById(“box”);
//类似与ios语言的UIButton *adBtn = [UIButtonbuttonWithType:UIButtonTypeCustom];
②绑定事件: 事件源box.事件onclick = function(){ 事件驱动程序 };
③书写事件驱动程序:关于DOM的操作
(3)常用事件
onclick //鼠标单击
ondblclick //鼠标双击
onkeyup //按下并释放键盘上的一个键时触发
onchange //文本内容或下拉菜单中的选项发生改变
onfocus //获得焦点,表示文本框等获得鼠标光标
onblur //失去焦点,表示文本框等失去鼠标光标
onmouseover //鼠标悬停,即鼠标停留再图片等的上方
onmouseout //鼠标移出,即离开图片等所在的区域
onload //网页文档加载事件
onunload //关闭网页时
onsubmit //表单提交事件
onreset //重置表单时
(4)绑定事件的方式
①直接绑定匿名函数(最常用)
var oDiv = document.getElementById("box");
oDiv.onclick = function () {
alert("我是弹出的内容");
};
②先单独定义函数,再绑定
var oDiv = document.getElementById("box");
oDiv.onclick = fn; //注意,这里是fn,不是fn()。fn()指的是返回值。
//单独定义函数
function fn() {
alert("我是弹出的内容");
};
注意上方代码的注释。绑定的时候,是写fn,不是写fn()。fn代表的是整个函数,而fn()代表的是返回值。
③行内绑定
注意第一行代码,绑定时,是写的"fn()"
,不是写的"fn"
。因为绑定的这段代码不是写在js代码里的,而是被识别成了字符串。
(5)window.onload()
①JavaScript入口函数 window.onload()
此函数调用,是当页面加载完毕(文档和图片)的时候,触发onload()函数,文档先加载,图片资源后加载
有一点我们要知道:js的加载是和html同步加载的。因此,如果使用元素在定义元素之前,容易报错。这个时候,onload事件就能派上用场了,我们可以把使用元素的代码放在onload里,就能保证这段代码是最后执行。
②window.onload()方法存在的问题
- 图片加载完成才调用onload方法,大家想个问题,如果现在用户访问JD商城页面,如果JD商城中的脚本文件在window.onload()方法调用,如果此时用户网速卡了,然后图片资源加载失败了,此时用户是做不了任何操作的,所以winodw.onload()方法有很大问题。所以等待着文档和图片资源加载完成之后,才会触发onload事件。
- window.onload()方法 如果脚本中书写两个这样的方法,那么会有事件覆盖现象。会执行后面那个。
window.onload = function(){
//1,获取事件源
var oDiv = document.getElementById('box');
console.log(oDiv);
//2,事件
oDiv.onclick = function(){
//3,事件驱动
alert(1);
}
};
4,事件驱动(DOM能做的)
(1)样式属性操作
所谓样式属性,就是style标签中的属性进行操作,并且通过js控制盒模型的属性(width,height等),控制盒子的显示隐藏(display:none|block),控制盒子的颜色切换(background:red|green)等等
操作文档对象三步走:
- 获取事件源
- 事件
- 事件驱动程序
test
样式属性-颜色切换
//1.获取事件源(事件对象,在文档中一切的标签都是对象)
var oDiv = document.getElementsByClassName('box')[0];
var isRed = true;
//2.事件 后执行的操作 异步操作
oDiv.onclick = function(){
if(isRed){
oDiv.style.backgroundColor = 'green';
isRed = false;
}else{
oDiv.style.backgroundColor = 'red';
isRed = true;
}
}
(2)标签值的操作
双闭合标签:innerText或者innerHTML
innerText和innerHTML的区别
innerText是对标签文本内容进行设置
innerHTML则是对文本和标签进行的渲染
像html中双闭合的标签,比如,
单闭合标签:除了img标签,就剩input了,使用value进行赋值
value使用在对于单闭合如表单控件中。有value属性的,必须通过value来设置值和赋值
test
哈哈哈
Qiuma
简洁版(解决冗余的代码)
test 哈哈哈Qiuma
(3)标签属性的操作
如对id,class,title,src,href等等进行肉眼能够看得到的标签的属性
元素显示隐藏的两种方式
display和class
①通过控制样式属性的display属性对盒子的显示和隐藏
②通过控制类来对盒子进行的显示和隐藏
(4)DOM的节点操作
比如可以对div中的一个p标签进行自由的删除和创建,可以使用函数方法完成。
①创建节点
新的标签(元素节点) = document.createElement("标签名");
比如,如果我们想创建一个li标签,或者是创建一个不存在的adbc标签,可以这样做:
②插入节点:
方式一:
父节点.appendChild(子节点)
//父子之间追加元素。父节点的最后插入一个新的子节点
方式二:
父.insertBefoe(新的子节点,作为参考的节点)
//兄弟之间插入新节点。在参考节点前插入一个新的节点。如果参考节点为null,那么他将在父节点最后插入一个子节点。
③删除节点
父节点.removeChild(子节点);
解释:用父节点删除子节点。必须要指定是删除哪个子节点。
如果我想删除自己这个节点,可以这么做:
node1.parentNode.removeChild(node1);
④获取父节点和子节点
⑤综合实例:
创建节点
哈哈哈哈
5,DOM案例
①模态框案例
点击一个按钮,弹出另一个界面,点击这个界面的x,可以返回最初界面
模态框
②模拟hover选择器效果
有一排的按钮,悬浮在这个按钮上面可以改变字体颜色,且可以改变鼠标的状态
③tab选项卡
使用ES6的语法来解决tab栏选项卡:
设计到解决变量提升的问题
主要是使用let可以声明块作用域中的变量,其他地方不可以使用
6,client, offset, scroll系列
①client系列
clientTop 内容区域到边框顶部的距离 ,说白了,就是边框的高度
clientLeft 内容区域到边框左部的距离,说白了就是边框的乱度
clientWidth 内容区域+左右padding 可视宽度
clientHeight 内容区域+ 上下padding 可视高度
Google Chrome 浏览器,中文名"谷歌浏览器",是一款免费的开源 web 浏览器,它由 Google 开发,发布于 2008 年。
②屏幕的可视宽度
通过onresize方法来获知屏幕可视区域的宽高,窗口大小发生变化时,会触发此方法。
③offset系列
offsetWidth占位宽 内容+padding+border
offsetHeight占位高
offsetTop: 如果盒子没有设置定位 到body的顶部的距离,如果盒子设置定位,那么是以父辈为基准的top值
offsetLeft: 如果盒子没有设置定位 到body的左部的距离,如果盒子设置定位,那么是以父辈为基准的left值
④scroll系列
监听滚动事件,可触发此方法
学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界学习新技能,达成人生目标,开始用自己的力量影响世界
5,定时器
一次性定时器:setTimeout(fn, 2000)
周期性循环定时器:setInterval(fn, 1000)
里面有两个参数,第一个函数是回调函数(匿名函数),第二个参数是时间(毫秒级)。
1, setTimeout()
只在指定时间后执行一次
//结果显示,走到尽头是最后才打印出来的,说明进行了异步操作。
2,setInterval()
在指定时间为周期循环执行
//结果会一直输出1,2,3,4...
3,清除定时器
clearInterval(); 清除一次性定时器
cleatTimeout(); 清除周期性定时器
定时器代码示例
我的盒子
使用定时器前需要先清定时器,再开定时器,页面不会出现问题。
6,BOM
浏览器对象模型
主要用于操作浏览器部分功能的API。比如让浏览器自动滚动,刷新,前进后退。
1,BOM的结构图
- window对象是BOM的顶层(核心)对象,所有对象都是通过它延伸出来的,也可以称为window的子对象。
- DOM是BOM的一部分。
window对象:
- window对象是JavaScript中的顶级对象。
- 全局变量、自定义函数也是window对象的属性和方法。
- window对象下的属性和方法调用时,可以省略window。
2,弹出系统对话框
比如说,alert(1)
是window.alert(1)
的简写,因为它是window的子方法。
window.alert(); //不同浏览器中的外观不一样,这里window可以省略不写
3,打开窗口,关闭窗口
①打开窗口
window.open(url,targer);
url: 要打开的地址
target: 新窗口的位置,可以是:_blank
、_self
、 _parent
父框架。
②关闭窗口
window.close();
4,location对象
window.location
可以简写成location。location相当于浏览器地址栏,可以将url解析成独立的片段。
location对象的属性
- href:跳转
- hash:返回url中#后面的内容,包含#
- host:主机名,包括端口号
- hostname:主机名
- pathname:url中的路径部分,路径名,端口号之后的路径
- protocol:协议 一般是http、https
- search:查询字符串
- origin:原始地址
- href:地址
location对象的方法;
window.location.reload(); //全局刷新页面,相当于浏览器导航栏上 刷新按钮
案例:模拟a标签跳转:
5,history对象
网页中的后退,前进,刷新
7,ToDoList案例
①index.css
body { margin: 0; padding: 0; font-size: 16px; background: #CDCDCD; } .header { height: 50px; background: #333; background: rgba(47,47,47,0.98); } .header .box,.content{ width: 600px; padding: 0 10px; margin: 0 auto; } .content{ margin: 0 auto; } label { float: left; width: 100px; line-height: 50px; color: #DDD; font-size: 24px; cursor: pointer; font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; } .header input { float: right; width: 60%; height: 24px; margin-top: 12px; text-indent: 10px; border-radius: 5px; box-shadow: 0 1px 0 rgba(255,255,255,0.24), 0 1px 6px rgba(0,0,0,0.45) inset; border: none } input:focus { outline-width: 0 } h2 { position: relative; } span { position: absolute; top: 2px; right: 5px; display: inline-block; padding: 0 5px; height: 20px; border-radius: 20px; background: #E6E6FA; line-height: 22px; text-align: center; color: #666; font-size: 14px; } ol,ul { padding: 0; list-style: none; } li input { position: absolute; top: 2px; left: 10px; width: 22px; height: 22px; cursor: pointer; } p { margin: 0; } li p input { top: 3px; left: 40px; width: 70%; height: 20px; line-height: 14px; text-indent: 5px; font-size: 14px; } li { height: 32px; line-height: 32px; background: #fff; position: relative; margin-bottom: 10px; padding: 0 45px; border-radius: 3px; border-left: 5px solid #629A9C; box-shadow: 0 1px 2px rgba(0,0,0,0.07); } ol li { cursor: move; } ul li { border-left: 5px solid #999; opacity: 0.5; } li a { position: absolute; top: 2px; right: 5px; display: inline-block; width: 14px; height: 12px; border-radius: 14px; border: 6px double #FFF; background: #CCC; line-height: 14px; text-align: center; color: #FFF; font-weight: bold; font-size: 14px; cursor: pointer; } .footer { color: #666; font-size: 14px; text-align: center; } .footer a { color: #666; text-decoration: none; color: #999;
②index.html
ToDoList—最简单的待办事项列表 正在进行
已经完成
③index.js
function clear() { localStorage.clear(); load(); } function postaction() { var title = document.getElementById("title"); if (title.value == "") { alert("内容不能为空"); } else { var data = loadData(); var todo = { "title": title.value, "done": false }; data.push(todo); saveData(data); var form = document.getElementById("form"); form.reset(); load(); } } function loadData() { var collection = localStorage.getItem("todo"); if (collection != null) { return JSON.parse(collection); } else return []; } function saveSort() { var todolist = document.getElementById("todolist"); var donelist = document.getElementById("donelist"); var ts = todolist.getElementsByTagName("p"); var ds = donelist.getElementsByTagName("p"); var data = []; for (i = 0; i < ts.length; i++) { var todo = { "title": ts[i].innerHTML, "done": false }; data.unshift(todo); } for (i = 0; i < ds.length; i++) { var todo = { "title": ds[i].innerHTML, "done": true }; data.unshift(todo); } saveData(data); } function saveData(data) { localStorage.setItem("todo", JSON.stringify(data)); } function remove(i) { var data = loadData(); var todo = data.splice(i, 1)[0]; saveData(data); load(); } function update(i, field, value) { var data = loadData(); var todo = data.splice(i, 1)[0]; todo[field] = value; data.splice(i, 0, todo); saveData(data); load(); } function edit(i) { load(); var p = document.getElementById("p-" + i); title = p.innerHTML; p.innerHTML = ""; var input = document.getElementById("input-" + i); input.setSelectionRange(0, input.value.length); input.focus(); input.onblur = function() { if (input.value.length == 0) { p.innerHTML = title; alert("内容不能为空"); } else { update(i, "title", input.value); } }; } function load() { var todolist = document.getElementById("todolist"); var donelist = document.getElementById("donelist"); var collection = localStorage.getItem("todo"); if (collection != null) { var data = JSON.parse(collection); var todoCount = 0; var doneCount = 0; var todoString = ""; var doneString = ""; for (var i = data.length - 1; i >= 0; i--) { if (data[i].done) { doneString += "
" + data[i].title + "
" + "-" + data[i].title + "
" + "-over.