1. 什么是作用域
常见的作用域主要分为几个类型:全局作用域、函数作用域、块状作用域、动态作用域。
| 对象 | 类型 |
|---|---|
| global/window | 全局作用域 |
| function | 函数作用域(局部作用域 |
| { } | 块状作用域 |
| this | 动态作用域 |
变量或者其他表达式不在 “当前的作用域”,那么JavaScript机制会继续沿着作用域链上查找直到全局作用域(global或浏览器中的 window)如果找不到将不可被使用。 作用域也可以根据代码层次分层,以便子作用域可以访问父作用域,通常是指沿着链式的作用域链查找 而不能从父作用域引用子作用域中的变量和引用
1.1全局作用域
变量在函数或者代码块{}外定义,即为全局作用域。不过,在函数或者代码块{}内未定义的变量也是拥有全局作用域的。
var name = "Sheep";
// 此处可调用name变量
function myFunction() {
// 函数内可调用 name 变量
}
上述代码中变量 name 就是在函数外定义的,它是拥有全局作用域的。这个变量可以在任意地方被读取或者修改,当然如果变量在函数内没有声明(没有使用 var 关键字),该变量依然为全局变量。
// 此处可调用 name 变量
function myFunction() {
name = "Sheep";
// 此处可调用 name 变量
}
以上实例中 name 在函数内,但是拥有全局作用域,它将作为 global 或者 window 的属性存在。(检验window的属性可以在控制台中delete XXX,如果是window的属性则返回true,否则返回false)。
在函数内部或代码块中没有定义的变量实际上是作为 window/global 的属性存在,而不是全局变量。换句话说没有使用 var 定义的变量虽然拥 有全局作用域,但是它是可以被 delete 的,而全局变量不可以。
1.2函数作用域
在函数内部定义的变量,就是局部作用域。函数作用域内,对外是封闭的,从外层的作用域无法直接访问函数内部的作用域。
在函数作用域中,不带var的变量一定是全局的。
*在函数作用域中,用var定义的变量会将声明提升到函数作用域的顶端,但是不会调用函数。
fn();
function fn() {
console.log('hi');
}
//结果:控制台打印字符串 hi
//注意:函数声明代表函数整体,所以函数提升后,函数名代表整个函数,但是函数并没有被调用。
//函数表达式创建函数,会执行变量提升,此时接收函数的变量名无法正确的调用
fn();
var fn = function() {
console.log('hello');
}
//结果:报错提示 ”fn is not a function"
//解释:该段代码执行之前,会做变量声明提升,fn在提升之后的值是undefined;而fn调用是在fn被赋值为函数体之前,此时fn的值是undefined,所以无法正确调用
2.Let & Const
2.1 let 声明的变量拥有块级作用域
{
let a = 1
}
console.log(a); //undefined
Tip: a变量是在代码块{}中使用 let 定义的,它的作用域是这个代码块内部,外部无法访问。
2.2 let 声明的全局变量不是全局对象的属性
不可以通过 window.变量名的方式访问这些变量,而 var 声明的全局变量是 window 的属性,是可以通过 window.变量名 的方式访的。
var a = 1
console.log(window.a); //1
let a = 1
console.log(window.a); // undefined
2.3 用let重定义变量会抛出一个语法错误
var 可以重复定义,使用 let 却不可以。
var a = 1 var a = 2
console.log(a) //2
let a = 1
let a = 2 // Uncaught SyntaxError: Identifier 'a' has already been declared
2.4 let声明的变量不会进行变量提升
function test () {
console.log(a)
var a = 1
}
test() //undefined
上述代码中,a 的调用在声明之前,所以它的值是 undefined,而不是 Uncaught ReferenceError。实际上因为 var 会导致变量提升,上述代码和 的代码等同:
function test () {
var a
console.log(a)
a = 1
}
3、数组
3、1数组的遍历
-
普通for循环
支持break和continue
const array=[1,2,,3,4,5]
for(var i =0; i < array.length; i++){ console.log(array[i]);
}
-
数组的forEach()
语法简洁,不需要通过索引去访问数组项,然而它的缺点也是很明显,不支持 break、continue 等。
array.forEach(function(i){
console.log(i);
})
//如下代码想遍历数组,遇到数组项 3 之后就结束遍历,不然打印出所遍历过的数值项
[1,2,3,4,5].forEach(function(i){
if(i===3){
return;
}else{
console.log(i);
}
})
//输出: 1,2,4,5
[!DANGER] forEach 的代码块中不能使用 break、continue,它会抛出异常
-
数组的every()
使用 every 遍历就可以做到 break 那样的效果,简单的说 return false 等同于 break,return true 等同于 continue。
[1,2,3,4,5].every(function(i){
if(i===3){
return false;
}else{
console.log(i);
return true;
}
})
[!DANGER] every 的代码块中不能使用 break、continue,它会抛出异常。
-
for...in
用for in不仅可以对数组,也可以对enumerable对象操作。
通常用for in来遍历对象的键名。
支持break和continue。
var enum = {
a:1,
b:2,
c:3,
d:"sheep"
};
for(let item in enum) {
console.log(item,enum[item]);
}
-
for...of
for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name
var arr=[1,2,4,5,6,7]
arr.name="sheep";
for (var value of arr) {
console.log(value);
}
4、类型转换
字符串长度: 变量.length可以获取字符串中字符的个数
空字符串的长度是0
字符串类型的数据,当是数字的时候,在进行乘除减的运算时,会先将字符串类型的数据
隐式转换成数值类型的数据,再进行计算,结果也是数值类型的数据.
字符串类型的数据在做相加运算的时候,是字符串的拼接
字符串类型的数据和任何类型的数据进行相加操作都是拼接
布尔类型中的true可以转换成数值类型的1,false可以转换成数值类型的0
字符串类型的数据和任何类型的数据进行相加操作都是拼接
5、函数
5.1 函数声明的方式
(1)、有名函数:命名函数,有函数名的函数
有名函数定义方式:
1.定义函数名的方式 function 函数名(){}
2.函数表达式的方式 var sum = function(){},调用要在定义之后,否则会报错
//定义函数名的方式
function fn(){
alert('定义函数名的方式');
}
//函数表达式方式
//sum(); //报错
var sum = function (a,b){
//alert('函数表达式方式');
return a+b;
}
//sum();
(2)、匿名函数:没有函数名的函数. 不能直接定义
匿名函数定义的方式:
1.作为函数表达式的方式定义
2.作为自调用函数的方式定义,自调用函数在定义的时候就立即调用,而且只能用一次
3.作为参数传递
4.作为事件处理函数
5.作为返回值
//错误的
// function(){
//}
(function(){
alert('函数自调用1');
})();
(function(){
alert('函数自调用2');
})();
5.2函数形参和实参数量不匹配时

注意:在JavaScript中,形参的默认值是undefined。
- 函数可以带参数也可以不带参数
- 声明函数的时候,函数名括号里面的是形参,形参的默认值为 undefined
- 调用函数的时候,函数名括号里面的是实参
- 多个参数中间用逗号分隔
- 形参的个数可以和实参个数不匹配,但是结果不可预计,我们尽量要匹配
5.3 break ,continue ,return 的区别
- break :结束当前的循环体(如 for、while)
- continue :跳出本次循环,继续执行下次循环(如 for、while)
- return :不仅可以退出循环,还能够返回 return 语句中的值,同时还可以结束当前的函数体内的代码
5.4 arguments的使用
当不确定有多少个参数传递的时候,可以用 arguments 来获取。JavaScript 中,arguments实际上它是当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有实参。arguments展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
-
具有 length 属性
-
按索引方式储存数据
-
不具有数组的 push , pop 等方法
注意:在函数内部使用该对象,用此对象获取函数调用时传的实参。
5.5 构造函数和原形
5.5.1对象的三种创建方式
-
字面量方式
var obj = {}; -
new关键字
var obj = new Object(); -
构造函数方式
function Person(name,age){ this.name = name; this.age = age; } var obj = new Person('sheep',23);
5.5.2构造函数原型prototype
构造函数通过原型分配的函数是所有对象所共享的。
JavaScript 规定,每一个构造函数都有一个prototype 属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
Star.prototype.sing = function() {
console.log('我会唱歌');
}
var ldh = new Star('刘德华', 18);
var zxy = new Star('张学友', 19);
ldh.sing();//我会唱歌
zxy.sing();//我会唱歌

5.5.3对象原型
对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。
__proto__对象原型和原型对象 prototype 是等价的
__proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype


5.5.4 constructor构造函数
对象原型( __proto__)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。
constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。
一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。
如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数如:
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
// 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数
Star.prototype = {
// 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
constructor: Star, // 手动设置指回原来的构造函数
sing: function() {
console.log('我会唱歌');
},
movie: function() {
console.log('我会演电影');
}
}
var zxy = new Star('张学友', 19);
console.log(zxy)
以上代码运行结果,设置constructor属性如图:

如果未设置constructor属性,如图:

5.5.5原型链
每一个实例对象有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。

5.5.6构造函数实例和原型对象三角关系
1.构造函数的prototype属性指向了构造函数原型对象
2.实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象
3.构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数

5.5.7原型链和成员的查找机制
任何对象都有原型对象,也就是prototype属性,任何原型对象也是一个对象,该对象就有__proto__属性,这样一层一层往上找,就形成了一条链,我们称此为原型链;
当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。
如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。
如果还没有就查找原型对象的原型(Object的原型对象)。
依此类推一直找到 Object 为止(null)。
__proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。
5.5.8原型对象中this指向
构造函数中的this和原型对象的this,都指向我们new出来的实例对象
function Star(uname, age) {
this.uname = uname;
this.age = age;
}
var that;
Star.prototype.sing = function() {
console.log('我会唱歌');
that = this;
}
var ldh = new Star('刘德华', 18);
// 1. 在构造函数中,里面this指向的是对象实例 ldh
console.log(that === ldh);//true
// 2.原型对象函数里面的this 指向的是 实例对象 ldh

5.5.9通过原型为数组扩展内置方法
Array.prototype.sum = function() {
var sum = 0;
for (var i = 0; i < this.length; i++) {
sum += this[i];
}
return sum;
};
//此时数组对象中已经存在sum()方法了 可以始终 数组.sum()进行数据的求
5.6继承
5.6.1 call()
- call()可以调用函数
- call()可以修改this的指向,使用call()的时候 参数一是修改后的this指向,参数2,参数3..使用逗号隔开连接
function fn(x, y) {
console.log(this);
console.log(x + y);
}
var o = {
name: 'andy'
};
fn.call(o, 1, 2);//调用了函数此时的this指向了对象o,

5.6..2子构造函数继承父构造函数中的属性
- 先定义一个父构造函数
- 再定义一个子构造函数
- 子构造函数继承父构造函数的属性(使用call方法)
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
// 2 .子构造函数
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
3.使用call方式实现子继承父的属性
Father.call(this, uname, age);
this.score = score;
}
var son = new Son('刘德华', 18, 100);
console.log(son);

