前端基础入门——让页面动起来的JS基础部分

548 阅读17分钟

什么是JavaScript

JavaScript是一种基于对象和事件驱动的客户端脚本语言,最初的设计师为了检验HTML表单输入的正确性,起源于Netscape公司的Livescript语言。sun公司与Netscape公司合作开发Livescript语言后,把它们主打的Java加到了script前面,因此JavaScript与Java其实没有什么关系。

JavaScript的组成

完整的JavaScript是由ECMAScript(语法)、Browser Objects(DOM、BOM)(特性)组成的。

ECMAscript提供核心语言功能,相当于它的语法

DOM提供访问和操作网页内容的方法和接口

BOM提供与浏览器交互的方法和接口,如打开浏览器、对浏览器窗口进行缩放等

在HTML中使用JavaScript

可以在head或body中使用

如果要导入外部脚本,需在script标签中的src属性中指定外部脚本的来源。

JS语法

JavaScript语法规则

JavaScript的注释与分号

// 单行注释

/**/多行注释

语句结束使用分号,如果省略,则有解析器确定语句的结尾。但还是强烈建议自行加上分号。

JavaScript的语法

ECMAScript中的一切(变量、函数名和操作符)都是大小写敏感的

标识符

标识符即变量、函数、属性的名字,或者函数的参数

标识符由字母、数字、下划线_或美元符号$组成,不能以数字开头,不能使用关键字、保留字等作为标识符

什么是变量

ECMAScript的变量是松散类型,可以用来保存任何类型的数据,换句话说,每个变量仅仅是一个用于保存值的占位符而已。

变量的声明与赋值

变量声明

语法:var 变量名

变量赋值

<script>
    //先声明再赋值
    var name;
    name="vicky";
    //声明的同时赋值
    var age=24;
    //一次声明多个变量
    var sexual,addr,tel;
</script>

说明:

  • 省略var声明的变量是全局变量
  • 不推荐省略var操作符来定义全局变量
  • 当我们想存储值的时候就需要使用变量

JavaScript数据类型

typeof操作符

变量本身没有类型,变量的类型取决于它的值

undefined与null

undefined类型只有一个值,即特殊的undefined。一般而言,不存在需要显式地把一个变量设置为undefined值的情况。如没有被赋值的变量就是undefined。

null值表示一个空对象指针。如果定义的变量准备在将来用于保存对象,那么最好将改变量初始化为null而不是其他值。

undefined值是派生自null值的,所以undefined==null的返回结果是true

number与isNaN

number表示整数和浮点数

NaN即非数值(Not a Number)是一个特殊的数值(typeof取到的类型还是number)【面试题重点】

说明【面试题重点】

  • 任何涉及NaN的操作(例如NaN/10)都会返回NaN
  • NaN与任何值都不相等,包括NaN本身【重要】

isNaN(n)功能是检测n是否是“非数值”,返回值是boolean

说明

isNaN()对接收的数值,先尝试转换为数值,再检测是否为非数值。因此即使是"16"检测出来也会是数值,返回false。

数值转换

有三个函数可以把非数值转换为数值,分别是:Number()、parseInt()、parseFloat()

Number()可以用于任何数据类型,parseInt()和parseFloat()则专门用于把字符串转换成数值

Number()

遇到转不了的就会返回NaN

parseInt()

把其他类型的数据中的数字部分提取出来,并转换成整型数。但是这个数字部分必须位于数据的开头。

会忽略字符串前面的空格,直至找到第一个非空格字符。

说明

  • 转换空字符返回NaN
  • 这个函数提供第二个参数,转换时使用的技术(即多少进制)

parseFloat()

从第一个字符开始解析每个字符,直至遇见一个无效的浮点数字符为止

注意

除了第一个小数点有效外,parseFloat()与parseInt()的第二个区别在于它始终都会忽略前导的零。

var topval=parseInt("28px");
console.log(topval);    //返回28
var c="abc58";
console.log(c);    //返回NaN
var d=parseFloat("12.34.56");    //返回12.34
var e=parseInt("0123");    //返回123
var f=parseFloat("0.123abc");    //返回0.123

JavaScript的String和Boolean类型

