react合成事件与原生事件区别备忘

朋友问起在做一个下拉框组件,下拉的点击事件是用react的onClick触发,外部区域点击关闭则用dom的原生点击事件绑定,问题是下拉的点击事件无法阻止冒泡到dom的原生事件。

我说,react的合成事件 和 原生事件是不一样的,尽可能不要混用,不然很绕。翻开之前在codepen写的demo

https://codepen.io/shellphon-the-encoder/pen/vYPEggK

也把自己绕晕了一下。

react的合成事件,注入onClick等事件,是在根元素上事件代理模拟的。react 16.8.0和之前的版本,是在document上事件代理,react 17则是在root

以demo上的div结构为例:v17.0.2

const {useState, useEffect, useRef} = React;
document.getElementById('root').addEventListener('click', (e) =>{
      console.log('外部原生root 点击', e);
  //e.stopPropagation();
    });
document.addEventListener('click', (e) =>{
      console.log('外部原生document 点击', e);
    }); 
const App = () => {
  const sonRef = useRef(null);
  const parentRef = useRef(null);
  const parentClick = (e)=>{
    console.log('合成事件parent click',e);
    //e.stopPropagation();
  };
  const sonClick = (e)=>{
    console.log('合成事件son click', e);
  }
  const sonClickNo = (e)=>{
    console.log('合成事件son click并阻止冒泡', e);
    e.stopPropagation();
  }
  useEffect(() => {
   document.addEventListener('click', (e) =>{
      console.log('内部原生document click', e);
    }); document.getElementById('root').addEventListener('click', (e) =>{
      console.log('内部原生 root click', e);
    });
    parentRef.current.addEventListener('click', (e) =>{
      console.log('内部原生事件ref p', e);
    });
    sonRef.current.addEventListener('click', (e) =>{
      console.log('内部原生事件ref son', e);
    });
  }, [])
  
  return <div ref={parentRef} onClick={parentClick}>
    <div onClick={sonClick}>son</div>
    <div onClick={sonClickNo} ref={sonRef}>son no</div>
  </div>
};

ReactDOM.render(<App />, document.getElementById('root'));

document>root>div>.parent>.son

son上的onClick(合成事件),实际是react root上的点击事件,在内部做模拟冒泡。

因为demo写的事件比较多,比较绕,所以画了出来。

当只有合成事件的时候,无非就是 son点击响应,然后parent点击响应。

同一个元素的原生事件和合成事件

当往son原生div加click事件时,点击son,会先响应原生click,再然后才是去合成事件,这中间如果parent也有原生click,那也是先原生click再到合成事件去。

如下:

const {useState, useEffect, useRef} = React;

const App = () => {
  const sonRef = useRef(null);
  const parentRef = useRef(null);
  const parentClick = (e)=>{
    console.log('合成事件parent click',e);
    //e.stopPropagation();
  };
  const sonClick = (e)=>{
    console.log('合成事件son click', e);
  }
  const sonClickNo = (e)=>{
    console.log('合成事件son click并阻止冒泡', e);
    e.stopPropagation();
  }
  useEffect(() => {
   
    parentRef.current.addEventListener('click', (e) =>{
      console.log('内部原生事件ref p', e);
    });
    sonRef.current.addEventListener('click', (e) =>{
      console.log('内部原生事件ref son', e);
    });
  }, [])
  
  return <div ref={parentRef} onClick={parentClick}>
    <div onClick={sonClick}>son</div>
    <div onClick={sonClickNo} ref={sonRef}>son no</div>
  </div>
};

ReactDOM.render(<App />, document.getElementById('root'));

点击第一个son时:

可以看到先响应了parent的原生事件,然后才到son的合成事件

合成事件和外部root事件的关系

在react外面给root绑定click事件,看合成事件的顺序

const {useState, useEffect, useRef} = React;
document.getElementById('root').addEventListener('click', (e) =>{
      console.log('外部原生root 点击', e);
  //e.stopPropagation();
    });
document.addEventListener('click', (e) =>{
      console.log('外部原生document 点击', e);
    }); 
const App = () => {
  const sonRef = useRef(null);
  const parentRef = useRef(null);
  const parentClick = (e)=>{
    console.log('合成事件parent click',e);
    //e.stopPropagation();
  };
  const sonClick = (e)=>{
    console.log('合成事件son click', e);
  }
  const sonClickNo = (e)=>{
    console.log('合成事件son click并阻止冒泡', e);
    e.stopPropagation();
  }
  useEffect(() => {
    parentRef.current.addEventListener('click', (e) =>{
      console.log('内部原生事件ref p', e);
    });
    sonRef.current.addEventListener('click', (e) =>{
      console.log('内部原生事件ref son', e);
    });
  }, [])
  
  return <div ref={parentRef} onClick={parentClick}>
    <div onClick={sonClick}>son</div>
    <div onClick={sonClickNo} ref={sonRef}>son no</div>
  </div>
};