5.6.3借用原型对象继承方法
- 先定义一个父构造函数
- 再定义一个子构造函数
- 子构造函数继承父构造函数的属性(使用call方法)
// 1. 父构造函数
function Father(uname, age) {
// this 指向父构造函数的对象实例
this.uname = uname;
this.age = age;
}
Father.prototype.money = function() {
console.log(100000);
};
// 2 .子构造函数
function Son(uname, age, score) {
// this 指向子构造函数的对象实例
Father.call(this, uname, age);
this.score = score;
}
// Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化
Son.prototype = new Father();
// 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数
Son.prototype.constructor = Son;
// 这个是子构造函数专门的方法
Son.prototype.exam = function() {
console.log('孩子要考试');
}
var son = new Son('刘德华', 18, 100);
console.log(son);
如上代码结果如图:

6、浏览器兼容性
6.1不同版本的浏览器
- 高级浏览器: chrome firefox ie9以上
- 低版本浏览器: ie 6 7 8
6.2 兼容性封装innerText和textContent
innerText属性是微软给ie浏览器制定的,后来被所有浏览器广泛使用,但是在之前的某个版本的firefox浏览器不能使用此属性。 textContent属性是后来出现的属性,功能和innerText一样,所有高级浏览器可以使用,低版本浏览器不能使用。
// 让所有的高级浏览器使用textContent,让ie低版本使用innerText
/**
* 兼容性封装读取元素内容
* @param {元素对象} obj
*/
function getTextContent(obj){
if(obj.textContent){
//alert('高级浏览器');
return obj.textContent;
}else{
//alert('低级浏览器');
return obj.innerText;
}
}
/**
* 兼容性封装设置元素内容
* @param {元素对象} obj
* @param {文本内容} txt
*/
function setTextContent(obj,txt){
if(obj.textContent){
//alert('高级浏览器');
obj.textContent = txt;
}else{
//alert('低级浏览器');
obj.innerText = txt;
}
}
6.3 兼容性封装元素的事件监听
元素对象.addEventListener('不带on的事件类型',事件处理函数,布尔值);
- 布尔值:false事件冒泡,true事件捕获
- 支持给同一个元素绑定多个相同的事件类型,只支持高级浏览器
元素对象.atachEvent('带on的事件类型',事件处理函数);
- 只支持ie10及以下浏览器,ie低版本在给同一个元素绑定多个相同的事件类型时,是倒叙执行。
/**
* 兼容性封装给元素添加事件监听
* @param {元素} obj
* @param {事件类型} type
* @param {事件处理函数} fn
* @param {布尔值:false事件冒泡,true事件捕获} bool
*/
function addEvent(obj, type, fn, bool) {
if (obj.addEventListener) {
//高级
obj.addEventListener(type, fn, bool)
} else {
//低级
obj.attachEvent('on' + type, fn)
}
}
/**
* 兼容性封装给元素解除事件监听
* @param {Object} obj
* @param {Object} type
* @param {Object} fn
@param {Object} bool
*/
function removeEvent(obj,type,fn,bool){
if(obj.removeEventListener){
obj.removeEventListener(type,fn,bool);
}else{
obj.detachEvent('on'+type,fn);
}
}
7、事件
7.1事件的三个阶段
1.事件捕获阶段: 由外向内,事件捕获是由网景公司给浏览器制定的规则 2.事件目标阶段: 被触发的那个元素 3.事件冒泡阶段: 由内向外,事件冒泡是由微软公司给ie浏览制定的规则
先是事件捕获到事件目标,再从事件目标进行事件冒泡
-
元素.on事件的方式只能是默认的事件冒泡过程
-
元素.addEventListener('click',事件处理函数,true);
第三个参数:是false时,是事件冒泡阶段,ture是事件捕获阶段
7.2 注册事件(2种方式)