String类型用于表示由零或者多个16位Unicode字符组成的字符序列,即字符串。字符串可以由双引号(")或者单引号(')表示

boolean用于表示真假的类型,即true表示真,false表示假

toString()与String()

语法:

str.toString();

String(str)

功能:将str转换为字符串

返回值:str的一个副本

参数:str是要转换的内容,可以是数值、布尔值、对象和字符串

说明:在不知道要转换的值是不是null或undefined的情况下,还可以用String()函数,它能够将任何类型的值转换为字符串。

Boolean()

语法:Boolean(x);

说明:

除0之外的所有数字,转换为布尔型都为true

除""之外的所有字符,转换为布尔型都为true

null和undefined转换为布尔型为false

JavaScript算数操作符

什么是表达式

将同类型的数据(如常量、变量、函数等)

符号按一定的规则连接起来的、有意义的式子称为表达式

操作符的分类

算符操作符、逻辑操作符、赋值操作符、比较操作符、三元操作符

算数操作符

+、-、*、/、%

<script>
var num1=10,num2="5",num3="c";
console.log(num1*num2);    //返回50,因为浏览器会进行隐式转换
console.log(num1*num3);    //返回NaN
</script>

注意

  • "+"运算符还能用于把文本值或字符串连接起来,如果需要把两个或以上的字符串连接起来,可以用"+"运算符
  • 抑或想把别的数据类型转化为字符串,也可以用"+"运算符把该数据类型跟字符串相加,因为只要表达式中有一个字符串,就会自动将其他表达式也转化为字符串。

递增和递减

++a与a++都是对a进行递增的操作

区别在于++a先返回递增之后的a的值,a++先返回a的值,再返回递增之后的值

递减同理

赋值操作符

简单赋值:=

复合赋值:+=、-=、*=、/=、%=

比较操作符

>、<、>=、<=、==、===、!=、!==

==:相等,只比较值是否相等

===:全等,比较值的同时比较数据类型是否相等

!=:不相等,比较值是否不相等

!==:不全等,比较值的同时比较数据类型是否不相等

返回值:boolean型

注意:

null==undefined返回true

null===undefined返回false

三元操作符

语法:条件?执行代码1:执行代码2

说明:可代替简单的if语句,如果条件成立,执行代码1,否则执行代码2

JavaScript逻辑操作符

&&:与

只要有一个条件不成立,返回false

说明

在有一个操作数不是布尔值的情况,逻辑与操作就不一定返回值,此时它遵循下列规则:

如果第一个操作数隐式类型转换后为true,则返回最后一个操作数。

console.log(80 && 55);    //打印55
console.log("hello" && 65 && "abc");    //打印abc

如果第一个从操作数隐式类型转换后为false,则返回第一个操作数。

console.log(0 && 88);    //返回0
console.log("" && 0 && 30>20);    //返回""

当前面的操作数隐式类型转换后为true时,如果有一个操作数是null,则返回null

当前面的操作数隐式类型转换后为true时,如果有一个操作数是NaN,则返回NaN

当前面的操作数隐式类型转换后为true时, 如果有一个操作数是undefined,则返回undefined

||:或

只要有一个条件成立,返回true

说明

在有一个操作数不是布尔值的情况下,逻辑或操作就不一定返回布尔值,此时它遵循下列规则:

如果第一个操作数隐式类型转换后为true,则返回第一个操作数

如果第一个操作数隐式类型转换后为false,则返回最后一个操作数

<script>
    var m;    //undefined
    console.log("hello" || 0);    //hello
    console.log(99 || 0 || "abc");    //99
    console.log("" || 88 ||true);    //88
    console.log("" || 0 || "abc");    //abc
    console.log(0 || "" || null);    //null
    console.log(0 || "" || null || "hello");    //hello
    console.log(m || NaN || 99);    //99
    console.log("" || m);    //99
    console.log(30*"abc" || 55-"def");    //NaN
 
    /*
    简言之,就是从前往后找到第一个进行隐式类型转换后非false、非null、非NaN、非undefined
    的值作为返回值,如果从头到尾都没有找到这样的,就返回表达式的最后一个值
    */
</script>

如果两个操作数是null,则返回null

如果两个操作数是NaN,则返回NaN

如果两个操作手是undefined,则返回undefined

!:非

条件为true,返回false,反之亦然

说明

无论操作数是什么数据类型,逻辑非都会返回一个布尔值

!!同时使用两个逻辑非操作符时:

第一个逻辑非操作会基于无论什么操作数返回一个布尔值

第二个逻辑非则对该布尔值求反

简言之,等效于对表达式进行显示类型转换,转换为boolean型

<script>
    console.log(!false);    //true
    console.log(!88);    //false
    console.log(!0);    //true
    console.log("red");    //false
    console.log(!NaN);    //true
    console.log(!null);    //true
    console.log(!!"");    //false
    console.log(!!"blue");    //true
</script>

JS流程控制语句

JavaScript分支语句

条件语句if

语法:

//语法①
if(条件){
    执行语句;
}
//语法②
if(条件){
    执行语句;
}
else{
    执行语句;
}
//语法③
if(条件){
    执行语句;
}
else if(条件){
    执行语句;
}
...
else{
    执行语句;
}

prompt()的应用

语法:prompt()

功能:弹出输入框

返回值:点击确定,返回输入内容;点击取消,返回null

alert()的应用

语法:alert()

功能:弹出警告对话框

length()的应用

语法:string.length

功能:获取string字符串的长度

返回值:number

switch语句

<script>
    switch(表达式){
    case 值:执行语句;
    break;
    case 值:执行语句;
    break;
    ...
    default:执行语句;    //上面没有符合条件的,就执行这一句
}
</script>

星期的获取

语法:new Date().getDay()

功能:获取星期

返回值:number(0-6) P.S 星期日返回0

document.write

语法:document.write("内容")

功能:向浏览器输出内容

JavaScript循环语句

for循环

<script>
for(变量初始化语句;入口条件;变化变量语句){
    执行语句;
}
//statement1:在循环(代码块)开始前执行,一般是给变量赋初值
//statement2:定义运行循环(代码块)的条件,循环的第一步
//statement3:在循环(代码块)已被执行之后执行,循环的最后一步,一般是给变量增(减)值
</script>

while循环

<script>
    while(条件){
    执行语句;
    变量变化语句;
}
</script>
<!--先判断条件是否成立,再执行循环体内的语句-->

do...while循环

<script>
    do{
    执行语句;
    变量变化语句;
}while(条件)
</script>
<!--先执行,再判断条件,这种语法的循环至少要被执行一次-->

总结:for循环语句适合已知循环次数的循环,while循环语句适合未知循环次数的循环

break及continue语句

break

立即退出循环 

continue

结束本次循环,继续开始下一次

JS函数

函数的作用

通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。

函数的定义

函数使用function声明,后跟一组参数以及函数体

<script>
    function functionName([arg0,arg1,...,argn]){
    statement;
}
</script>

说明

  • functionName是要定义的函数名,属于标识符
  • [ ] 中的arg0,arg1,...argn为函数的参数
  • [ ] 说明里面的内容不是必须的,它不是语法

函数的调用

函数名([arg1,arg2,...argn])

函数的返回值 

任何函数通过return语句,后面跟着返回的值来实现返回值

说明

  • 函数会在执行完return语句之后停止并立即退出
  • return语句也可以不带有任何返回值,用于提前停止函数执行又不需要返回值的情况

函数的参数

ECMAScript中的参数在内部用一个类数组来表示,在函数体内通过arguments对象来访问这个类数组参数,即用一个arguments来管理所有的参数,因此即使声明中的参数个数与实际调用时的参数个数不一致,也不会有什么影响,因为数组实际长度取决于调用时的参数个数。

说明

  • arguments对象知识与数组类似,并不是array的实例,即不是真正意义上的数组,而是一个类数组对象
  • [ ]语法访问它的每一个元素
  • length属性确定传递参数的个数
<script>    
    function inner(){    //声明函数时没有参数
    console.log(arguments.length);    //打印2
    console.log(arguments[0]); // 索引是从0开始的正整数,打印10
    console.log(arguments[1]); // 索引是从0开始的正整数,打印5
    console.log(arguments[2]); // arguments中只有两个参数,因此索引为2没有对应的参数,打印undefined
    }
    inner(10,5);    //调用函数时有两个参数
 
 
    function add(num1,num2){    
    arguments[0]=99;    //num1放在arguments[0],值由55改变为99
    console.log(num1);    //打印99
    }
    add(55,88);
    //P.S 严格模式下arguments不能通过赋值被更改
</script>

JS内置对象

所谓内置对象可以理解为浏览器已经封装好的对象,我们可以直接调用它

JavaScript对象之数组

数组主要是用来存储一组数据的

ECMAScript中的数组每一项都可以是不同类型的数据,数组的长度也可以随着元素的增加而自动增长

创建数组

使用Array构造函数

语法:new Array()

小括号说明:

  • 预先知道数组要 保存的项目数量
  • 向Array构造函数中传递数组应包含的项

使用数组字面量表示法

由一堆包含数组项的方括号[ ]表示,多个数组项之间用逗号隔开

<script>
	   // 创建一个保存颜色的数组
       var colors=new Array(3);    //指定数组长度
       var nums=new Array(1,3,6,9);    //指定数组包含项
       console.log(nums);    //打印[1,3,6,9]
       var cols=["red","yellow","green"];    //字面量表示法
       console.log(cols);    //打印["red","yellow","green"]
       var infos=[6,"marry",true];
       console.log(infos);    //打印[6,"marry",true]
</script>

数组元素的读和写

<script>
       var colors=new Array(3);
       colors[0]="#f00";
       colors[1]="#0f0";
       colors[2]="#00f";
       //通过索引往数组中写数据
 
 
       var cols=["red","yellow","green"];
       console.log(cols[1]);  // 读取cols这个数组中索引为1的值,打印yellow*/
       console.log(cols[5]);  // 读取cols这个数组中索引为5的值,该元素未定义,打印undefined*/
       //通过索引读取数组中的数据
</script>

数组的length属性

语法:array.length

功能:获取数组array的长度

返回值:number

说明

通过设置length可以从数组的末尾移除项或向数组中添加新项

把一个值放在超出当前数组大小的位置上时,会重新计算数组长度值,长度值等于最后一项索引+1(因为索引从0开始)

<script>
   var arr=["a","b","c","d"];
   console.log(arr.length);  //打印4
   arr[99]="z";
   console.log(arr.length); // 打印100
 
   // 数组的遍历
   for(var i=0;i<arr.length;i++){
       console.log(arr[i]);
   }
</script>

数组的方法

push()

语法:arrayObject.push(newele1,newele2,...,neweX)

功能:把它的参数顺序添加到arrayObject的尾部

返回值:把指定的值添加到数组后的新长度

unshift()

语法:arrayObject.unshift(newele1,newele2,...,neweX)

功能:把它的参数顺序添加到arrayObject的开头

返回值:把指定的值添加到数组后的新长度

pop()

语法:arrayObject.pop()

功能:删除arrayObject的最后一个元素

返回值:被删除的那个元素

shift()

语法:arrayObject.shift()

功能:删除arrayObject的第一个元素

返回值:被删除的那个元素

join()

语法:arrayObject.join(separator)

功能:用于把数组中的所有元素放入一个字符串

返回值:字符串

说明:separate是分隔符,默认情况下是用逗号隔开

<script>
    // join
    var nums=[2,4,5];
    var str=nums.join();  //打印2,4,5 
    var words=["border","left","color"];
    var wordstr=words.join("");
    console.log(wordstr);    //打印borderleftcolor
    var wordstr=words.join("-");
    console.log(wordstr);    //打印border-left-color
</script>

reverse()

语法:arrayObject.reverse()

功能:用于颠倒数组中元素的顺序

返回值:数组

sort()

语法:arrayObject.sort(sortby)

功能:用于对数组的元素进行排序

返回值:数组

说明:

  • 即使数组中的每一项都是数值,sort()方法比较的也是字符串
  • sort()方法可以接收一个比较函数作为参数,因此如果要进行数值的升降序排列,可以在参数中写一个匿名函数
<script>
    var arr=[9,23,15,-99,88,12,-2];
	// 降序 return 参数1<参数2
	//arr.sort(function(a,b){return a<b});
	// 升序 return 参数1>参数2
	arr.sort(function(a,b){return a>b});
	console.log(arr);
</script>

注意:不带参数进行的是字符编码的比较,即看作字符串来比较,如果想进行数值比较,需要用一个比较函数作为参数

concat()

语法:arrayObject.concat(newele1,newele2,...,neweX)

功能:用于连接两个或多个数组

返回值:数组

slice()

语法:arrayObject.slice(start,end)

功能:从已有的数组中返回选定的元素

参数:

start(必需)规定从何处开始选取,如是负数,从数组尾部开始算起

end(可选)规定从何处结束选取,是数组片段结束除的数组下标。

说明:

  • start和end指的是数组中的索引值
  • 截取从start到end(不包含该元素)的元素,即从start到end-1的元素
  • 如果没有指定end,切分的数组包含从start到数组结束的所有元素。
  • 如slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置。

返回值:数组

<script>
      var colors=["red","green","blue","yellow","orange"];
      var newColors=colors.slice(1,3);    //end=3,end-1=2,打印"green","blue"
      var newColors2=colors.slice(2,3);    //end=3,end-1=2,打印"blue"
      var newColors3=colors.slice(-4,3); //-4+5(数组长度)=1,end=3,end-1=2,打印"green","blue"
</script>

【面试常考】如何实现数组的拷贝

<script>
    // 完成以下代码段,实现b数组对a数组的拷贝,方法越多越好
    var a=[1,"yes",3],b;
    // 1、数组遍历,push
    b=new Array();
    for(var i=0;i<a.length;i++){
       b.push(a[i]);
    }
 
    // 2、concat()
    b=[].concat(a);
 
    // 3、slice();
    b=a.slice(0);
</script>

splice()  【非常强大】

删除数组项

语法:arrayObject.splice(index,count)

功能:删除从index处开始的零个或多个元素

返回值:含有被删除的元素的数组

说明:count是要删除的项目数量,如果设置为0,则不会删除项目。如果不设置,则删除从index开始的所有值

插入数组项

语法:arrayObject.splice(index,0,item1,...itemX)

功能:在指定位置插入值

参数:

index:起始位置

0:要删除的项数

item1...itemX:要插入的项

返回值:数组

替换数组项

语法:arrayObject.splice(index,count,item1,...itemX)

功能:在指定位置插入值,且同时删除任意数量的项

参数:

index:起始位置

count:要删除的项数

item1...itemX:要插入的项

返回值:从原始数组中删除的项(如果没有删除任何项,则返回空数组)

indexOf()

语法:arrayObject.indexOf(searchValue,startIndex)

功能:从数组的开头(位置0)开始向后查找

参数:

searchValue:必需,要查找的项

startIndex:可选,起点位置的索引

返回值:number,查找的项在数组中的位置,没有找到的情况下返回-1

lastIndexOf()

语法:arrayObject.lastIndexOf(searchValue,startIndex)

功能:从数组的末尾开始向前查找

参数:

searchValue:必需,要查找的项

startIndex:可选,起点位置的索引

返回值:number,查找的项在数组中的位置,没有找到的情况下返回-1

<script>
    var nums=[1,7,5,7,8,1,6,9];
    var pos=nums.indexOf(7);    //返回1
    var pos=nums.indexOf(7,2);    //返回3
    var pos=nums.lastIndexOf(1);    //返回5,因为离末尾最近的1所在的索引位置是5
</script>

说明

  • 在比较第一个参数与数组中的每一项时,会使用全等操作符,即要求查找的项必须严格相等。
  • 数组的位置方法时ECMAScript5位数组实例新增的,所以支持的浏览器有:IE9+、Firefox、Safari、Opera和Chrome

JavaScript对象之String

字符串检索的方法

charAt()

语法:stringObejct.charAt(index)

功能:返回stringObject中index位置的字符

说明:ECMAScript5中可使用“方括号加字符索引”来访问字符串中特定的字符,但是IE7及更早的浏览器会返回undefined

charCodeAt()

语法:stringObejct.charCodeAt(index)

功能:返回stringObject中index位置的字符编码

indexOf()

语法:stringObject.indexOf("o")

功能:从一个字符串中搜索给定的子字符串,返回子字符串第一次出现的位置

返回值:数值

说明:如果没有找到该子字符串,则返回-1

lastIndexOf()

语法:stringObject.lastIndexOf("o")

功能:从一个字符串中搜索给定的子字符串,返回子字符串最后一次出现 的位置

返回值:数值

说明:如果没有找到该子字符串,则返回-1

字符串截取方法

slice()

语法:stringObject.slice(start,end)

功能:截取子字符串

参数说明:

  • start(必需)指定子字符串的开始位置
  • end(可选)表示子字符串到哪里结束,end本身不在截取范围之内,省略时截取至字符串的末尾,即所截取的字符串的最后一位索引为end-1
  • 当参数为负数时,会将传入的负值与字符串的长度相加

substring()

说明:语法及功能与slice()完全一样

区别:当参数为负数时,自动将参数转换为0

substr()

语法:stringObject.substr(sstart,len)

功能:截取子字符串

参数说明:

  • start:必需,指定子字符串的开始位置
  • len:可选,表示截取的字符总数,省略时截取至字符串的末尾
  • 当start为负数时,会将传入的负值与字符串的长度相加
  • 当len为负数时,返回空字符串

字符串其它方法 

split()

语法:stringObject.split(separator)

功能:把一个字符串分割成字符串数组

返回值:Array

说明:separator:必需,分隔符

<script>
    var str='welcome-to-beijing';
    //使用split将str转换为数组
    var arr=str.split("-");    //打印["welcome","to","beijing"]
    var date='2016/05/05';    
    var dateArr=date.split("/");    //打印[2016,05,05]
</script>

replace()

语法:stringObject.replace(regexp/substr,replacement)

功能:在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串

返回值:String

参数

  • regexp:必需,规定子字符串或要替换的模式的RegExp对象
  • replacement:必需,一个字符串值

注意:replace不会改变原字符串,只会返回一个新的字符串,而且只替换第一个符合模式的子字符串

toUpperCase()

语法:stringObject.toUpperCase()

功能:把字符串转换为大写

toLowerCase()

语法:stringObject.toLowerCase()

功能:把字符串转换为小写

JavaScript之Math对象

Math对象的方法

min()

语法:Math.min(num1,num2,...numN)

功能:求一组数中的最小值

返回值:Number

注意:只要一组数中出现一个非数字,就会返回NaN

max()

语法:Math.max(num1,num2,...numN)

功能:求一组数中的最大值

返回值:Number

注意:只要一组数中出现一个非数字,就会返回NaN

ceil()

语法:Math.ceil(num)

功能:向上取整,即返回大于num的最小整数

返回值:Number

floor()

语法:Math.floor(num)

功能:向下取整,即返回小于num的最大整数

返回值:Number

round()

语法:Math.round(num)

功能:将数值四舍五入为最接近的整数

返回值:Number

abs()

语法:Math.abs(num)

功能:返回num的绝对值

返回值:Number

random()

语法:Math.random()

功能:返回大于等于0小于1的一个随机数

返回值:Number

求n到m之间的随机整数的公式

  • random=Math.floor(Math.random()*(m-n+1)+n);
  • random=Math.ceil(Math.random()*(m-n)+n);

JavaScript之Date对象

创建日期对象

Date()

语法:new Date()

功能:创建一个日期时间对象

返回值:不传参的情况下,返回当前的日期时间对象

获取日期时间

getFullYear():返回4位数的年份

getMonth():返回日期中的月份,返回值为0-11

getDate():返回月份中的天数

getDay():返回星期,返回值为0-6

getHours():返回小时

getMinutes():返回分

getSeconds():返回秒:

getTime():返回表示日期的毫秒数

语法:dateObject.getXXX()

设置日期时间

方法一:利用set函数

<script>
    // 创建一个日期时间对象
    var today=new Date();
    today.setFullYear(2017);
    today.setMonth(15);
    console.log(today.getFullYear());    //返回2017
</script>

方法二:创建对象时同时设置

<script>
    // 创建一个日期时间对象
    var date=new Date(year+1,month,day);    //年月日必须设定,时分秒可以不设定
</script>

说明

setMonth(month,day)

第一个参数month是必需的,取值在0-11,但具有强大的容错能力。比如,-1则为去年最后一个月,12则为明年第一个月;

第二个参数day是可选的,取值在1-31。同样具有强大的容错额能力。比如,若当月有31天,32则为下个月第一天,若当月只有30天,32则为下个月第二天。

错误调试与处理

语法错误

简单来说,语法错误就是不符合JS语法的错误。出现语法错误,控制台会进行报错并告知出错的行号(但行号不一定准确)。

常见语法错误

符号漏打、多打、少打、错打

使用了不合语法的变量名

语句写错,没写完等

运行时错误

指代码没有语法错误,而在运行时才发生的错误。运行时错误是一个统称。

常见语法错误

ReferenceError,变量引用异常出发

TypeError,类型使用错误时出发。如获取未初始化的变量的属性或方法;调用类型错误

RangeError,不太常见,一般会在递归爆栈时出发,即递归深度太深

如何区别语法错误与运行时错误

  • 语法错误无论如何都不可能运行成功,而运行时错误是有可能运行成功的
  • 也可以通过编译器的不同语法高亮来判断错误类型

逻辑错误

一般指的是计算结果不符合预期的错误

debugger的使用

可以使用单步跟踪调试,即在代码中加入debugger,代码运行到此处就会暂停,利用运行、跳过函数、进入函数、出函数等控制按钮以及watch窗口查看相关变量的值,也可以点击具体的行添加断点。前提是要把调试工具打开

try-catch语句

为了使程序更加健全,可以主动触发一些错误,并使用throw语句来抛出这个错误,throw语句捕捉到异常之后,会去寻找一个离自己最近的try-catch语句块。其中,catch捕获throw语句抛出的异常,并在catch语句中执行对该类异常的处理。而finally语句是无论有没有捕获到异常都会执行的,因此常用来做一些清理工作。如果try块内部代码抛出错误(浏览器自己抛出的),则直接跳入catch块运行,且把错误对象赋值给catch括号内的变量。

注意:

  • finally语句不管怎样都会运行
  • 无法包括语法错误代码块
  • 错误的冒泡

JS DOM操作

理解DOM

文档对象模型(DOM, Document Object Model)主要用于对HTML文档的内容进行操作。DOM把HTML文档表达成一个节点树,通过对节点进行操作,实现对文档内容的添加、删除、修改、查找等功能。

DOM节点

DOM节点就是HTML上所有的内容,包括:

  • 文档节点
  • 元素节点(标签)
  • 元素属性节点
  • 文本节点
  • 注释节点

创建节点

document.write()方法

只适用于将结点添加在body标签内所有节点前面

create系列方法

document.createElement()

参数是标签名

<script>
    myReady(function(){
    var ul=document.getElementById("myList");
    var li=document.createElement("li");
    ul.appendChild(li);
});
</script>
<head>
    <body>
        <ul id="myList"></ul>
    </body>
</head>
 
=========================================================================
<!--执行后查看网页源代码-->
<head>
    <body>
        <ul id="myList">
            <li></li>    <!--li作为子节点被装填到ul中-->
        </ul>
    </body>
</head>

document.createTextNode()

用于创建新的文本节点

<script>
    myReady(function(){
    var ul=document.getElementById("myList");
    var li=document.createElement("li");
    var txt=document.createTextNode("Item");
    li.appendChild(txt);
    ul.appendChild(li);
});
</script>
<head>
    <body>
        <ul id="myList"></ul>
    </body>
</head>
 
=========================================================================
<!--执行后查看网页源代码-->
<head>
    <body>
        <ul id="myList">
            <li>Item</li>    <!--txt的内容作为子节点被装填到li中-->
        </ul>
    </body>
</head>

document.createDocumentFragment()

创建新的文本片段

<script>
    myReady(function(){
    var fragment=document.createDocumentFragment();
    var ul=document.getElementById("myList");
    var li=null;
    for(var i=0;i<3;i++){
        li=document.createElement("li");    //创建li结点
        li.appendChild(document.createTextNode("Item"+(i+1)));    //向li结点装填文本内容
        fragment.appendChild(li);    //将li装填到fragment中
    }
    ul.appendChild(fragment);    //将文档片段装填到ul中
});
</script>
<head>
    <body>
        <ul id="myList"></ul>
    </body>
</head>
 
=========================================================================
<!--执行后查看网页源代码-->
<head>
    <body>
        <ul id="myList">
        <li>Item1</li>    <!--li作为子节点被装填到ul中-->
        <li>Item2</li>   
        <li>Item3</li>  
        </ul>
    </body>
</head>

document.createComment()

传入一个注释文本作为参数,就可以创建一个注释节点

<script>
    myReady(function(){
    var comment=document.createComment("A Comment");
    var fragment=document.createDocumentFragment();
    var ul=document.getElementById("myList");
    var li=null;
    for(var i=0;i<3;i++){
        li=document.createElement("li");    //创建li节点
        li.appendChild(document.createTextNode("Item"+(i+1)));    //向li节点装填文本内容
        fragment.appendChild(li);    //将li装填到fragment中
    }
    ul.appendChild(fragment);    //将文档片段装填到ul中
    document.body.insertBefore(comment,document.body.firstChild);    
    //第一个参数是要插入的节点,第二个参数是插入节点的位置,这里的document.body.firstChild是ul节点
});
</script>
<head>
    <body>
        <ul id="myList"></ul>
    </body>
</head>
 
=========================================================================
<!--执行后查看网页源代码-->
<head>
    <body>
        <!--A comment-->
        <ul id="myList">
        <li>Item1</li>    <!--li作为子节点被装填到ul中-->
        <li>Item2</li>   
        <li>Item3</li>  
        </ul>
    </body>
</head>

注意:document.createElement还能支持创建当前浏览器不支持的标签名,在IE6-8下,这是一个著名的hack

<head>
<style>
    /*html5*/
    article {
    font-size: 40px;
    color: red;
    }
</style>
<script src="domReady.js"></script>
<script> 
    (function() {
      //由于我们只希望这段代码在IE浏览器下执行,所以要加上下面这句话,
      //如果是IE浏览器就会执行注释掉的中间代码,即条件编译代码,返回true。其他浏览器不执行,返回false。
      if (! 
      /*@cc_on!@*/
      0) return;
      var e = "abbr, article, aside, audio, canvas, datalist, details, dialog, eventsource, figure, footer, header, hgroup, mark, menu, meter, nav, output, progress, section, time, video".split(', ');    //将h5新增的所有标签放到数组e里
      var i = e.length;
      while (i--){
        document.createElement(e[i]);    //创建这些新增的节点
      }
    })();
</script>
</head>
<body>
  <article>
      You are my sunshine.
  </article>
</body>

高效创建节点的方法

innerHTML

用来设置或获取当前标签起始和结束里面的内容

<head>
<script src="domReady.js"></script>
<script>
    myReady(function(){
    var content = document.getElementById("content");
    var str = "<p>This is a <strong>paragraph</strong> with a list following it.</p>"
                + "<ul>"
                + "<li>Item 1</li>"
                + "<li>Item 2</li>"
                + "<li>Item 3</li>"
                + "</ul>";
    content.innerHTML = str;
    alert(content.innerHTML);
   });
</script>
</head>
<body>
    <div id="content"></div>
</body>

限制:

  • 字符串的最左边不能出现空白,IE6-8会自动移除掉它
  • 大多数浏览器不会对script标签进行脚本执行操作
  • 不能单独创建meta、style、link等元素,一定要在前面加上一些字符

注意:

  1. 必须为script标签添加defer属性,设置了defer的元素意为在页面中看不到的元素,IE会在解析这个字符串前删除这个元素
  2. script标签必须要放在有作用域的元素之后
<head>
<!--为了使IE浏览器也能识别style标签应在script标签前添加节点-->    
<script src="domReady.js"></script>
<script>
    myReady(function(){
    var content = document.getElementById("content");
    var str = "<input type=\"hidden\"><script defer>alert('hi');<\/script>";
    <!--
        方法二:var str="<div>&nbsp;</div><script defer>alert('hi');<\/script>";
        但会出现一个"_",需要后续用dom删除
    -->
    <!--
        方法三:var str="_<script defer>alert('hi');<\/script>";
        但会出现一个" ",需要后续用dom删除
    -->
    content.innerHTML = str;
   });
</script>
</head>
<body>
  <div id="content"></div>
</body>

outerHTML

返回调用它的元素及所有子节点的HTML标签

兼容性:IE4+、Safari4+、Chrome、Opera8+、firefox8+

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var content = document.getElementById("content");
        console.log(content.outerHTML);    <!--outerHTML读模式-->
        <!--
            执行到此,打印
            <div id="content">
              <p>This is a <strong>paragraph</strong> with a list following it.</p>
              <ul>
                <li>Item 1</li>
                <li>Item 2</li>
                <li>Item 3</li>
              </ul>
            </div>
        -->
        content.outerHTML = "<p>This is a paragraph.</p>";    <!--outerHTML写模式-->
        <!--
            执行到此,打印<p>This is a paragraph.</p>
        -->
      });
    </script>
  </head>
  <body>
    <div id="content">
      <p>This is a <strong>paragraph</strong> with a list following it.</p>
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
      </ul>
    </div>
  </body>

