在Shadow DOM中的setTimeout中更改了event.target?

如何解决在Shadow DOM中的setTimeout中更改了event.target?

https://jsbin.com/qogewewomi/1/edit?html,output

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>

<body>
  <test-test></test-test>
  <button id='out'>Outside Shadow DOM</button>
</body>

</html>
customElements.define('test-test',class extends HTMLElement {
  constructor() {
    super();

    const node = document.createElement('template');
    node.innerHTML = '<button id="in">Inside Shadow DOM</button>';
    this.attachShadow({
      mode: 'open'
    }).appendChild(node.content);

    this.shadowRoot.querySelector('#in').addEventListener('click',e => {
      console.log(e.target);
      setTimeout(() => {
        console.log(e.target);
      });
    });
  }
});

document.querySelector('#out').addEventListener('click',e => {
  console.log(e.target);
  setTimeout(() => {
    console.log(e.target);
  });
});

我在影子DOM内外的事件侦听器中发现了这些不一致的行为。单击Inside Shadow DOM按钮后,控制台将输出:

<button id="in">Inside Shadow DOM</button>
<test-test>...</test-test>

单击Outside Shadow DOM按钮时,控制台将输出:

<button id="out">Outside Shadow DOM</button>
<button id="out">Outside Shadow DOM</button>

在Chrome,FireFox和Safari中进行了测试。他们都有这些不一致的行为。我不知道这是预期的行为还是错误?

更新: 这个问题不应该被解决。另一个没有回答这个问题。

解决方法

这是预期的行为,不是错误。

解释将需要太多字符。

请参阅:


简而言之:

JavaScript是单线程的。
(e)Event是在所有事件处理程序周围传递的全局对象

使用SetTimeout时,Event的内容可以/将有所不同

我重写了您的测试代码:https://jsfiddle.net/CustomElementsExamples/wj1234km/

<shadow-element id="lightcoral" title=One></shadow-element>
<script>
  function log(label,color,scope,evt) {
    let composedTarget = (evt.composed && evt.composedPath());
    console.log(`%c ${label} \t%c ${evt.target.id} `,`background:${color}`,`background:${evt.target.id};color:white`,'\n\ttarget:',evt.target,"\n\tthis:",scope.nodeName || "window","\n\tcurrentTarget",evt.currentTarget,'\n\tclickedTarget:',evt.clickedTarget,"\n\tcomposed:",evt.composed ? "True" : "False","composedPath[0]:",composedTarget[0]);
  }

  customElements.define('shadow-element',class extends HTMLElement {
    constructor() {
      super().attachShadow({mode:'open'})
             .innerHTML = `<button id=green>click ${this.title}</button>`;
      let button = this.shadowRoot.querySelector('button');
      button.onclick = e => {
        let savedTarget = e.target;
        e.clickedTarget = e.target;
        button.onclick = false; //prevent double capture
        log(`clicked element:${this.id}`,'lightgreen',this,e);
        setTimeout(() => {
          log('timeout element','red;color:yellow',e)
        },500);
      };
      //this.onclick = button.onclick;
    }
  });

</script>

要输出:

target现在是<shadow-element>,因为一旦setTimeout运行 global 事件,该事件就一直沿DOM传递。

currentTarget告诉您所有事件处理均已完成
https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget

clickedTarget演示您可以在该全局事件对象(正在传递)上设置自定义属性。从而“保存”您单击的目标。但是其他事件(或下面的element.onclick函数调用)可能会覆盖它,因此最好在正确的范围内设置自定义变量savedTarget,并在您的setTimeout

中使用

您可以通过在元素本身上设置点击处理程序来查看target的变化。

target成为事件<shadow-element>冒出DOM并“逃离” shadowDOM的那一刻

,

不应关闭此问题。另一个没有回答这个问题。

是的,如果您仔细阅读的话。但由于社区不同意,我觉得我必须回答...


there的主要要点是,将对Shadow DOM中的元素触发的事件重定向了,以便从Light元素中隐藏Shadow DOM。这意味着Event对象在其生存期内将更改其target

  • 首先,在捕获阶段,尽管Event对象仍未到达Shadow DOM,但Light容器(此处为<test-test>)将成为该Event对象的目标。
  • 然后在Shadow DOM内部,它将更改为实际的Shadow目标(<button>)。
  • 最后,在从Shadow DOM退出的冒泡阶段,它将重新设置为Light容器。

因此,当您从超时记录日志时,所有事件阶段都会发生,并且事件对象将处于最终状态,并且Light容器为.target

customElements.define('test-test',class extends HTMLElement {
  constructor() {
    super();

    const node = document.createElement('template');
    node.innerHTML = '<button id="in">Inside Shadow DOM</button>';
    this.attachShadow({
      mode: 'open'
    }).appendChild(node.content);

    this.shadowRoot.addEventListener('click',e => {
      console.log("[capturing phase] in Shadow DOM",e.target);
    },{ capture: true });
    this.shadowRoot.addEventListener('click',e => {
      console.log("[bubbling phase] in Shadow DOM",{ capture: false });

  }
});

document.addEventListener('click',e => {
  console.log( "[capturing phase] in Light DOM",e.target);
},{ capture: true });
document.addEventListener('click',e => {
  console.log( "[bubbling phase] in Light DOM",{ capture: false });
<test-test></test-test>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-