7.3 事件监听
addEventListener()事件监听(IE9以后支持)

eventTarget.addEventListener()方法将指定的监听器注册到 eventTarget(目标对象)上,当该对象触发指定的事件时,就会执行事件处理函数。

attacheEvent()事件监听(IE678支持)

eventTarget.attachEvent()方法将指定的监听器注册到 eventTarget(目标对象) 上,当该对象触发指定的事件时,指定的回调函数就会被执行。

<button>传统注册事件</button>
<button>方法监听注册事件</button>
<button>ie9 attachEvent</button>
<script>
var btns = document.querySelectorAll('button');
// 1. 传统方式注册事件
btns[0].onclick = function() {
alert('hi');
}
btns[0].onclick = function() {
alert('hao a u');
}
// 2. 事件侦听注册事件 addEventListener
// (1) 里面的事件类型是字符串 必定加引号 而且不带on
// (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
btns[1].addEventListener('click', function() {
alert(22);
})
btns[1].addEventListener('click', function() {
alert(33);
})
// 3. attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function() {
alert(11);
})
</script>
事件监听兼容性解决方案
封装一个函数,函数中判断浏览器的类型:

7.4 删除事件(解绑事件)

<div>1</div>
<div>2</div>
<div>3</div>
<script>
var divs = document.querySelectorAll('div');
divs[0].onclick = function() {
alert(11);
// 1. 传统方式删除事件
divs[0].onclick = null;
}
// 2. removeEventListener 删除事件
divs[1].addEventListener('click', fn) // 里面的fn 不需要调用加小括号
function fn() {
alert(22);
divs[1].removeEventListener('click', fn);
}
// 3. detachEvent
divs[2].attachEvent('onclick', fn1);
function fn1() {
alert(33);
divs[2].detachEvent('onclick', fn1);
}
</script>
**删除事件兼容性解决方案 **