innerText

设置或获取位于对象起始和结束标签内的文本

兼容性:IE4+、Safari3+、Opera8+、Chrome、firefox不支持(但是支持textContent属性)

注意:通过innerText设置的内容,即使是带了元素标签的,也会以文本的形式显示在网页中

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var content = document.getElementById("content");
        //一个任何浏览器都适用的获取innerText的方法
        function getInnerText(element){
          return typeof (element.textContent == "string") ? element.textContent : element.innerText;
        }
        //一个任何浏览器都适用的设置innerText的方法
        function setInnerText(element, text){
          if (typeof element.textContent == "string"){
            element.textContent = text;
          } else {
            element.innerText = text;
          }
        }
        setInnerText(content, "Hello world!");    //p标签内的文本内容会变成Hello world!
        console.log(getInnerText(content));
      });
    </script>
  </head>
  <body>
    <div id="content">
      <p>This is a <strong>paragraph</strong> with a list following it.</p>
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
      </ul>
    </div>
  </body>

outerText

outerText的读模式与innerText基本一眼,但写模式不同。

outerText不仅替换调用它的元素的子节点,还会替换整个元素,包括子节点。即如果content调用了这个方法去设置文本,这段文本会把包括content自身的所有内容替换掉。

兼容性:IE4+、Safari3+、Opera8+、Chrome,不建议使用该属性,因为它调用的元素可能不存在

