Web前端入门第 77 问:JavaScript 由程序触发绑定事件的几种方式
<p>开发中经常会遇这样的需求:点击 A 元素的时候,需要触发 B 元素的事件,比如:点击一个 div 元素,然后触发 input:file 的 click 事件,用来选择文件上传。</p><h2 id="click-方法">click 方法</h2>
<p>以上需求可通过元素的 click 方法触发:</p>
<pre><code class="language-html"><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>
</code></pre>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250714091333550-953091826.gif"></p>
<p>点击事件有 <code>click</code> 方法可以使用,如果需求是点击 A 元素触发 B 元素的 <code>mousedown</code> 事件,那能用 <code>mousedown</code> 方法吗?</p>
<pre><code class="language-html"><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>
</code></pre>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250714091340809-68641409.gif"></p>
<p>想法在实现上遇到了问题,html 元素上不存在 <code>mousedown</code> 方法,<code>click</code> 方法也只能触发 click 事件,无法触发绑定的 mousedown 事件,那么有办法可以做到吗?答案肯定是有的~~</p>
<h2 id="dispatchevent-方法">dispatchEvent 方法</h2>
<p>dispatchEvent 方法可以触发任意事件,其参数是一个实例化的 Event 对象。以上例子中使用 dispatchEvent 方法,实现效果如下:</p>
<pre><code class="language-html"><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>
</code></pre>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250714091349625-176593356.gif"></p>
<h2 id="event-对象">Event 对象</h2>
<p>语法:</p>
<pre><code class="language-js">new Event(type, options)
</code></pre>
<p>该对象有两个参数:</p>
<p>第一个事件类型 <code>type</code> 为必传参数,不传则报错 <code>Uncaught TypeError: Failed to construct 'Event': 1 argument required, but only 0 present.</code></p>
<p>第二个 <code>options</code> 参数为可选参数,拥有三个属性:<br>
<code>bubbles</code>: 可选,Boolean 类型,默认值为 false,表示该事件是否冒泡。<br>
<code>cancelable</code>: 可选,Boolean 类型,默认值为 false,表示该事件能否被取消。<br>
<code>composed</code>: 可选,Boolean 类型,默认值为 false,指示事件是否会在影子 DOM 根节点之外触发侦听器。</p>
<p><strong>bubbles 冒泡:</strong></p>
<p>关于冒泡很好理解,就是子元素是否能冒泡到父元素,如下例子 test1 触发的事件将会冒泡到容器 test-container 上。</p>
<pre><code class="language-html"><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>
</code></pre>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250714091354940-1611482926.gif"></p>
<p><strong>cancelable 事件能否取消:</strong></p>
<p><code>true</code>:表示事件是可取消的,调用 event.preventDefault() 会阻止浏览器的默认行为。<br>
<code>false</code>:表示事件不可取消,调用 event.preventDefault() 无效。</p>
<p>实测就算传入的是传入为 <code>false</code>,也能调用 event.preventDefault() 并不会报错,对 Event 对象无效,但是对 <code>MouseEvent</code> 对象有用(参考后文)。</p>
<p>设置此参数可在事件的 event 参数上获取传入的值:</p>
<pre><code class="language-js"><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>
</code></pre>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250714091359518-1320471408.gif"></p>
<blockquote>
<p>dispatchEvent 触发的 Event 对象,并 <code>不会响应</code> 元素本身的默认事件,比如 a 标签的跳转,input:file 的文件选择等。</p>
</blockquote>
<p><strong>composed 是否允许穿透 shadow DOM 节点:</strong></p>
<p>如果不使用影子节点,这属性基本没啥用处,影子节点在常规开发中很少使用~~</p>
<p>一个简单示例:</p>
<pre><code class="language-html"><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>
</code></pre>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250714091404285-1572185803.gif"></p>
<h2 id="customevent-对象">CustomEvent 对象</h2>
<p><code>Event</code> 对象没办法传入自定义数据,某些特定需求需要传入自定义参数时,可以祭出 <code>CustomEvent</code> 对象。</p>
<p>CustomEvent 继承 Event,所以 Event 支持的配置都支持,只是多了一个自定义数据字段。</p>
<p>示例:</p>
<pre><code class="language-html"><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>
</code></pre>
<p>以上代码使用了 <code>CustomEvent</code> 触发了自定义的 <code>dev</code> 事件,并传入了自定义数据。</p>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250714091408848-715348326.gif"></p>
<h2 id="mouseevent">MouseEvent</h2>
<p>前面说了 dispatchEvent 触发的 Event 对象不会响应元素本身的默认事件,但可以通过 MouseEvent 对象来触发一些元素本身的默认事件。</p>
<p>如下例子:</p>
<pre><code class="language-html"><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>
</code></pre>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/596097/202507/596097-20250714091413319-606584561.gif"></p>
<p>dispatchEvent 返回值当 event 可被取消(cancelable 值为 true),且 event 中至少有一个事件处理程序调用了 Event.preventDefault() 方法时,返回 false。否则,返回 true。</p>
<h2 id="写在最后">写在最后</h2>
<p>按照 MDN 的说法,由程序触发的事件,还有一个专用名词 <code>合成事件</code>,表示不是浏览器本身触发的事件。</p>
<p>除了本文例子中的几个 Event 对象外,还有一些其他对象,在使用时可参考 MDN 文档。</p>
<p>用户交互:<br>
鼠标、键盘、触摸交互<code>MouseEvent</code>, <code>KeyboardEvent</code>, <code>TouchEvent</code></p>
<p>表单与输入:<br>
输入框、表单提交<code>InputEvent</code>, <code>SubmitEvent</code></p>
<p>媒体控制:<br>
音视频播放、设备流<code>MediaStreamTrackEvent</code></p>
<p>拖放与剪贴板:<br>
拖拽操作、复制粘贴<code>DragEvent</code>, <code>ClipboardEvent</code></p>
<p>存储与通信:<br>
本地存储、跨文档通信<code>StorageEvent</code>, <code>MessageEvent</code></p>
<p>错误与调试:<br>
脚本错误捕获<code>ErrorEvent</code></p>
<p>设备与传感器:<br>
方向、加速度检测<code>DeviceOrientationEvent</code></p>
<p>动画与过渡:<br>
CSS 动画/过渡生命周期<code>AnimationEvent</code>, <code>TransitionEvent</code></p>
</div>
<div id="MySignature" role="contentinfo">
<p> </p>
<p style="font-size: 18px;font-weight: bold;">文章首发于微信公众号【<span style="color:rgb(255, 71, 87)">前端路引</span>】,欢迎 <span style="color:#4ec259">微信扫一扫</span> 查看更多文章。</p>
<p>
<img style="max-width: 320px;" src="https://images.cnblogs.com/cnblogs_com/linx/2447020/o_250228035031_%E5%85%AC%E4%BC%97%E5%8F%B7%E4%BA%8C%E7%BB%B4%E7%A0%81.png"/>
</p>
<p>本文来自博客园,作者:前端路引,转载请注明原文链接:https://www.cnblogs.com/linx/p/18983403</p>
<p> </p><br><br>
来源:https://www.cnblogs.com/linx/p/18983403
頁:
[1]