7.5 DOM事件流
html中的标签都是相互嵌套的,我们可以将元素想象成一个盒子装一个盒子,document是最外面的大盒子。 当你单击一个div时,同时你也单击了div的父元素,甚至整个页面。 那么是先执行父元素的单击事件,还是先执行div的单击事件 ???

比如:我们给页面中的一个div注册了单击事件,当你单击了div时,也就单击了body,单击了html,单击了document。


IE 提出从目标元素开始,然后一层一层向外接收事件并响应,也就是冒泡型事件流。 Netscape(网景公司)提出从最外层开始,然后一层一层向内接收事件并响应,也就是捕获型事件流。 最终,w3c 采用折中的方式,平息了战火,制定了统一的标准 —--— 先捕获再冒泡。 现代浏览器都遵循了此标准,所以当事件发生时,会经历3个阶段。
DOM 事件流会经历3个阶段:
-
捕获阶段
-
当前目标阶段
-
冒泡阶段
我们向水里面扔一块石头,首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具体元素(目标点)的捕获过程;之后会产生泡泡,会在最低点( 最具体元素)之后漂浮到水面上,这个过程相当于事件冒泡。


事件冒泡
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// onclick 和 attachEvent(ie) 在冒泡阶段触发
// 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略
// son -> father ->body -> html -> document
var son = document.querySelector('.son');
// 给son注册单击事件
son.addEventListener('click', function() {
alert('son');
}, false);
// 给father注册单击事件
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
// 给document注册单击事件,省略第3个参数
document.addEventListener('click', function() {
alert('document');
})
</script>
事件捕获
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// 如果addEventListener() 第三个参数是 true 那么在捕获阶段触发
// document -> html -> body -> father -> son
var son = document.querySelector('.son');
// 给son注册单击事件,第3个参数为true
son.addEventListener('click', function() {
alert('son');
}, true);
var father = document.querySelector('.father');
// 给father注册单击事件,第3个参数为true
father.addEventListener('click', function() {
alert('father');
}, true);
// 给document注册单击事件,第3个参数为true
document.addEventListener('click', function() {
alert('document');
}, true)
</script>
7.6 事件对象
什么是事件对象
事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象。
比如:
-
谁绑定了这个事件。
-
鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
-
键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。
事件对象的使用
事件触发发生时就会产生事件对象,并且系统会以实参的形式传给事件处理函数。
所以,在事件处理函数中声明1个形参用来接收事件对象。