节点遍历

DOM树

每个节点都有一个childNodes属性,其中保存着一个nodeList对象,这是一个类数组对象。因此我们可以通过childNodes[i]、childNodes.item(i)来获取节点的任意一个子节点。

注意:标签元素中的文本节点也是该标签元素的一个子节点

API1

API1

 

注意:使用这套API,用childNodes获取子节点,会把空白节点也算进来,因此要想办法把空白子节点去掉。

解决空白子节点

<head>
  <script src="domReady.js"></script>
  <script>
    myReady(function(){
      var box = document.getElementById("box");
      for(var i = 0, len = box.childNodes.length; i < len; i++) {
        if (box.childNodes[i].nodeType == 1)    //nodeType只有对元素节点才返回1
        {    
          console.log(box.childNodes[i]);
        }
      }
    });
  </script>
</head>
<body>
  <ul id="box">  
    <li>节点一</li>
    <li>节点二</li>
    <li>节点三</li>  
  </ul>
</body>

API2

说明:用API2中的children[i]访问到的都是元素节点,自然不会出现有空白节点的情况

类数组对象NodeList

特点

  • 是一种类数组对象,用于保存一组有序的节点
  • 可以通过方括号语法来访问NodeList的值,有item方法与length属性
  • 它并不是Array的实例,没有数组对象的方法
  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var box = document.getElementById("box");
        var nodes = box.childNodes;
        function makeArray(nodeList){
          var arr = null;
          //把类数组nodeList装填到真正的数组arr中
          try {
            return Array.prototype.slice.call(nodeList);    //如果没有出错就执行这行代码
          }catch (e){    //如果用IE浏览器执行上述代码会抛出异常,故执行以下代码
            arr = new Array();
            for(var i = 0, len = nodeList.length; i < len; i++){
              arr.push(nodeList[i]);
            }
          }
          return arr;
        }
        var newNodeList = makeArray(nodes);
        newNodeList.push("<li>节点四</li>");    //现在就可以用真正数组的push方法添加元素了
        console.log(newNodeList);
      });
    </script>
  </head>
  <body>
    <ul id="box">  
      <li>节点一</li>
      <li>节点二</li>
      <li>节点三</li>  
    </ul>
  </body>

类数组对象HTMLCollection

使用以下方法都会返回HTMLCollection对象

Ele.getElementsByTagName():根据节点名称返回一组元素集合

document.scripts:返回页面全部script元素的集合

documents.links: 返回页面全部a元素的集合

documents.images: 返回页面全部img元素的集合

documents.forms: 返回页面全部form元素的集合

tr.cell:返回该tr所有td子元素集合

select.options:返回该select的全部选项

<head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var scripts = document.scripts;
        var links = document.links;
        var cells = document.getElementById("tr").cells;
        var imgs = document.images;
        var forms = document.forms;
        var options = document.getElementById("select").options;
        var ps = document.getElementsByTagName("p");
 
        console.log(cells.namedItem('td'));    //获取cells集合中id名为td的所有元素中的第一个
      });
    </script>
  </head>
  <body>
    <ul id="box">
      <li>节点一</li>
      <li>节点二</li>
      <li>节点三</li>
    </ul>
 
    <table border="1">
      <tr id="tr">
        <td>第一行</td>
        <td name="td">第二行</td>
        <td name="td">第三行</td>
      </tr>
    </table>
 
    <img src="duer.jpg" alt="img1" />
    <img src="ipone6s+.png" alt="img2" />
 
    <form action="">
      <input type="text" value="用户名">
    </form>
    <form action="">
      <input type="text" value="密码">
    </form>
 
    <a href="#">忘记密码</a>
    <a href="#">更多内容</a>
 
    <select id="select">
      <option value="0">北京</option>
      <option value="1">天津</option>
      <option value="2">河北</option>
    </select>
 
    <p>DOM探索之基础详解篇</p>
    <p>DOM探索之节点操作篇</p>
  </body>

类数组对象NamedNodeMap

使用以下方法都会返回HTMLCollection对象

Ele.attributes

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var box = document.getElementById("box");
        var attrs = box.attributes;
        console.log(attrs);    //打印{0:id,1:data-url,2:node-action}
        console.log(attrs.length);    //3
        console.log(attrs[0]);    //id
        console.log(attrs.item(1));    //date-url
      });
    </script>
  </head>
  <body>
    <ul id="box" data-url="index.html" node-action="submit">
      <li>节点一</li>
      <li>节点二</li>
      <li>节点三</li>
    </ul>
  </body>

类数组对象的动态性 

NodeList、HTMLCollection、NamedNodeMap三个集合都是动态的,是有生命、有呼吸的对象

它们实际上是基于DOM结构动态执行查询的结果,因此DOM结构的变态能够自动反映到这些对象中

每当文档结构发生变化时,它们都会得到更新。因此,它们始终都会保存着最新、最准确的信息

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var divs = document.getElementsByTagName("div");
        var i = 0;
        while(i < divs.length){
          document.body.appendChild(document.createElement("div"));
          i++;
        }
      });
    </script>
  </head>
  <body>
    <div></div>
    <div></div>
    <div></div>
  </body>
 
<!--
    程序会陷入死循环,原因是随着div元素的增加,divs.length也会跟着更新,
    而i又每次都加1,因此i<divs.length这个条件永远不会成立,充分说明了类数组对象具有动态性,
    为了解决这个问题,只需要事先将divs.length保存在一个变量中,再用i与这个变量比较
-->

获取节点

getElementById

通过某个元素的id名找到该元素

注意:

不能使用非document对象来调用这个方法,会报错

在IE浏览器中调用此方法,会把name值和目标id值相同的元素也获取到,又因为调用此方法只返回符合条件的第一个元素,因此如果name和id相同,且name位于id之前,就会获取到与目标不一致的元素

解决getElementById()的bug

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var getElementById = function(id) {
          var el = document.getElementById(id);
 
        //IE浏览器中"\v"没有转义,而其他浏览器解析为垂直制表符,
        //所以IE浏览器解析的"\v1"就等于字符串"1",在前面加上"+"就是为了将 +"1" 转化成NaN,
        //最前面的"!"表示取反,故如果返回true,就是IE浏览器,
 
          if(!+"\v1") {
            if(el && el.id === id) {    //要判断获得的元素的id名与函数参数是否一致,
                                        //若不一致,再去找到第一个id名与函数参数一致的元素。
              return el;
            } else {
              var els = document.all[id],
                  n = els.length;
              for (var i = 0; i < n; i++) {
                if (els[i].id === id) {
                  return els[i];
                }
              }
            }
          }
          return el;
        };
        console.log(getElementById("target").tagName);
      });
    </script>
  </head>
  <body>
    <a name="target" href="http://www.cnblogs.com/rubylouvre/">这是错误的元素</a>
    <p id="target">这是正确的元素</p>
  </body>

getElementsByName()

