一、DOM事件流分为三个阶段
1.事件捕获阶段
当某个事件触发时,文档根节点最先接收到事件,然后根据DOM树结构向具体绑定事件的元素传递,从父元素向子元素传递,该阶段为捕获阶段。
window ->document -> body -> div
2.目标阶段
事件苑已经捕获事件,开始向外冒泡。
3.事件冒泡阶段
根据DOM结构由触发事件的元素向根节点传递,称为冒泡阶段。
div -> body -> document -> window
二、原生DOM注册监听事件
使用addEventListener函数为DOM元素注册监听事件
DOMElement.addEventListener('事件名称',Fn,useCapture),useCapture时在捕获阶段监听事件;为false时,在冒泡阶段监听事件。默认为false。
三、原生事件委托
<div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
</div>
const click = document.querySelector("ul").addEventListener("click",function(e){
console.log("asdasda");
},true);
假设我们想要在每个 li 上绑定 click 事件,可以直接给每个 li 添加点击事件,增加事件回调。有一种更好的做法,那就是在 ul 上添加一个监听事件,由于事件的冒泡机制,事件就会冒泡到ul上,因为ul上有事件监听,所以事件就会触发。这就是事件委托。
四、React合成事件
和原生DOM的事件都是绑定在真实DOM元素上不同的就是React中就是用了这种事件委托的方法,将所有事件都绑定在document 这个节点上,document监听事件的变化。故只有document这个节点上面才绑定了DOM原生事件。
<div onClick={handleClick}>
<DropDown />
</div>
const handleClick = (e) => {
e.stopPropagation()
}
如上,和原生DOM不同,React通过驼峰式的命名方法来绑定事件,这叫合成事件
来个栗子:
const Test = () => {
useEffect(() => {
document.addEventListener('click', function(){
console.log('document click')
})
document.getElementsByClassName('App')[0].addEventListener('click', function(){
console.log('app click')
})
document.getElementsByClassName('Btn')[0].addEventListener('click', function(e){
console.log('button click')
// e.stopPropagation();
})
},[])
onClick = (e) => {
e.stopPropagation() // 能够阻止div.app的触发
e.nativeEvent.stopImmediatePropagation(); // 能够阻止document的触发
e.nativeEvent.stopPropagation(); // 什么都阻止不了
console.log('react button click');
}
return (
<div className="App" onClick={() => {console.log('react app click')}}>
<Button className="Btn" onClick={this.onClick}>点我</Button>
</div>
)BBtn
}
【结果是------------------------------】
button click
app click
react button click
react app click
document click
五、React中阻止冒泡
A、只阻止React合成事件间的冒泡,用e.stopPropagation()
【栗子结果】
button click
app click
react button click
document click
B、阻止React合成事件和document的冒泡,用e.nativeEvent.stopImmediatePropagation()
【栗子结果】
button click
app click
react button click
react app click
C、阻止合成事件与非合成事件(除document)的冒泡,用e.target判断
e.target && e.target === document.querySelector('#inner')
e.target.NodeName
D、e.native.stopPropagation 什么都阻止不了
【栗子结果】
button click
app click
react button click
react app click
document click
六、javascript中不会冒泡的事件
- scroll
- focus & blur
- Media事件(onplay、onpause...)
- mouseleave & mouseenter
这些不会冒泡的事件自然就不用阻止冒泡,e.stopPropagation()无效。
在事件委托时,要注意在捕获阶段进行监听,否则事件会失效