事件对象的兼容性处理
事件对象本身的获取存在兼容问题:
-
标准浏览器中是浏览器给方法传递的参数,只需要定义形参 e 就可以获取到。
-
在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找。

只要“||”前面为false, 不管“||”后面是true 还是 false,都返回 “||” 后面的值。
只要“||”前面为true, 不管“||”后面是true 还是 false,都返回 “||” 前面的值。
<div>123</div>
<script>
var div = document.querySelector('div');
div.onclick = function(e) {
// 事件对象
e = e || window.event;
console.log(e);
}
</script>
事件对象的属性和方法

e.target 和 this 的区别
-
this 是事件绑定的元素(绑定这个事件处理函数的元素) 。
-
e.target 是事件触发的元素。
常情况下terget 和 this是一致的, 但有一种情况不同,那就是在事件冒泡时(父子元素有相同事件,单击子元素,父元素的事件处理函数也会被触发执行), 这时候this指向的是父元素,因为它是绑定事件的元素对象, 而target指向的是子元素,因为他是触发事件的那个具体元素对象。
<div>123</div>
<script>
var div = document.querySelector('div');
div.addEventListener('click', function(e) {
// e.target 和 this指向的都是div
console.log(e.target);
console.log(this);
});
</script>
事件冒泡下的e.target和this
<ul>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>
<script>
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 我们给ul 绑定了事件 那么this 就指向ul
console.log(this); // ul
// e.target 触发了事件的对象 我们点击的是li e.target 指向的就是li
console.log(e.target); // li
});
</script>
7.7 阻止默认行为
html中一些标签有默认行为,例如a标签被单击后,默认会进行页面跳转。
<a href="http://www.baidu.com">百度</a>
<script>
// 2. 阻止默认行为 让链接不跳转
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 标准写法
});
// 3. 传统的注册方式
a.onclick = function(e) {
// 普通浏览器 e.preventDefault(); 方法
e.preventDefault();
// 低版本浏览器 ie678 returnValue 属性
e.returnValue = false;
// 我们可以利用return false 也能阻止默认行为 没有兼容性问题
return false;
}
</script>
7.8 阻止事件冒泡
事件冒泡本身的特性,会带来的坏处,也会带来的好处。

<div class="father">
<div class="son">son儿子</div>
</div>
<script>
var son = document.querySelector('.son');
// 给son注册单击事件
son.addEventListener('click', function(e) {
alert('son');
e.stopPropagation(); // stop 停止 Propagation 传播
window.event.cancelBubble = true; // 非标准 cancel 取消 bubble 泡泡
}, false);
var father = document.querySelector('.father');
// 给father注册单击事件
father.addEventListener('click', function() {
alert('father');
}, false);
// 给document注册单击事件
document.addEventListener('click', function() {
alert('document');
})
</script>
阻止事件冒泡的兼容性处理

7.9 事件委托
事件委托:把事情委托给别人,代为处理。
事件委托也称为事件代理,在 jQuery 里面称为事件委派。
说白了就是,不给子元素注册事件,给父元素注册事件,把处理代码在父元素的事件中执行。
js事件中的代理:

事件委托的原理
给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。
事件委托的作用
-
我们只操作了一次 DOM ,提高了程序的性能。
-
动态新创建的子元素,也拥有事件。
<ul>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
<li>知否知否,点我应有弹框在手!</li>
</ul>
<script>
// 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// e.target 这个可以得到我们点击的对象
e.target.style.backgroundColor = 'pink';
})
</script>
8、Dom操作


关于dom操作,我们主要针对于元素的操作。主要有创建、增、删、改、查、属性操作、事件操作。
8.1. 创建

8.2. 增加

8.3. 删

8.4. 改

8.5. 查

8.6. 属性操作

===================待更新============================