页面中,id属性是唯一的, name属性不唯一,一个值的name可以在多个元素上定义。

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myUl = document.getElementsByName('myUl');
        var myDiv = document.getElementById('myDiv');
        var myUl = myDiv.getElementsByName('myUl');    //报错
        var citys = document.getElementsByName('city');
        console.log(myUl);
        console.log(myDiv);
        console.log(citys.length);    //打印3
      });
    </script>
  </head>
  <body>
    北京<input type="checkbox" id="beijing" name="city" value="北京" />
    天津<input type="checkbox" name="city" value="天津" />
    上海<input type="checkbox" name="city" value="上海" />
    <div id="myDiv">
      You are my sunshine.
      <ul id="myUl" name="myUl">
        <li>1</li>
        <li>2</li>
        <li>3</li>
      </ul>
    </div>
  </body>

getElementByTagName()

通过标签元素名称来获取元素

<!DOCTYPE html>
<html>
  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var lis1 = document.getElementsByTagName('li');
        var lis2 = document.getElementsByTagName('Li');    //大小写不敏感
        var inputs = document.getElementsByTagName('input');
        var comment = document.getElementsByTagName('!');    //获取注释内容
        var all = document.getElementsByTagName('*');
        console.log(lis1.length);    //3
        console.log(lis2.length);    //3
        console.log(inputs[0].value);    //打印“请输入内容”
        console.log(comment[0].nodeValue);    //DOCTYPE html
        console.log(comment[1].nodeValue);    //这才是真正的注释内容
        for(var i = 0, len = all.length; i < len; i++) {
        console.log(all[i].tagName);
        }
      });
    </script>
  </head>
  <body>
    <!--页面元素-->
    <input type="text" name="myDiv" value="请输入内容" />
    <div id="myDiv">
      You are my sunshine.
      <ul id="myUl">
        <li>1</li>
        <li>2</li>
        <li>3</li>
      </ul>
    </div>
  </body>
</html>

getElementsByClassName()

此方法需要在较新的浏览器上才能使用,通过class名查找元素

兼容性:IE9+、chrome、Safari4+、Firefox3+

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var ul = document.getElementById('myUl');
        var lis1 = ul.getElementsByClassName('light');
        var lis2 = ul.getElementsByClassName('light dark');
        console.log(lis1);
        console.log(lis2);
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li class="light">1</li>
      <li class="dark light">2</li>    <!--不区分次序,一样能查找到-->
      <li class="light">3</li>
    </ul>
  </body>

解决getElementByClass()兼容性 

getElementByClassName.js

改进目标

  • 类名可以在文档的任何位置进行查找
  • 根据标签名过滤一些元素
  • 做到兼容
  • 支持该属性的直接使用
var getElementsByClassName = function(opts) {
	var searchClass = opts.searchClass; // 存储要查找的类名
	var node = opts.node || document;  // 存储要出查找的范围
	var tag = opts.tag || '*'; // 存储一定范围内要查找的标签
	var result = [];
		// 判断浏览器支不支持getElementsByClassName方法
	if (document.getElementsByClassName) { // 如果浏览器支持
		var nodes = node.getElementsByClassName(searchClass);
		if (tag !== "*") {
			for (var i = 0; node = nodes[i++];) {
				if (node.tagName === tag.toUpperCase()) {
					result.push(node);
				}
			}
		} else { 
			result = nodes;
		}
		return result;
	} else { // 使用IE8以下的浏览器能够支持该属性
		var els = node.getElementsByTagName(tag);
		var elsLen = els.length;
		var i, j;
		var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
		for (i = 0, j = 0; i < elsLen; i++) {
			if (pattern.test(els[i].className)) { // 检测正则表达式
				result[j] = els[i];
				j++;
			}
		}
		return result;
	}
}

getElementByClassName-Fixed.html

  <head>
    <script src="domReady.js"></script>
    <script src="getElementsByClassName.js"></script>
    <script>
      myReady(function(){
        var myUl2 = document.getElementById("myUl2");
        var r = getElementsByClassName({
          searchClass: "light dark",
          node: myUl2
        });
        console.log(r[0].innerHTML);
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li class="light">1</li>
      <li class="light dark">2</li>
      <li class="light">3</li>
    </ul>
    <ul id="myUl2">
      <li class="light">1</li>
      <li class="light dark">second</li>
      <li class="light">3</li>
    </ul>
  </body>

querySelector()

返回文档中匹配指定CSS选择器的一个元素

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myDiv = document.getElementById('myDiv');
        //var ul = myDiv.querySelector('#myUl');    //返回id为myUl的元素
        var ul = document.querySelector('#myUl');    //返回id为myUl的元素
        var li = myDiv.querySelector('li:last-child');    //返回li的父元素的最后一个子元素,即<li>3</li>
        var els = document.querySelector('input, li');    //返回input元素,因为querySelector只返回一个对象
        var span = document.querySelector('span');    //返回null
        var input = document.querySelector('.foo\\:bar');    //这里的\\:是转义字符,返回class=foo:bar的元素
      });
    </script>
  </head>
  <body>
    <input type="text" class="foo:bar" value="请输入内容" />
    <div id="myDiv">
      You are my sunshine.
      <ul id="myUl">
        <li>1</li>
        <li>2</li>
        <li>3</li>
      </ul>
    </div>
  </body>

querySelectorAll()

返回文档中匹配指定CSS选择器的一个元素

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myDiv = document.getElementById('myDiv');    //返回id为myDiv的所有元素
        var ul = myDiv.querySelectorAll('ul');    //返回一个集合[ul#myUl]
        var li = myDiv.querySelectorAll('li');    //返回一个集合[li,li,li]
        var span = myDiv.querySelectorAll('span');    //返回一个空数组[]
      });
    </script>
  </head>
  <body>
    <input type="text" name="myDiv" value="请输入内容" />
    <div id="myDiv">
      You are my sunshine.
      <ul id="myUl">
        <li>1</li>
        <li>2</li>
        <li>3</li>
      </ul>
    </div>
  </body>

兼容性:主流浏览器基本都支持该属性,IE8以下不支持 

操作节点

appendChild()

为指定元素节点的最后一个子节点之后添加节点,该方法返回新的子节点

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myUl = document.getElementById('myUl');
        var txt = document.createTextNode('4');
        var li = document.createElement('li');
        var firstLi = myUl.firstElementChild;    //返回<li>1</li>
        li.appendChild(txt);    
        myUl.appendChild(li);    //将<li>4</li>追加到ul元素的最后一个
        var returnedNode = myUl.appendChild(firstLi);    //将<li>1</li>追加到ul元素的最后一个子元素后面
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

insertBefore()

在指定的已有子节点之前插入新的子节点,该方法返回新的子节点

参数:要插入的新节点,参照节点(新节点插入到此节点之前)

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myUl = document.getElementById('myUl');
        var txt = document.createTextNode('4');
        var liNew = document.createElement('li');
        liNew.appendChild(txt);
        var li2 = myUl.children.item(1);    //获取<li>2</li>节点
        var returnNode = myUl.insertBefore(liNew, li2);    //两个参数,一个是我们要插入的新节点,一个是参照节点,函数的返回值是要插入的新节点
        myUl.insertBefore(liNew, null);    //等效于appendChild,在myUl的最后一个元素后面插入子节点
        myUl.insertBefore(liNew, myUl.firstElementChild);    //在<li>1</li>之前插入新节点
        myUl.insertBefore(liNew, myUl.lastElementChild);    //在<li>4</li>之前插入新节点
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

replaceChild()

该方法用新节点替换某个子节点,返回被替换的节点

参数:要插入的节点,要被替换的节点

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myUl = document.getElementById('myUl');
        var txt = document.createTextNode('4');
        var liNew = document.createElement('li');
        liNew.appendChild(txt);
        var li2 = myUl.children.item(1);    //获取<li>2</li>
        var returnNode = myUl.replaceChild(liNew, li2);    //返回<li>2</li>
        console.log(returnNode);    
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

cloneNode()

创建节点的拷贝,并返回该副本。

拷贝的节点要有父节点,如果没有父节点,要通过appendChild()、insertBefore()、replaceChild()等方法对其进行添加

参数:true,进行深度复制,赋值该节点及其子节点。false,进行浅复制,只复制该节点

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myUl = document.getElementById('myUl');
        var newNode = myUl.cloneNode(true);    //进行深复制,返回一个id为myUl的元素的副本
        console.log(newNode);
        document.body.appendChild(newNode);
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

normalize()

合并相邻的Text节点

  <head>
    <script>
      myReady(function(){
        var div = document.createElement("div");
        var textNode = document.createTextNode("DOM探索");
        div.appendChild(textNode);
 
        var textNode2 = document.createTextNode("之节点操作篇");
        div.appendChild(textNode2);
        document.body.appendChild(div);
 
        console.log(div.childNodes.length);    //打印2
        div.normalize();    //合并两个相邻的div元素
        console.log(div.childNodes.length);    //合并成功,打印1
        console.log(div.firstChild.nodeValue);    //返回div第一个子节点中的文本内容
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

splitText()

按照指定的位置把文本节点分割为两个节点,返回新的文本节点

参数:分割的位置,从0开始

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var div = document.createElement("div");
        var textNode = document.createTextNode("DOM探索之节点操作篇");
        div.appendChild(textNode);
        document.body.appendChild(div);
 
        var newNode = div.firstChild.splitText(5);    //分割位置是5,即“索”子后面
        console.log(div.firstChild.nodeValue);    //打印“DOM探索”
        console.log(newNode.nodeValue);    //新节点,即被分割出来的节点,打印“之节点操作篇”
        console.log(div.childNodes.length);    //打印2,分割成两个元素
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

删除节点

removeChild()

删除某个节点下面的子节点,不能不传入参数

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myUl = document.getElementById('myUl');
        console.log(myUl.childNodes.length);    //3
        var secondChild = myUl.removeChild(myUl.childNodes[1]);    //删除<li>2</li>
        console.log(secondChild);    //此时ul的第二个子节点为<li>3</li>
        console.log(myUl.childNodes.length);    //2
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

removeNode()

IE的私有实现,将目标节点从文档中删除,返回目标节点

参数:布尔值,默认值是false,表示仅删除此节点,保留子节点。如果是true,就和removeChild()类似

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var myUl = document.getElementById('myUl');
        var removedNode = myUl.removeNode(true);    //删除元素及其子节点
        console.log(removedNode.outerHTML);    //返回<ul id="myUl"><li>1</li><li>2</li》<li>3</li></ul>
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

innerHTML()

将元素装填入DOM树后,再立刻用removeChild()删除这个元素,在Chrome浏览器上,该被删节点的父节点返回值为null,若要获取它的父节点的类型自然会报错。而在IE浏览器上,该被删节点的父节点返回HTMLDocument对象,节点类型是11,这样就可以重复使用被删除的元素。但在实际情况中,用户往往不会去使用这些被删除的元素,久而久之会多出很多碎片,造成内存泄漏。而用innerHTML删除子元素后,返回被删除的子元素的父节点,无论在IE或者Chrome下都是null。用removeChild()和innerHTML方法删除子元素时,在Chrome下还能获取被删除元素的内容,而在IE下只能返回第一个被删除的子元素的内容。

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var div = document.createElement("div");
        console.log(div.parentNode);
        document.body.removeChild(document.body.appendChild(div));
 
        console.log(div.parentNode);
        console.log(div.parentNode.nodeType);
 
        var myUl = document.getElementById('myUl');
        document.body.innerHTML = '';
        console.log(myUl.parentNode);
 
        myUl.parentNode.removeChild(myUl);
        console.log(myUl.id + ":" + myUl.innerHTML);
        var myUl2 = document.getElementById('myUl2');
        myUl2.parentNode.innerHTML = '';
        console.log(myUl2.id + ":" + myUl2.innerHTML);
      });
    </script>
  </head>
  <body>
    <ul id="myUl">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
    <ul id="myUl2">
      <li>1</li>
      <li>2</li>
      <li>3</li>
    </ul>
  </body>

