基于React内部的处理 如果我们给合成事件绑定一个普通函数 当事件触发 绑定的函数执行 方法中的this会是undefined(这样是不好的)
合成事件对象SyntheticBaseEvent:React内部经过特殊处理 把各个浏览器的事件对象统一化后 的一个事件对象
我们在React合成事件对象触发的时候 我们也可以获取到事件对象 只不过此对象是合成事件对象
合成事件对象中 也包含了浏览器内置事件对象中的一些属性和方法(常用的基本都有)例如clientX/clientY pageX/pageY target type preventDefault stopPropagation…
nativeEvent:这个属性可以获取浏览器内置(原生)的事件对象
注意:经过bind处理改变this指向 不管有没有预传参数 最后一个实参都是合成对象
我们先来复习一下事件委托
事件委托:利用事件的传播机制 实现一套事件绑定处理方案
例如:一个容器中 有很多元素都要在点击的时候做一些事情
事件具备传播机制
第一步:从最外层向最里层逐一查找 (捕获阶段:分析出路径)
第二部:把事件源(点击的这个元素) 的点击行为触发(目标阶段)
第三步:按照捕获阶段分析出来的路径 从里到外 把每一个元素的点击行为触发(冒泡阶段)
事件和事件绑定
即使我们没有给body的点击事件绑定方法 当我们点击body的时候 其点击行为也会被触发 只不过啥事都不做而已
e.stopPropagation 阻止事件的传播 (包含捕获与冒泡)
e.stopImmediatePropagation() 阻止事件传播 只不过它可以把当前元素绑定的其他方法(同级也会阻止) 如果还未执行 也不会在执行了
所谓合成事件绑定 其实并没有给元素自身做事件绑定 而是给元素设置 onXxx/onXxxCapture这样的合成事件属性
当事件行为触发 根据原生事件传播的机制 都会传播到#root容器上 React内部给#root容器做了事件绑定(捕获与冒泡)
当React内部绑定的方法执行的时候 会依据ev.composedPath()中分析的路径 依次把对应阶段的onXxx/onXxxCapture 等事件合成属性触发执行
=>合成事件是利用事件委托(事件传播机制完成的)
因为最后执行的handle方法 从此可以知道为什么 如果不经过处理 方法中的this是undefined (如果绑定的方法是箭头函数 则找函数上级上下文中的this)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>合成事件原理</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
overflow: hidden;
}
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#root {
width: 300px;
height: 300px;
background: lightblue;
}
#outer {
width: 200px;
height: 200px;
background: lightgreen;
}
#inner {
width: 100px;
height: 100px;
background: lightcoral;
}
</style>
</head>
<body>
<div id="root" class="center">
<div id="outer" class="center">
<div id="inner" class="center"></div>
</div>
</div>
<script>
const root = document.querySelector("#root"),
outer = document.querySelector("#outer"),
inner = document.querySelector("#inner");
outer.onClick = () => {
console.log("outer 冒泡 合成");
};
outer.onClickCapture = () => {
console.log("outer 捕获 合成");
};
inner.onClick = () => {
console.log("inner 冒泡 合成");
};
inner.onClickCapture = () => {
console.log("inner 捕获 合成");
};
//给#root做事件绑定 捕获 冒泡 #root上绑定的方法执行把所有规划的路径中 有合成事件属性的都执行即可
root.addEventListener(
"click",
(ev) => {
let path = ev.composedPath(); //path: [事件源-> ... -> window] 所有祖先元素
[...path].reverse().forEach((ele) => {
let handle = ele.onClickCapture; // 获得祖先元素的onClickCapture
if (handle) {
handle();
}
});
},
true
);
root.addEventListener(
"click",
(ev) => {
let path = ev.composedPath(); //path: [事件源-> ... -> window] 所有祖先元素
path.forEach((ele) => {
let handle = ele.onClick; // 获得祖先元素的onClick
if (handle) {
//这里把绑定的合成事件方法执行 如果不经过处理 方法中的this是undefined (如果绑定的方法是箭头函数 则找函数上级上下文中的this)
//在执行这些方法之前 把原生的事件对象ev做特殊处理 返回合成事件对象
handle();
}
});
},
false
);
</script>
</body>
</html>
React中合成事件的处理原理
在组件渲染的时候 如果发现jsx元素属性中有 onXxxx/onXxxxCapture这样的属性 不会给当前元素直接做事件绑定 只是把绑定的方法
赋值给元素的相关属性!!
例如:
然后对#root这个容器做了事件绑定(捕获和冒泡都做了)
原因:因为组件中所渲染的内容 最后都会插入到#root容器中 这样点击页面中任何一个元素 最后都会把#root的点击行为触发
而在给#root绑定的方法中 把之前给元素设置的onXxxx/onXxxCapture属性 在相应的阶段执行!!!
移动端的click会存在300ms延迟
移动端的click是单击事件
pc端的click是点击
如果连着点击两下:
pc端会触发:两次click、一次dblclick
移动端:不会触发click 只会触发dblclick
单击事件:第一次点击后 检测300ms 看是否有第二次点击操作 如果没有就是单击 如果有就是双击
方法一:
单手指事件模型:touch
touchstart touchmove touchend
模拟点击效果
import React from "react";
class Demo extends React.Component {
//手指按下:记录手指的起始坐标
touchstart = (ev) => {
console.log(ev);
let finger = ev.changedTouches[0]; //记录了操作手指的相关信息
this.touch = {
startX: finger.pageX,
startY: finger.pageY,
isMove: false,
};
};
//手指移动:记录手指偏移量 和误差值做对比 分析出是否发生移动
touchmove = (ev) => {
let finger = ev.changedTouches[0],
{ startX, startY } = this.touch;
let changeX = finger.pageX - startX,
changeY = finger.pageY - startY;
if (Math.abs(changeX) > 10 || Math.abs(changeY) > 10) {
this.touch.isMove = true;
}
};
//手指离开:根据isMove判断是否是点击
touchend = () => {
let { isMove } = this.touch;
if (isMove) return;
//说明触发了点击操作
console.log("点击了按钮");
};
render() {
return (
<div>
<button
onTouchStart={this.touchstart}
onTouchMove={this.touchmove}
onTouchEnd={this.touchend}
>
提交
</button>
</div>
);
}
}
export default Demo;
方法二:
使用Fastclick解决移动端使用click事件的300ms延迟的问题
Fastclick.attach(document.body);
import React from "react";
import ReactDOM from "react-dom/client";
import "@/index.less";
import "./jsxHandle";
import Demo from "./views/Demo";
import Fastclick from "fastclick";
//使用Fastclick解决移动端使用click事件的300ms延迟的问题
Fastclick.attach(document.body);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<>
<Demo />
</>
);