当子元素上面所有的父元素注册有相同的事件时,你执行每个元素的事件时,会发生什么呢?这就是js中的事件流,即事件发生的顺序。这里用click来举例
背景
在以前,浏览器的兼容性比现在要小得多,在浏览器发展到第四代的时候,Netscape(网景)提出事件捕获,而Internet Explorer提出事件冒泡。在W3C组织的统一下,JS支持了冒泡流和捕获流,最终被应用在现在浏览器里。
注:但是目前低版本的IE浏览器还是只能支持冒泡流(IE6,IE7,IE8均只支持冒泡流),所以为了能够兼容更多的浏览器,建议大家使用冒泡流
三个阶段
在DOM兼容浏览器中,事件流分为3个阶段:
(1)捕获阶段:事件从Document节点自上而下向目标节点传播的阶段;
(2)目标阶段:真正的目标节点正在处理事件的阶段;
(3)冒泡阶段:事件从目标节点自上而下向Document节点传播的阶段。
例:
<style>
#box1 {
width: 300px;
height: 300px;
background-color: red;
}
#box2 {
width: 200px;
height: 200px;
background-color: green;
}
#box3 {
width: 100px;
height: 100px;
background-color: blue;
}
</style>
<div id="box1">box1
<div id="box2">box2
<div id="box3">box3
</div>
</div>
</div>
- 当js代码如下时
<script>
function $(el) {
return document.getElementById(el);
}
$('box1').onclick = function() {
console.log('box11');
};
$('box2').onclick = function() {
console.log('box22');
};
$('box3').onclick = function() {
console.log('box33');
};
window.body.onclick = function() {
console.log('body00');
}
//onclick 只支持事件冒泡
window.body.addEventListener('click', function() {
console.log(this.id);
}, true);
</script>
- 事件捕获阶段:当addEventListener第三个参数为true时
点击box3,输出 body box33 box22 box11 body00
点击box2,输出 body box22 box11 body00
点击box1,输出 body box11 body00 - 事件冒泡阶段:当addEventListener第三个参数为false时
点击box3,输出 box33 box22 box11 body00 body
点击box2,输出 box22 box11 body00 body
点击box1,输出 box11 body00 body - 当js代码改为如下时
<script>
function $(el) {
return document.getElementById(el);
}
$('box1').addEventListener('click',function () {
console.log('box11');
}, true)
$('box2').addEventListener('click',function () {
console.log('box22');
}, true)
$('box3').addEventListener('click',function () {
console.log('box33');
}, true)
window.body.addEventListener('click', function() {
console.log('body');
}, true);
</script>
- 事件捕获阶段:当addEventListener第三个参数为true时
点击box3,输出 body box22 box11 box33
点击box2,输出 body box11 box22
点击box1,输出 body box11 - 事件冒泡阶段:当addEventListener第三个参数为false时
点击box3,输出 box33 box22 box11 body
点击box2,输出 box22 box11 body
点击box1,输出 box11 body
事件委托
如果在li上绑定click事件,那么新添加的li元素不会有这个click事件。此时应该在ul上绑定事件,根据事件捕获的原理,事件会自上而下传递给li。通过event.target找到这个li元素
阻止事件冒泡
在w3c模型中你必须调用事件的stopPropagation()方法
stopPropagation()
在微软的模型中,你必须设置事件的cancelBubble的属性为true
window.event.cancelBubble = “true
无法在捕获阶段阻止事件冒泡
call、applay、bind的区别
在不同场景中this的指向不同,但有些情况我们为了使用某种特定环境的this引用,这是后就需要改变this的指向
例如:在使用定时器时,在外部备份this引用,然后在定时器内部引用
call()
语法
fun.call(thisArg, arg1, arg2,……)
thisArg在fun运行时指定的this值在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
arg1, arg2,……指定的参数列表
返回值
返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined
使用call方法调用父构造函数
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
console.log(new Food('zs', 23));//{name:zs,price:23,category:food}
使用call方法调用匿名函数
var animals = [{
species: 'Lion',
name: 'King'
},
{
species: 'Whale',
name: 'Fail'
}
];
for (var i = 0; i < animals.length; i++) {
(function (i) {
this.print = function () {
console.log(i + ' ' + this.species + ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}//0 Lion King \n 1 Whale Fail
使用call方法调用函数并且指定上下文的’this’
function greet() {
var reply = [this.person, 'is', this.role].join(' ');
console.log(reply);
}
var i = {
person: 'zs', role: 'person'
};
greet.call(i);//zs is person
bind
语法
fun.bind(thisArg, [arg1, arg2,……])
当绑定函数被调用时,该参数会作为原函数运行时的 this 指向。当使用new 操作符调用绑定函数时,该参数无效。
当绑定函数被调用时,这些参数将置于实参之前传递给被绑定的方法。
都是将this指向了thisArg;
var num = {
x: 10,
add: function(){
return this.x;
}
}
var getx = num.add;
var bindX = getx.bind(num);
console.log(bindX());//bind()返回一个新的函数
var callX = getx.call(num);
console.log(callX)//call直接调用函数
var applyx = getx.apply(num);
console.log(applyx)//apply直接调用函数
//apply只有两个参数,第二个参数是数组