总结:在IE6~8下,removeChild()相当于掰断树枝但还可以再次使用,innerHTML就是把树枝拔下来再烧掉。而对于Chrome,这两种方法都是掰断树枝但还可以再次使用。

JS DOM属性

属性的分类

Property固有属性

浏览器已经绑定好的属性叫固有属性

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var a = document.getElementById('txt');
        console.log(a.type);    //打印text
        console.log(a.id);    //打印txt
        console.log(a.a);    //打印undefined,因为不是固有属性
        console.log(a.title);    //打印空字符串,因为没赋值,默认为空
      });
    </script>
  </head>
  <body>
    <input type="text" id="txt" a="b" />
  </body>

Attribute自定义属性

如果定义同名属性,后面定义的属性不会生效,只保留第一个拥有该名称的属性,属性名大小写不敏感

不能通过ele.property的方法获取自定义属性,可以通过ele.attributes.getNamedItem('xxx').nodeValue获取自定义属性

而如果固有属性没有在元素内显式定义,就不能用getNamedItem()的方法来获取它的属性

因此getNamedItan()方法只能和显式定义的属性对应

此外,用attributes['xx']方法(其中xx为属性名),也可以获得该属性

使用setNamedItem可以创建一个属性,但它的参数是一个属性对象,即要先创建一个属性对象,再对它进行赋值。

创建属性的方法是attr=document.createAttribute('xxx')

给属性赋值的方法是attr.value='yyy'

将属性装载到元素上的方法就是ele.attributes.setNamedItem(attr)

用removeNamedItem可以将自定义属性删除,但不能删除固有属性

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var div = document.querySelector('div');
        console.log(div.xxx);    //报错,不能通过这种方法获取自定义属性
        console.log(div.attributes.getNamedItem('xxx').nodeValue);    //打印aaa
        console.log(div.nodeName);    //打印div0
        console.log(div.attributes.getNamedItem('nodeName').nodeValue);    //报错
        console.log(div.attributes.getNamedItem('id').nodeValue);    //打印div0
        console.log(div.attributes['id'].nodeValue);    //打印div0
        div.attributes.removeNamedItem('yyy');    //移除属性yyy
        div.attributes.setNamedItem('data-title', 'ddd');    //用这种方法创建属性并赋值是错误的
 
        var attr = document.createAttribute('data-title');    //应先创建属性
        attr.value = 'ddd';    //再给属性赋值
        div.attributes.setNamedItem(attr);    //再装载到元素中
      });
    </script>
  </head>
  <body>
    <div id="div0" url="http://www.baidu.com" xxx="aaa" yyy="bbb"></div>
  </body>

布尔属性

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var inputs = document.querySelectorAll("input");
        inputs[0].checked = 1;    //实际上checked属性的值只要能转化为truw就能使对应的复选框被选中
        inputs[0].removeAttribute('checked');    //用removeAttribute()无法移除固有属性
      });
    </script>
  </head>
  <body>
    你去过的城市:<br/>
    <input type="checkbox">北京
    <input type="checkbox" checked="checked">香港    //选中
    <input type="checkbox" checked>纽约    //选中
  </body>
  • checked属性用来设置单选框和复选框的默认选中值,selected用来设置选项卡中默认选中值,multiple用来使选项卡实现多选的功能。
  • disabled与readOnly的相同点在于都无法对文本进行编辑,仅可读,区别在于提交表单时,设置了disabled属性的元素值不会提交,而设置了readOnly属性的元素的值仍然会提交。
  • hidden属性是h5中新增的属性,可以隐藏一个元素,且在页面中不占用位置。

字符串属性

id、title、href、src、lang(辅助搜索引擎、打印机、扩音器使用)、dir(文本的输出方向)、accesskey(组合键属性,用快捷键选中某元素)、name、value、class

所有HTML元素都拥有的全局属性有:accesskey、class、contenteditable(是否可编辑元素的内容)、dir、hidden、id、lang、spellcheck(检查拼写是否符合某个语法)、style、tabindex(规定当使用"tab"键进行导航时元素的顺序)、title、translate(规定是否应该翻译元素内容,目前所有主流浏览器都无法正确支持该属性)

  <head>
    <link rel="stylesheet" href="style.css">
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var p = document.getElementById('sentence');
        alert(p.innerHTML);    <!--打印 寒雨连江夜入吴, -->
        var author = document.getElementById('author');
        author.className = 'light';    <!--给author对象设置className为light-->
      });
    </script>
  </head>
  <body>
    <p style="text-align:center" id="title" class="large"><bdo dir="RTL">芙蓉楼送辛渐</bdo></p>
    <p style="text-align:center" id="author"><bdo dir="RTL">王昌龄</bdo></p>    <!--文字输出方向是右到左-->
    <p style="text-align:center" id="sentence"><bdo dir="RTL">寒雨连江夜入吴,</bdo></p>
    <p style="text-align:center" id="sentence"><bdo dir="RTL">平明送客楚山孤。</bdo></p>
    <p style="text-align:center" title="洛阳亲友如相问,"><bdo dir="RTL">洛阳亲友如相问,</bdo></p>
    <p style="text-align:center" title="一片冰心在玉壶。"><bdo dir="RTL">一片冰心在玉壶。</bdo></p>
    <a href="http://www.diyifanwen.com/sicijianshang/tangshisanbaishou/">更多</a>
    <img src="x.jpg" alt="欧美小孩">
    <input type="text" name="txt1" value="tab1" accesskey="a" />    <!--在浏览器中按Alt+a可选中该文本框-->
    <input type="text" name="txt2" value="tab2" accesskey="s" />    <!--在浏览器中按Alt+s可选中该文本框-->
  </body>

data属性

近年来引入的新属性,以data-开头,用于存储页面或程序私有定义的数据

  <head>
    <script src="domReady.js"></script>
    <script>
      myReady(function(){
        var btn = document.getElementById('btn');
        console.log(btn.dataset.toggle);    //打印modal
        console.log(btn.dataset.xxxYyy);    //打印abc
      });
    </script>
  </head>
  <body>
    <button id="btn" type="button" data-xxx-yyy="abc" data-toggle="modal">Small modal</button>
  </body>

class属性 

可以定义某个元素的样式,它的值是以空格分割的多个字符串,因此一个class可以关联多个css类,它不是一对一的操作。class属性一样有增删改查的需求。

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>class attributes</title>
    <script src="domReady.js"></script>
    <script>
      var CC = {
        getClass: function(ele) {
          return ele.className.replace(/\s+/, " ").split(" ");    
 
        },
        //正则表达式中,\s:空格,+:一个或多个。
        //匹配一个或多个空格,并用一个空格替换,再以一个空格为分隔符将class名存进数组中
 
        hasClass: function(ele, cls) {
          return -1 < (" " + ele.className + " ").indexOf(" " + cls + " ");
        },
        //看返回结果是否是-1,若非-1,则说明成功获取到某个class名的位置,即查找到某个class名
        //在ele.className前后都加上空格是为了去掉收尾没有空格的情况,便于按照统一规则来查找,即" cls "的形式
 
        addClass: function(ele, cls) {
          if (!this.hasClass(ele, cls))
            ele.className += " " + cls;
        },
        //如果在class名字符串中找不到目标,便可以往里面追加一个新的class名
        //要记住在名字前面加上空格,以防原有的class名字符串的末尾没有空格,会致使两个class名连接在一起
 
        removeClass: function(ele, cls) {
          if (this.hasClass(ele, cls)) {
            var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)', "gi");
            ele.className = ele.className.replace(reg, " ");
          }
        },
        //在正则表达式中,\s:空格,^:开头无字符,$:结尾无字符,gi:整串字符
        //如果目标class名存在于class名字符串中,按照正式则来匹配,把前后不带或带有空格的class名作为一个完整的字符串保存到reg中,再替换为一个空格存到原来的class名字符串中
 
        toggleClass: function(ele, cls) {
          if (this.hasClass(ele, cls)) {
            this.removeClass(ele, cls);
          } else {
            this.addClass(ele, cls);
          }
        }
      };
        //若class名存在,就删除,否则添加新的class名
 
      myReady(function() {
        var body = document.body;
        console.log(CC.getClass(body));
        console.log(CC.hasClass(body, 'bbb'));
        CC.addClass(body, 'ccc');
        CC.removeClass(body, 'aaa');
        CC.toggleClass(body, 'bbb');
      });
    </script>
  </head>
  <body class="aaa bbb aaa">
      TEST
  </body>

其实,用classList来调用已经封装好的方法就可以实现和上述我们自己写的方法一样的功能。

添加class名:ele.classList.add(cls);

删除class名:ele.classList.remove(cls);

判断class名是否存在:ele.classList.contains(cls);

切换(有则删除,无则添加):ele.classList.toggle(cls);

JS事件

事件就是文档或浏览器窗口中发生的一些特定的交互瞬间,是可以被JavaScript侦测到的行为,当用户与web页面进行某些交互时,解释器就会创建响应event对象以描述事件信息。

事件句柄

事件句柄(又称事件处理函数、事件监听函数),指用于响应某个事件而调用的函数。每一个事件均对应一个事件句柄,当程序执行时,将相应的函数或语句指定给事件句柄,则在该事件发生时,浏览器便执行指定的函数或语句。

事件定义

三要素:1、事件对象2、 事件对象绑定一个事件类型3、事件句柄

为特定事件定义监听函数有三种方式:

HTML事件

直接在HTML中定义元素的事件相关属性,执行脚本。