ReactDOM.render(<App />, document.getElementById('root'));

点击第一个son时,先原生事件, 然后 到了外部root绑定的事件,再到合成事件的son、parent,再然后是document

合成事件是在root上模拟的,而外部绑定root的事件和这个合成事件也就是在一个dom上多次绑定事件响应,各不相干,顺序上谁先绑定谁先响应,于是外部root原生先响应,再到合成事件的处理。而document是在root的上层,因此document事件是在最后才响应。

如果在组件周期里也给root加一个原生事件响应,那它会在合成事件完成之后才响应,因为它也是给同一个dom绑定的事件之一,只是晚于合成事件。

回到最开始的demo代码,当在son的onClick上阻止冒泡时,它做了两件事情:

1. 阻止了向上冒泡的模拟

2. 调用了原生事件的阻止冒泡 (解释了合成事件阻止冒泡后,为什么document事件没有响应)

由此,如果朋友在react 17版本上document上绑定的事件应该是能被合成事件阻止冒泡的。

但如果在react 16.8.0 合成事件是在document上绑定,那么额外绑定的document事件不会被合成事件阻止冒泡。那该怎么办呢?他是在组件的didmount阶段,才绑定document的事件的,实际上,可以拿到合成事件的原生event,调用event.nativeEvent.stopImmediatePropagation(),可以阻止和document的合成事件同级的剩余事件。

参考资料:

https://github.com/youngwind/blog/issues/107

https://mdnice.com/writing/85c044f9087746dcbd719e4a0b847278

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/563402.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端表单input的简单使用

1.代码结构介绍 2.实战效果

【嵌入式linux】Ubuntu 修改用户名

第一次打开Ubuntu时不小心把初始用户名“siriusiot”写成“siriousiot”&#xff08;多了一个o&#xff09; 。作为技术人&#xff0c;我们要保持严谨&#xff0c;我们要纠正过来&#xff08;其实就是单词拼错了怕被笑话&#xff09;。 打开终端&#xff0c;输入&#xff1a; …

TypeError: Cannot read property ‘forceUpdate‘ of undefined

今天给大家展示一个 我自己在写项目的时候遇到的保存 其实很简单就是没有修改addid 把自己的小程序appid填上去就好了 学习记录笔记&#xff01;

【高校科研前沿】东北地理所孙敬轩博士为一作在《中国科学:地球科学(中英文版)》发文:气候变化下东北地区农业绿水安全风险评估

目录 01 文章简介 02 研究内容 03 文章引用 04 期刊简介 01 文章简介 论文名称&#xff1a;Risk assessment of agricultural green water security in Northeast China under climate change&#xff08;气候变化下东北地区农业绿水安全风险评估&#xff09; 第一作者及…

实验 3--表的基本操作与数据查询

文章目录 实验 3--表的基本操作与数据查询4.3.1 实验目的4.3.2 实验准备实验内容1.在 SSMS 中向数据库 YGKQ 中的表插入数据。2.使用 T-SQL 语句向 YGKQ 中的表插入数据。3.在 SSMS 中删除数据库 YGKQ 中的表数据。4.使用 T-SQL 语句删除数据库 YGKQ中的表数据。5.在 SSMS 中修…

ChatGPT基础(三) 让ChatGPT回答质量提高十倍的提示词模版

上篇文章介绍了ChatGPT使用提示词的一些方法策略和如何优化我们的提示词。这里呢&#xff0c;我介绍一下参照大佬的方法总结的一个提示词的一个用法的模板。使用这个模板之后&#xff0c;我们的提问和获得答案的效率和收集素材的完整度能提高很多。 首先我介绍一下这个模板&am…

NUMA测试

一、开启NUMA 添加链接描述 二、绑定核数 nerdctl update --cpuset-cpus0-7 3aecd121924a enable_thread_pool true thread_pool_attr 512, 2, (allbind)

day48_servlet

今日内容 周一 0 复习上周 1 本周计划 2 MVC和三层架构 3 Login案例 4 请求转发 5 重定向 0 复习昨日 1 jdbc五大步骤 注册驱动(反射)获得连接获得执行sql对象执行SQL关流 2 什么是SQL注入 通过SQL关键词,在执行SQL时出现不正常的情况 3 PreparedStatement怎么使用,有什么特点 …

