惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Y
Y Combinator Blog
D
Docker
有赞技术团队
有赞技术团队
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
爱范儿
爱范儿
H
Help Net Security
美团技术团队
MyScale Blog
MyScale Blog
B
Blog RSS Feed
C
Check Point Blog
Microsoft Security Blog
Microsoft Security Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
小众软件
小众软件
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
GbyAI
GbyAI
G
Google Developers Blog
月光博客
月光博客
Google DeepMind News
Google DeepMind News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
F
Fortinet All Blogs

博客园 - 前端路引

高精度计算插件 decimal.js 处理 JS 浮点数精度问题(0.1 + 0.2 !== 0.3) 还在痛苦前端的拖拽排序吗?SortableJS 轻松搞定 浏览器 15 个常见指纹特征,使用插件 FingerprintJS 生成浏览器指纹 Web前端入门第 90 问:JavaScript 也能无中生有的创建音频 Web前端入门第 89 问:总结 8 种跨域通信处理方案 Web前端入门第 88 问:引入 JavaScript 的 script 标签究竟有多少用法? Web前端入门第 87 问:JavaScript 中 setInterval 和 setTimeout 细节 Web前端入门第 86 问:JavaScript 中的 Web Worker 为什么能提升代码性能? Web前端入门第 85 问:JavaScript 一个简单的 IndexedDB 数据库入门示例 Web前端入门第 84 问:JavaScript sessionStorage 那些容易踩坑的地方 Web前端入门第 83 问:JavaScript localStorage 有大小限制吗?溢出会怎样? Web前端入门第 82 问:JavaScript cookie 有大小限制吗?溢出会怎样? Web前端入门第 81 问:JavaScript cookie 的读写操作 Web前端入门第 80 问:JavaScript 哪些地方需要 try...catch 异常捕获 Web前端入门第 79 问:JavaScript async & await 的异步任务进化之路 Web前端入门第 78 问:JavaScript 比较少见的模版字符串用法 Web前端入门第 76 问:JavaScript 鼠标事件(mouse) enter/leave 和 over/out 区别 Web前端入门第 75 问:JavaScript 性能优化之事件委托(事件代理)原理 Web前端入门第 74 问:JavaScript 事件冒泡与事件捕获
Web前端入门第 77 问:JavaScript 由程序触发绑定事件的几种方式
前端路引 · 2025-07-14 · via 博客园 - 前端路引

开发中经常会遇这样的需求:点击 A 元素的时候,需要触发 B 元素的事件,比如:点击一个 div 元素,然后触发 input:file 的 click 事件,用来选择文件上传。

click 方法

以上需求可通过元素的 click 方法触发:

<style>
  .test2 {
    padding: 4px;
    margin-top: 12px;
  }
</style>
<input type="file" class="test1"><br>
<button class="test2">前端路引--事件测试</button>

<script>
  (() => {
    const test1 = document.querySelector('.test1')
    const test2 = document.querySelector('.test2')
    test2.addEventListener('click', () => {
      //  点击 test2 后,触发 test1 的 click 事件
      test1.click()
    })
  })()
</script>

效果:

77-1

点击事件有 click 方法可以使用,如果需求是点击 A 元素触发 B 元素的 mousedown 事件,那能用 mousedown 方法吗?

<style>
  button {
    padding: 4px;
    margin-top: 12px;
  }
</style>

<button class="test1">前端路引--事件测试 test1</button><br>
<button class="test2">前端路引--事件测试 test2</button>

<script>
  (() => {
    const test1 = document.querySelector('.test1')
    const test2 = document.querySelector('.test2')
    test1.addEventListener('mousedown', () => {
      console.log('test1 mousedown')
    })
    test2.addEventListener('click', () => {
      //  点击 test2 后,触发 test1 的 click 事件
      test1.click()
      test1.mousedown();
    })
  })()
</script>

效果:

77-2

想法在实现上遇到了问题,html 元素上不存在 mousedown 方法,click 方法也只能触发 click 事件,无法触发绑定的 mousedown 事件,那么有办法可以做到吗?答案肯定是有的~~

dispatchEvent 方法

dispatchEvent 方法可以触发任意事件,其参数是一个实例化的 Event 对象。以上例子中使用 dispatchEvent 方法,实现效果如下:

<style>
  button {
    padding: 4px;
    margin-top: 12px;
  }
</style>

<button class="test1">前端路引--事件测试 test1</button><br>
<button class="test2">前端路引--事件测试 test2</button>

<script>
  (() => {
    const test1 = document.querySelector('.test1')
    const test2 = document.querySelector('.test2')
    test1.addEventListener('mousedown', () => {
      console.log('test1 mousedown')
    })
    test2.addEventListener('click', () => {
      //  点击 test2 后,触发 test1 的 click 事件
      const event = new Event('mousedown')
      test1.dispatchEvent(event)
    })
  })()
</script>

效果:

77-3

Event 对象

语法:

new Event(type, options)

该对象有两个参数:

第一个事件类型 type 为必传参数,不传则报错 Uncaught TypeError: Failed to construct 'Event': 1 argument required, but only 0 present.

第二个 options 参数为可选参数,拥有三个属性:
bubbles: 可选,Boolean 类型,默认值为 false,表示该事件是否冒泡。
cancelable: 可选,Boolean 类型,默认值为 false,表示该事件能否被取消。
composed: 可选,Boolean 类型,默认值为 false,指示事件是否会在影子 DOM 根节点之外触发侦听器。

bubbles 冒泡:

关于冒泡很好理解,就是子元素是否能冒泡到父元素,如下例子 test1 触发的事件将会冒泡到容器 test-container 上。

<div class="test-container">
  <button class="test0">前端路引--事件测试 test0</button>
</div>
<button class="test1">前端路引--事件测试 test1</button>
<button class="test2">前端路引--事件测试 test2</button>

<script>
  (() => {
    const test0 = document.querySelector('.test0')
    const test1 = document.querySelector('.test1')
    const test2 = document.querySelector('.test2')
    document.querySelector('.test-container').addEventListener('mousedown', () => {
      console.log('test-container mousedown')
    })
    test1.addEventListener('click', () => {
      // 默认不允许冒泡
      const event = new Event('mousedown')
      test0.dispatchEvent(event)
    })
    test2.addEventListener('click', () => {
      // 配置 bubbles 允许冒泡
      const event = new Event('mousedown', { bubbles: true })
      test0.dispatchEvent(event)
    })
  })()
</script>

效果:

77-4

cancelable 事件能否取消:

true:表示事件是可取消的,调用 event.preventDefault() 会阻止浏览器的默认行为。
false:表示事件不可取消,调用 event.preventDefault() 无效。

实测就算传入的是传入为 false,也能调用 event.preventDefault() 并不会报错,对 Event 对象无效,但是对 MouseEvent 对象有用(参考后文)。

设置此参数可在事件的 event 参数上获取传入的值:

<input type="file" name="" id="" class="file"><br>
<button class="test1">前端路引--事件测试 cancelable: false</button><br>
<button class="test2">前端路引--事件测试 cancelable: true</button>

<script>
  (() => {
    const file = document.querySelector('.file')
    const test1 = document.querySelector('.test1')
    const test2 = document.querySelector('.test2')
    file.addEventListener('click', () => {
      console.log('cancelable', event.cancelable);
    })
    test1.addEventListener('click', () => {
      const event = new Event('click', { cancelable: false })
      file.dispatchEvent(event)
    })
    test2.addEventListener('click', () => {
      const event = new Event('click', { cancelable: true })
      file.dispatchEvent(event)
    })
  })()
</script>

效果:

77-5

dispatchEvent 触发的 Event 对象,并 不会响应 元素本身的默认事件,比如 a 标签的跳转,input:file 的文件选择等。

composed 是否允许穿透 shadow DOM 节点:

如果不使用影子节点,这属性基本没啥用处,影子节点在常规开发中很少使用~~

一个简单示例:

<div class="wrapper">
  <my-component id="host"></my-component>