<button onclick="alert('hello')">按钮</button>
<body onload="init()">...</body>

语法:<tag 事件="执行脚本">

功能:在HTML元素上绑定事件。

说明:执行脚本可以是一个函数的调用

缺点:违反了“内容与行为相分离”的原则,不利于代码复用,应尽可能少用。

<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style>
       .btn{width:140px;height:30px;line-height: 30px;background:#00f;
       	color:#fff;font-size:14px;text-align:center;border-radius:5px;
       	cursor:pointer;margin-top:30px;}
	</style>
</head>
<body>
	<input type="button" value="弹 出" onclick="alert('我是按钮')">
	<!--鼠标划过按钮时调用mouseoverFn的函数-->
	<div class="btn" onmouseover="mouseoverFn(this,'#f00')" onmouseout="mouseoutFn(this,'#ff0')">开始</div>
    //this指向函数所绑定的元素,在函数的定义中,要用一个参数来接收这个this
	<div class="btn" onmouseover="mouseoverFn(this,'#0f0')" onmouseout="mouseoutFn(this,'#333')">结束</div>
	<script>
        //这里用btn接收this指向的元素
        function mouseoverFn(btn,bgColor){
        	// 鼠标划过按钮时,按钮的背景变为红色
            btn.style.background=bgColor;
        }
        function mouseoutFn(btn,bgColor){
            btn.style.background=bgColor;
        }
	</script>
</body>

HTML脚本执行顺序是从上到下的,当浏览器解析到script时,就会去执行里面的脚本,如果这时候要获取一个定义在下面代码的元素,是获取不到的,会报错。name为了让script可以写到head标签中,并且不会不会因为元素定义在其后而报错,就要用到onload事件,它会在页面加载完之后触发,即页面加载完所有结构、文字、图片、二进制文件等之后,才会触发这个事件。但如果脚本放在最后,就不需要用window.onload

<head>
	<script>
	   // 页面加载时执行,unload页面卸载
       window.onload=function(){
       	 // 获取box
       	 var box=document.getElementById("box");
       	 var clicked=function(){
       	 	  alert('我被点击了');
       	 }
       	 box.onclick=clicked;
       }
	</script>
</head>
<body>
	<div id="box">这是一个DIV</div>
</body>

onfocus()和onblur()事件

<head>
	<style>
       .box{
       	  padding:50px;
       }
       .left,.tip{
       	  float:left;
       }
       .left{margin-right:10px;}
       .tip{display:none;font-size:14px;}
	</style>
	<script>
       window.onload=function(){
       	  // 获取文本框和提示框
       	  var phone=document.getElementById("phone"),
              tip=document.getElementById("tip");
          // 给文本框绑定激活的事件
          phone.onfocus=function(){
          	  // 让tip显示出来
          	  tip.style.display='block';
          }
          // 给文本框绑定失去焦点的事件
          phone.onblur=function(){
             // 获取文本框的值,value用于获取表单元素的值
             var phoneVal=this.value;
             // 判断手机号码是否是11位的数字
             // 如果输入正确,则显示对号图标,否则显示错号图标
             if(phoneVal.length==11 && isNaN(phoneVal)==false){
                tip.innerHTML='<img src="img/right.png">';
             }else{
             	tip.innerHTML='<img src="img/error.png">';
             }
          }
       }
	</script>
</head>
<body>
	<div class="box">
		<div class="left">
			<input type="text" id="phone" placeholder="请输入手机号码">
		</div>
		<div class="tip" id="tip">
           请输入有效的手机号码
		</div>
	</div>
</body>

onkeydown()和onkeypress()事件

onkeydown():在用户按下一个键盘按键时发生

onkeypress():在按下键盘按键时发生(只会响应字母与数字符号)

onkeyup():在键盘按键被松开时发生

keyCode():返回onkeypress、onkeydown或onkeyup事件触发的键的值,或按键的键码