SpringAOP从入门到源码分析大全(四)SpringAOP的源码分析

文章目录 系列文档索引六、EnableAspectJAutoProxy源码分析1、AnnotationAwareAspectJAutoProxyCreator源码&#xff08;1&#xff09;wrapIfNecessary方法&#xff08;2&#xff09;createProxy 2、getAdvicesAndAdvisorsForBean查找所有Advisor&#xff08;1&#xff09;find…

人工智能入门(一):基于Pytorch的手写数字识别模型

前言&#xff1a; 因为还在上学&#xff0c;时间不太够用&#xff0c;很多内容写到后面心有余力不足&#xff0c;未来有时间我会慢慢补充。人工智能的知识涉猎范围广又杂乱无章&#xff0c;啃书或上课学到的知识往往很早就过时了或者离实际的项目无关。所以&#xff0c;我很希…

安装mmsegmentation默认主分支main

安装时间2024.4.21 mmsegmentation新版本main分支&#xff08;v1.2.2&#xff09; 安装过程 conda create --name openmmlab python3.8 -y conda activate openmmlab// 很关键&#xff0c;可以避免mmcv版本问题 pip install torch1.10.1cu113 torchvision0.11.2cu113 torcha…

【力扣 Hot100 | 第七天】4.22(移动零)

文章目录 1.移动零1.1题目1.2解法&#xff1a;双指针1.2.1双指针思路1.2.2代码实现 1.移动零 1.1题目 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数…

[docker] volume 补充 环境变量 参数

[docker] volume 补充 & 环境变量 & 参数 这里补充一下 volume 剩下的内容&#xff0c;以及添加参数(ARG) 和 环境变量 ENV 的内容 read only volumes ❯ docker run-p 3000:80--rm--name feedback-app-v feedback:/app/feedback-v "$(pwd):/app"-v /app/…

第 394 场 LeetCode 周赛题解

A 统计特殊字母的数量 I 哈希&#xff1a;遍历然后枚举 class Solution {public:int numberOfSpecialChars(string word) {unordered_map<char, int> m;for (auto ch : word)m[ch] 1;int res 0;for (char ch a; ch < z; ch)if (m.count(ch) && m.count(A …

爬虫采集:数据提取

目录 1. 数据分类 2. JSON 2.1 json数据转换​编辑 3. 正则表达式 3.1 re模块 3.1.1 常见方法 3.1.2 单字符匹配 3.1.4 匹配开头和结尾 3.1.5 分组匹配 3.1.6 贪婪非贪婪匹配 4. Xpath 4.1 语法 4.2 查找特定节点 4.3 lxml 模块 4.3.1 安装 4.3.2 导入 4.3.3 使…

【行为型模式】命令模式

一、命令模式概述 命令模式的定义&#xff1a;将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。(对象行为型) 命令模式优缺点&#xff1a; 优点&#xff1a; 1.类间解耦&#xff1a;调用者角色与接收者角色之间没有任何依…

TPG原理以及verilog实现

文章目录 一、前言二、verilog代码实现三、仿真以及结果分析 一、前言 TPG(video_test_pattern generator) 视频测试模式发生器用于产生测试数据&#xff0c;对视频数据通路测试。根据视频输出时序产生相应的图像数据 二、verilog代码实现 timescale 1ns / 1nsmodule tpg ( i…

鸿蒙入门10-CheckBoxGroup组件

复选框群组 用于控制多个复选框全选或者不全选状态 参数 参数形式 &#xff1a; CheckboxGroup( options?: { group?: string } ) 创建复选框群组&#xff0c;可以用于控制群组内的 CheckBox 成员 全选 或者 不全选 相同 group 的 CheckBox 和 CheckBoxGroup 为同一群组 参…

Python turtle海龟绘制美国队长盾牌

使用Python中的turtle模块绘制美队盾牌 具体思路如下&#xff1a; 导入海龟库第1个圆&#xff1a;半径 200&#xff0c;红色填充第2个圆&#xff1a;半径 150&#xff0c;白色填充第3个圆&#xff1a;半径 100&#xff0c;红色填充第4个圆&#xff1a;半径 50&#xff0c;蓝色…

摩科智能协办“提高不动产登记质量,促进优化营商环境培训会”

为深入落实国家和自治区自然资源工作会议精神&#xff0c;加强不动产登记队伍作风常态化建设&#xff0c;提高不动产登记质量&#xff0c;促进优化营商环境&#xff0c;学习先进地区工作经验。2024年4月19日&#xff0c;“提高不动产登记质量 促进优化营商环境培训会”在浙江省…