1、
import React from "react";
export default class App extends React.Component {
innerClick = () => {
console.log("A: react inner click.");
};
outerClick = () => {
console.log("B: react outer click.");
};
componentDidMount() {
document.getElementById("outer").addEventListener("click", () => {
console.log("C: native outer click");
});
document.getElementById("inner").addEventListener("click", () => {
console.log("D: native inner click");
});
}
render() {
return (
<div id="outer" onClick={this.outerClick}>
<button id="inner" onClick={this.innerClick}>
BUTTON
</button>
</div>
);
}
}
输出:
D: native inner click
C: native outer click
A: react inner click.
B: react outer click.
2、
export default class App extends React.Component {
innerClick = (e) => {
console.log("A: react inner click.");
e.stopPropagation();
};
outerClick = () => {
console.log("B: react outer click.");
};
componentDidMount() {
document.getElementById("outer").addEventListener("click", () => {
console.log("C: native outer click");
});
document.getElementById("inner").addEventListener("click", () => {
console.log("D: native inner click");
});
}
render() {
return (
<div id="outer" onClick={this.outerClick}>
<button id="inner" onClick={this.innerClick}>
BUTTON
</button>
</div>
);
}
}
输出:
D: native inner click
C: native outer click
A: react inner click.
3、
import React from 'react';
export default class extends React.Component {
constructor(props) {
super(props)
document.addEventListener('click', () => {
console.log('C: native document click')
})
}
innerClick = () => {
console.log('A: react inner click.')
}
outerClick = () => {
console.log('B: react outer click.')
}
render() {
return (
<div id='outer' onClick={this.outerClick}>
<button id='inner' onClick={this.innerClick}>
BUTTON
</button>
</div>
)
}
}
输出:
A: react inner click.
B: react outer click.
C: native document click
C: native document click
4、
import React from 'react';
export default class extends React.Component {
constructor(props) {
super(props)
document.addEventListener('click', () => {
console.log('C: native document click')
})
}
innerClick = (e) => {
console.log('A: react inner click.')
e.nativeEvent.stopImmediatePropagation()
}
outerClick = () => {
console.log('B: react outer click.')
}
componentDidMount() {
document.addEventListener('click', () => {
console.log('D: native document click')
})
}
render() {
return (
<div id='outer' onClick={this.outerClick}>
<button id='inner' onClick={this.innerClick}>
BUTTON
</button>
</div>
)
}
}
输出:
A: react inner click.
B: react outer click.
事件委托
对于li的点击事件,绑定在父元素ul上;
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
const $ul = document.querySelector('ul')
$ul.addEventListener('click', (e) => {
console.log(e.target.innerText)
});
</script>
事件执行顺序
<element1>
<element2></element2>
</element1>
两者都有一个onClick事件处理程序。如果用户单击element2,他将在element1和element2中都引起click事件。但是哪个事件首先触发?应该先执行哪个事件处理程序?换句话说,事件顺序是什么?
毫不奇怪,在过去的糟糕日子里,Netscape和Microsoft得出了不同的结论。
Netscape说,element1上的事件首先发生。这称为事件捕获。 Microsoft坚持认为element2上的事件优先。这称为事件冒泡。
1、事件捕获
使用事件捕获时,element1的事件处理程序首先触发,element2的事件处理程序最后触发。
2、事件冒泡
使用事件冒泡时,element2的事件处理程序首先触发,element1的事件处理程序最后触发。
3、W3C模型
W3C事件模型中发生的任何事件 都首先被捕获,直到到达目标元素,然后再次冒泡。
//如果它的最后一个参数是true为捕获阶段设置的事件处理程序,则如果它是 false为冒泡阶段设置的事件处理程序。
addEventListener()
DOM事件模型:例1
<style>
.outer {
width: 150px;
height: 150px;
background: #ddd;
}
</style>
<div id="outer" class="outer">
<button id="btn">Button</button>
</div>
<script>
const $outer = document.querySelector('#outer')
const $btn = document.querySelector('#btn')
document.addEventListener('click', () => {
console.log('document click')
})
$outer.addEventListener('click', (e) => {
console.log('div click 1')
})
$outer.addEventListener('click', (e) => {
console.log('div click 2')
})
$outer.addEventListener('click', (e) => {
console.log('div click 3')
})
$btn.addEventListener('click', () => {
console.log('button click')
})
</script>
addEventListener的第三个参数默认为false,表示在冒泡阶段捕获事件。事件触发的顺序是从下至上(从里到外),同一个元素上的事件按照绑定的顺序执行 。
测试:
//点击页面空白,控制台输出:
document click
//点击outer区域,控制台输出:
div click 1
div click 2
div click 3
document click
//点击按钮,控制台输出:
button click
div click 1
div click 2
div click 3
document click
DOM事件模型:例2阻止冒泡
<style>
.outer {
width: 150px;
height: 150px;
background: #ddd;
}
</style>
<div id="outer" class="outer">
<button id="btn">Button</button>
</div>
<script>
const $outer = document.querySelector('#outer')
const $btn = document.querySelector('#btn')
document.addEventListener('click', () => {
console.log('document click')
})
$outer.addEventListener('click', (e) => {
console.log('div click 1')
})
$outer.addEventListener('click', (e) => {
e.stopPropagation()
console.log('div click 2')
})
$outer.addEventListener('click', (e) => {
console.log('div click 3')
})
$btn.addEventListener('click', () => {
console.log('button click')
})
</script>
新加了一句 e.stopPropagation(),其作用是阻止事件扩散,所以 document 上的事件监听函数就不会执行了。
测试:
//点击页面空白,控制台输出:
document click
//点击outer区域,控制台输出:
div click 1
div click 2
div click 3
//点击按钮,控制台输出:
button click
div click 1
div click 2
div click 3
DOM事件模型:例3
<style>
.outer {
width: 150px;
height: 150px;
background: #ddd;
}
</style>
<div id="outer" class="outer">
<button id="btn">Button</button>
</div>
<script>
const $outer = document.querySelector('#outer')
const $btn = document.querySelector('#btn')
document.addEventListener('click', () => {
console.log('document click')
})
$outer.addEventListener('click', (e) => {
console.log('div click 1')
})
$outer.addEventListener('click', (e) => {
console.log('div click 2')
},true)
$outer.addEventListener('click', (e) => {
console.log('div click 3')
},true)
$btn.addEventListener('click', () => {
console.log('button click')
})
</script>
这里把 div 的两个事件监听函数绑定在捕获阶段。当事件触发的时候会先执行捕获阶段的监听函数,执行顺序是从上而下(从外到里),相同元素上仍然按照绑定顺序执行。
测试:
//点击页面空白,控制台输出:
document click
//点击outer区域,控制台输出:
div click 2
div click 3
div click 1
document click
//点击按钮,控制台输出:
div click 2
div click 3
button click
div click 1
document click
DOM事件模型:例4
<style>
.outer {
width: 150px;
height: 150px;
background: #ddd;
}
</style>
<div id="outer" class="outer">
<button id="btn">Button</button>
</div>
<script>
const $outer = document.querySelector('#outer')
const $btn = document.querySelector('#btn')
document.addEventListener('click', () => {
console.log('document click')
})
$outer.addEventListener('click', (e) => {
console.log('div click 1')
})
$outer.addEventListener('click', (e) => {
e.stopImmediatePropagation()
console.log('div click 2')
},true)
$outer.addEventListener('click', (e) => {
console.log('div click 3')
},true)
$btn.addEventListener('click', () => {
console.log('button click')
})
</script>
新增了 e.stopImmediatePropagation(),该方法是加强版的 stopPropagation,不仅可以阻止向其他元素扩散,也可以在本元素内部阻止扩散。
测试:
//点击页面空白,控制台输出:
document click
//点击outer区域,控制台输出:
div click 2
//点击按钮,控制台输出:
div click 2
More
通过几个例子来理解 React 的事件系统
https://mp.weixin.qq.com/s/NLJlcdhMcPPgrS8KrnmQ9A
Event order
https://www.quirksmode.org/js/events_order.html#link4