<head>
	<style>
       .text span{font-weight:bold;color:#f00;}
       em{font-style:normal;}
       b{font-weight:normal;}
	</style>
</head>
<body>
	<div>
		<p class="text">
			<b id="showcount">您还可以输入</b>
			<span id="totalbox"><em id="count">30</em>/30</span>
		</p>
		<div class="input">
			<textarea name="" id="text" cols="70" rows="4"></textarea>
		</div>
	</div>
	<script>
	   // 获取文本框及其他元素
       var text=document.getElementById("text");
       var total=30;
       var count=document.getElementById("count");
       var showcount=document.getElementById("showcount");
       var totalbox=document.getElementById("totalbox");
	   // 绑定键盘事件
	   document.onkeyup=function(){
	   	  // 获取文本框值的长度
	   	  var len=text.value.length;
	   	  // 计算可输入的剩余字符
	   	  var allow=total-len;
	   	  var overflow=len-total;
	   	  // 如果allow小于0
	   	  if(allow<0){
              showcount.innerHTML="您已超出"+overflow;
              totalbox.innerHTML='';
	   	  }else{
	   	  	  showcount.innerHTML='您还可以输入';
	   	  	  totalbox.innerHTML='<em id="count">'+allow+'</em>/30';
	   	  }
	   }
	</script>
</body>

DOM0级事件

在JavaScript中为元素的事件相关属性赋值:

document.getElementById("btn").onclick=function(){//...}
document.body.onload=init;
function init(){//...}

缺点:此语法实现了“内容与行为相分离”,但元素仍只能绑定一个监听函数,即新绑定的事件会覆盖前面绑定的事件

DOM2级事件(事件委托)

高级事件处理方式,一个事件可以绑定多个监听函数,用addEventListener去绑定事件,第一个参数是事件类型,第二个参数是事件句柄,第三个参数管理事件捕获:

btn.addEventListener("click",function(){},false);    //DOM
btn.attachEvent("onclick",function(){});    //IE,第三个参数不写时默认为false
document.body.addEventListener("load",init);
document.body.attachEvent("onload",init);
function init(){//...}

此语法可以为一个元素绑定多个监听函数, 但需要注意浏览器兼容性问题

优点:松耦合,绑定多个事件,事件捕获和事件冒泡

DOM事件流

添加事件:addEventListener()

语法:element.addEventListener(event,function,useCapture)

功能:用于向指定元素添加事件句柄

参数:

event:必须。字符串,指定事件名。

function:必须。指定要事件触发时执行的函数

useCapture:可选。布尔值,指定事件是否在捕获或冒泡阶段执行

解绑事件:removeEventListener()

语法:element.removeEventListener(event,function,useCapture)

功能:移除addEventListener()方法添加的事件句柄

参数:

event:必须。字符串,要移除的事件名

function:必须。指定要移除的函数(addEventListener()中的函数不能是匿名函数)

useCapture:可选。布尔值,指定移除事件句柄对的阶段

说明:事件解绑成功的原因就在于保持addxx和removexx里面的额参数都是一致的,但是function中不能是匿名函数,否则就算函数体一模一样,这两个函数也不相等

IE事件流

添加事件:attachEvent()

语法:element.attachEvent(event,function)

功能:用于向指定元素添加事件句柄

参数:

event:必须。字符串,指定事件名,必须加“on”前缀

function:必须。指定要事件触发时执行的函数

说明:IE浏览器默认是冒泡,不需要加事件阶段属性

解绑事件:detachEvent()

语法:element.detachEvent(event,function)

功能:移除attachEvent()方法添加的事件句柄

参数:

event:必须。字符串,要移除的事件名(attachEvent()中的函数不能是匿名函数)

function:必须。指定要移除的函数

跨浏览器事件处理程序

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>跨浏览器事件处理程序</title>
	</head>
	<body>
		<button id="myBtn">点击我</button>
		<script type="text/javascript">
			var EventUtil = {
				addHandler:function(element,type,handler){
					//绑定事件
					//Chrome Firefox IE9等     addEventListener 
					//IE8及IE8以下的浏览器     attachEvent 
					if(element.addEventListener){
						element.addEventListener(type,handler,false);
					}else if (element.attachEvent) {
						element.attachEvent("on"+ type,handler);
					} else{
						element["on"+type] = handler 
					}
				},
				removeHandler: function(element,type,handler){
					//移除事件
					//Chrome Firefox IE9等      
					//IE8及IE8以下的浏览器     
					if(element.removeEventListener){
						element.removeEventListener(type,handler,false);
					}else if (element.detachEvent) {
						element.detachEvent("on"+type,handler);
					} else{
						element["on"+type] = handler
					}
				}
			}
			var btn = document.getElementById("myBtn");
			var handler = function(){
				alert("Clicked");
			}
			EventUtil.addHandler(btn,"click",handler);
		    //EventUtil.removeHandler(btn,"click",handler);
		</script>
	</body>
</html>

常用事件类型

UI事件

onload:当页面完全加载后在window上面触发

onunload:切换页面时触发

onresize:窗口大小发生改变时触发

onscroll:滚动滚动轮时触发

鼠标事件

onclick:鼠标点击时触发

ondblclick:鼠标双击时触发

onmouseover:鼠标滑过目标元素或其子元素时触发

onmouseout:鼠标离开目标元素或其子元素时触发

onmouseenter:只能进入目标元素时才触发

onmouseleave:只能离开目标元素时才触发

onmousedown:鼠标按钮被按下

onmousemove:鼠标被移动

onmouseup:鼠标按键被松开

onfocus:获得焦点时触发(仅用于input标签type为text、password以及textarea标签)

onblur:失去焦点时触发

onchange:域的内容改变时发生

键盘事件

onkeydown:按下任意键触发,支持keyCode

onkeyup:释放任意键触发,支持keyCode

onkeypress:按下并释放字符键触发,支持charCode,返回ASCII码

DOM事件

DOMNodeRemoved:元素中任意元素被删除就会触发

DOMSubtreeModified:DOM结构中发生任何变化都会触发

DOMNodeRemovedFromDocument:从文档中移除之前被触发

DOMNodeInserted:元素中任意元素被添加就会触发

DOMNodeInsertedIntoDocument:从文档中添加之前被触发

DOMContentLoaded:在DOM树之后就会触发,不理会图像、JavaScript文件、css文件或者其他资源是否已经下载,因此速度一定快于load事件

haschange:一定给window添加,#号后面的值变化

移动端常用事件

ontouch:手指芥末屏幕时触发

ontouchmove:手指在屏幕上滑动时触发,注意代码量不要很复杂,否则容易崩溃

ontouchend:手指从屏幕上移开时触发

ontouchcancel:当系统停止跟踪时触发

<body>
        <button id="mybtn">点击我</button>
        <script type="text/javascript">
                var EventUtil = {
                        addHandler: function(element, type, handler) {
                                //绑定事件
                                //Chrome Firefox IE9等     addEventListener 
                                //IE8及IE8以下的浏览器     attachEvent 
                                if(element.addEventListener) {
                                        element.addEventListener(type, handler, false);
                                } else if(element.attachEvent) {
                                        element.attachEvent("on" + type, handler);
                                } else {
                                        element["on" + type] = handler
                                }
                        },
                        getCharCode: function(event) {
                                if(typeof event.charCode == "number") {
                                        return event.charCode;
                                } else {
                                        event.keyCode;
                                }
                        },
                        removeHandler: function(element, type, handler) {
                                //移除事件
                                //Chrome Firefox IE9等      
                                //IE8及IE8以下的浏览器     
                                if(element.removeEventListener) {
                                        element.removeEventListener(type, handler, false);
                                } else if(element.detachEvent) {
                                        element.detachEvent("on" + type, handler);
                                } else {
                                        element["on" + type] = handler;
                                }
                        },
                        getTarget: function(event) {
                                return event.target || event.srcElement;
                        },
                        preventDefault: function(event) {
                                if(event.preventDefault) {
                                        event.preventDefault();
                                } else {
                                        event.returnValue = false;
                                }
                        },
                        stopPropagation: function(event) {
                                if(event.stopPropagation) {
                                        event.stopPropagation()
                                } else {
                                        event.cancelBubble = true;
                                }
                        }
                }

                //touch
                //手指触摸屏幕的时候触发
                var mybtn = document.getElementById("mybtn");
                EventUtil.addHandler(mybtn, "touchstart", function(event) {
                        console.log("当前触摸屏幕的触摸点数组:"+event.touches)
        //所有放在屏幕上的触摸点的信息放入数组中
                        console.log("数组中只包含引起事件的触摸点信息:"+event.changedTouches)
        //放在屏幕上并发生移动的几个触摸点的信息
                        console.log("只包含放在元素上的触摸信息:"+event.targetTouches)
        //放在目标元素上的触摸点的信息
                });
                //手指在屏幕上滑动的时候触发
                EventUtil.addHandler(mybtn, "touchmove", function(event) {
                        console.log(111111)
                });
                //手指在屏幕上移开的时候触发
                EventUtil.addHandler(mybtn, "touchend", function(event) {
                        console.log(111111)
                });
                //touchcancel  当系统停止跟踪触摸的时候触发
        </script>
</body>

event对象

用来获取事件的详细信息。(如:键盘的状态、鼠标的位置、鼠标按钮的状态) 

event对象浏览器兼容问题

function (ev) {
    // IE: 支持window.event
    // 标准: 事件处理函数传入的ev对象
    var ev = ev || window.event;
}

事件源对象

FF: event.target
IE: event.srcElement
Chreme 都支持
兼容写法:
var target = event.target || event.srcElement;

事件捕获和事件冒泡机制 

事件捕获

当一个事件触发后,从Window对象触发,不断经过下级节点,直到目标节点。在事件到达目标节点之前的过程就是捕获阶段。所有经过的节点,都会触发对应的事件

事件冒泡

事件到达目标节点后,会沿着捕获阶段的路线原路返回。同样,所有经过的节点,都会触发对应的事件

事件委托与新增节点绑定事件的关系

事件委托的优点

提高性能

每一个函数都会占用内存空间,只需添加一个事件处理程序代理所有事件,所占用的内存空间更少。

如table可以代理所有td的click事件,ul可以代理所有li的click事件

动态监听

使用事件委托可以自动绑定动态添加的元素,即新增的节点不需要主动添加也可以一样具有和其他元素一样的事件。

如新增的li不用绑定事件,删除li时,不需要解绑

<script>
    window.onload = function(){
        let div = document.getElementById('div');
        
        div.addEventListener('click',function(e){
            console.log(e.target)
        })
 
        let div3 = document.createElement('div');
        div3.setAttribute('class','div3')
        div3.innerHTML = 'div3';
        div.appendChild(div3)
    }
</script>
 
 
<body>
    <div id="div">
        <div class="div1">div1</div>
        <div class="div2">div2</div>
    </div>
</body>

虽然没有给div1和div2添加点击事件,但是无论是点击div1还是div2,都会打印当前节点。因为其父级绑定了点击事件,点击div1后冒泡上去的时候,执行父级的事件,而父级事件中的e是当前是事件源,即被点击的元素,因此会打印出不同的div名。这样无论后代新增了多少个节点,一样具有这个点击事件的功能,因为它们冒泡都会寻找到父节点绑定的事件并执行它。

事件委托的缺点

事件的委托基于冒泡,对于onfocus和onblur事件不支持。

层级过多,冒泡过程中,可能会被某层阻止掉(建议就近委托)

只要事件不支持冒泡或者途中有 event.stopPropagation() 等,那么委托就会失败,所以并不适用直接在document上进行委托。

有时候会出现干扰,因此需要阻止事件冒泡

阻止事件冒泡:
IE: event.cancalBubble = true; 
W3C标准: event.stopPropagation();IE9以下版本不支持
封装阻止事件冒泡函数:
function stopBubble(ev) {
    var ev = ev || window.event;
    if(ev.stopPropagation) {
        ev.stopPropagation();
    }else{
        ev.cancelBubble = true;
    }
}
 
阻止默认事件:
默认事件: 如表单提交,a标签跳转,右键菜单等...
1. return false; 只有以对象属性的方式注册的事件才生效;
2. event.preventDefault(); W3C标准,IE9以下不兼容
3. event.returnValue = false; IE
封装阻止默认事件函数:
function cancelHandler(ev){
    var ev = ev || window.event;
    if(ev.preventDefault) {
        ev.preventDefault();
    }else{
        ev.returnValue = false;
    }
}
 

this的指向问题 

在事件触发的函数中,this是对该dom对象的引用

JS BOM基础

什么是BOM

BOM(browser object model)浏览器对象模型

BOM对象有:window、navigator、screen、history、location、document、event

window

window是浏览器的一个实例,在浏览器中,window对象具有双重角色,它既是通过JavaScript访问浏览器窗口的一个接口,又是ECMAScript规定的global对象。意味着网页中定义的任何一个对象、变量、函数都是以window作为global对象的,因此我们才有权限去访问它的方法。

全局变量——脚本的任何一个地方都能调用的变量

window.xxx="xxx" 等价于 var xxx="xxx"

全局方法——脚本的任何一个地方都能调用的方法

window.xxx=function(){...}    //定义\
window.xxx;    //调用\

等价于

function xxx(){};    //定义\
xxx();    //调用\

总结:所有全局变量和全局函数都被归在window对象上

window对象方法

语法:window.alert("content")
功能:显示带有一段消息和一个确认按钮的警告框

语法:window.confirm("message")
功能:显示一个带有指定消息和OK及取消按钮的对话框

返回值:如果用户点击确定按钮,则confirm()返回true,如果点击取消按钮,则返回false

语法:window.prompt("text,defaultText")

参数说明:

  • text:要在对话框中显示的纯文本(而不是HTML格式的文本)
  • defaultText:默认的输入文本
  • 返回值:如果用户单击提示框的取消按钮,则返回null,如果单击确认按钮,则返回输入字段当前显示的文本

语法:window.open(pageURL,name,parameters)
功能:打开一个新的浏览器窗口或查找一个已命名的窗口

参数说明:

  • pageURL:子窗口路径
  • name:子窗口句柄(name声明了新窗口的名称,方便后期通过name对子窗口进行引用)
  • parameters:窗口参数(各参数用逗号分隔)

<body>
	<input type="button" value="退 出" id="quit">
	<script>
       window.onload = function(){
       	  // 打开子窗口,显示newwindow.html
       	  window.open("newwindow.html","newwindow","width=400,height=200,left=0,top=0,toolbar=no,menubar=no,scrollbars=no,location=no,status=no");
       	  var quit = document.getElementById("quit");
       	  // 点击关闭当前窗口
       	  quit.onclick = function(){
       	  	  window.close("newwindow.html");
       	  }
       }
	</script>
</body>

语法:window.close()
功能:关闭窗口

location对象

location对象提供了与当前窗口中加载的文档有关的信息,还提供了一些导航的功能,它即时window对象的属性,也是document对象的属性

location对象的常用属性

语法:location.href
功能:返回当前加载页面的完整URL
说明:location.href与window.location.href等价

语法:location.hash
功能:返回URL中的hash(#号后跟零或多个字符),如果不包含则返回空字符串

<head>
	<style>
       .box1{height:400px;background:#ccc;}
       .box2{height:600px;background:#666;}
	</style>
</head>
<body>
	<div class="box1" id="top"></div>
	<div class="box2"></div>
	<input type="button" id="btn" value="返回顶部">
	<script>
       // console.log(location.href);
       // console.log(location.hash);
       var btn=document.getElementById("btn");
       btn.onclick=function(){
          location.hash="#top";    //点击返回顶部按钮,会定位到id为top的元素所在的地方,同时URL尾部会加上#top
       }
       console.log(location.pathname);
	</script>
</body>

语法:location.host
功能:返回服务器名称和端口号(如果有)


语法:location.hostname
功能:返回不带端口号的服务器名称


语法:location.pathname
功能:返回URL中的目录和(或)文件名

location对象方法

改变浏览器位置的方法:location.href属性
location对象其他属性也可改变URL:location.hash、location.search

语法:location.replace(url)
功能:重新定向URL
说明:使用location.replace不会在历史记录中(回退按钮)生成新纪录

语法:location.reload()
功能:重新加载当前显示的页面,相当于刷新

说明:

  • location.reload()有可能从缓存中加载
  • location.reload(true)从服务器重新加载

history对象

history对象保存了用户在浏览器中访问页面的历史记录

history对象的方法

语法:history.back()
功能:回到历史记录的上一步
说明:相当于使用了history.go(-1),go()方法是用来加载history猎豹中的某个具体页面。

语法:history.forward()
功能:回到历史记录的下一步
说明:相当于使用了history.go(1)

语法:history.go(-n)
功能:回到历史记录的前n步

语法:history.go(n)
功能:回到历史记录的后n步

screen对象

screen对象包含有关客户端显示屏幕的信息

screen对象的属性

语法:screen.availWidth
功能:返回可用的屏幕宽度

语法:screen.availHeight
功能:返回可用的屏幕高度

说明:availWidth获取到的是除了任务栏之外的内容,在这里我们的任务栏是隐藏掉的

注意:

  • 获取窗口文档显示区的高度和宽度,可以使用innerHeight和innerWidth
  • 获取显示屏幕的高度和宽度,可以使用availHeight和availWidth