</div>
<script>
  (() => {
    class MyComponent extends HTMLElement {
      constructor() {
        super();
        const shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = `
          <button id="inner-button1">影子节点内部按钮1--前端路引</button>
          <button id="inner-button2">影子节点内部按钮2--前端路引</button>
        `;
      }
    }
    customElements.define('my-component', MyComponent);

    const host = document.querySelector('#host');
    const innerBtn1 = host.shadowRoot.querySelector('#inner-button1')
    const innerBtn2 = host.shadowRoot.querySelector('#inner-button2')
    innerBtn1.addEventListener('click', () => {
      const event = new Event('custom-event', { 
        bubbles: true,    // 允许在 Shadow DOM 内部冒泡
        composed: false   // 禁止穿透到外部 DOM
      });
      innerBtn1.dispatchEvent(event);
    });
    innerBtn2.addEventListener('click', () => {
      const event = new Event('custom-event', { 
        bubbles: true,    // 允许在 Shadow DOM 内部冒泡
        composed: true   // 允许穿透到外部 DOM
      });
      innerBtn2.dispatchEvent(event);
    });
    // 尝试在外部 DOM 监听事件
    host.addEventListener('custom-event', () => {
      console.log('外部 DOM 监听到事件'); // 不会触发
    });
  })()
</script>

效果:

77-6

CustomEvent 对象

Event 对象没办法传入自定义数据,某些特定需求需要传入自定义参数时,可以祭出 CustomEvent 对象。

CustomEvent 继承 Event,所以 Event 支持的配置都支持,只是多了一个自定义数据字段。

示例:

<button class="test1">前端路引--事件测试 test1</button><br>
<button class="test2">前端路引--事件测试 test2</button>

<script>
  (() => {
    const test1 = document.querySelector('.test1')
    const test2 = document.querySelector('.test2')
    test1.addEventListener('dev', (event) => {
      console.log('自定义数据:', event.detail);
    })
    test2.addEventListener('click', () => {
      //  点击 test2 后,触发 test1 的 click 事件
      const event = new CustomEvent('dev', {
        detail: {
          name: '前端路引',
          age: 1
        }
      })
      test1.dispatchEvent(event)
    })
  })()
</script>

以上代码使用了 CustomEvent 触发了自定义的 dev 事件,并传入了自定义数据。

效果:

77-7

MouseEvent

前面说了 dispatchEvent 触发的 Event 对象不会响应元素本身的默认事件,但可以通过 MouseEvent 对象来触发一些元素本身的默认事件。

如下例子:

<input type="file" name="" id="" class="file"><br>
<button class="test1">前端路引--事件测试 cancelable: false</button><br>
<button class="test2">前端路引--事件测试 cancelable: true</button>

<script>
  (() => {
    const file = document.querySelector('.file')
    const test1 = document.querySelector('.test1')
    const test2 = document.querySelector('.test2')
    file.addEventListener('click', () => {
      event.preventDefault()
      console.log('cancelable', event.cancelable);
    })
    test1.addEventListener('click', () => {
      const event = new MouseEvent('click', { cancelable: false })
      const res = file.dispatchEvent(event)
      console.log(`${!res ? '调用了' : '没调用'} preventDefault`);
    })
    test2.addEventListener('click', () => {
      const event = new MouseEvent('click', { cancelable: true })
      const res = file.dispatchEvent(event)
      console.log(`${!res ? '调用了' : '没调用'} preventDefault`);
    })
  })()
</script>

效果:

77-8

dispatchEvent 返回值当 event 可被取消(cancelable 值为 true),且 event 中至少有一个事件处理程序调用了 Event.preventDefault() 方法时,返回 false。否则,返回 true。

写在最后

按照 MDN 的说法,由程序触发的事件,还有一个专用名词 合成事件,表示不是浏览器本身触发的事件。

除了本文例子中的几个 Event 对象外,还有一些其他对象,在使用时可参考 MDN 文档。

用户交互:
鼠标、键盘、触摸交互 MouseEvent, KeyboardEvent, TouchEvent

表单与输入:
输入框、表单提交 InputEvent, SubmitEvent

媒体控制:
音视频播放、设备流 MediaStreamTrackEvent

拖放与剪贴板:
拖拽操作、复制粘贴 DragEvent, ClipboardEvent

存储与通信:
本地存储、跨文档通信 StorageEvent, MessageEvent

错误与调试:
脚本错误捕获 ErrorEvent

设备与传感器:
方向、加速度检测 DeviceOrientationEvent

动画与过渡:
CSS 动画/过渡生命周期 AnimationEvent, TransitionEvent