原生JS为CustomEvent添加非detail属性供jQuery监听器读取方案问询
请问是否有方案可以在原生JavaScript中为新建事件传递额外属性,无需使用detail字段,且可以被jQuery正常接收?如下例所示,我希望事件对象直接挂载namespace属性,而非将其放在detail中写成detail: {namespace = ''},该写法在原生JS和原生监听器下可正常运行。
var event = new CustomEvent('myEvent', {bubbles: true}) event.namespace = ""; var el = document.querySelector('span'); el.addEventListener("myEvent", function(e){ console.log("namespace is defined?", e.namespace !== undefined); }); el.dispatchEvent(event)
<span></span>
该需求的背景是我使用的部分依赖库基于jQuery开发,会显式读取事件的namespace属性。如果全链路使用jQuery实现事件触发和监听,功能可正常运行:
var el = $('span'); el.on("myEvent", function(e){ console.log("namespace is defined?", e.namespace !== undefined); }); el.trigger('myEvent');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <span><span>
但当前遇到的问题是:使用原生JS创建事件并添加自定义属性后触发,jQuery的监听器无法读取到该属性。由于依赖库的监听器基于jQuery编写,我无权限修改其内部逻辑,且我不希望在自有代码中引入jQuery来实现事件触发:
var event = new CustomEvent('myEvent', {bubbles: true}) event.namespace = ""; var el = document.querySelector('span'); $(el).on("myEvent", function(e){ // I have no control inside here console.log("namespace is defined?", e.namespace !== undefined); }); el.dispatchEvent(event)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <span></span>
请问有没有可行方案可以解决该问题,还是必须使用jQuery实现?
问题根因
jQuery绑定的事件回调收到的不是原生事件对象,而是jQuery自行封装的jQuery.Event实例。jQuery默认只会同步原生事件的标准内置属性到封装对象上,你手动添加的自定义属性不在同步列表里,所以直接读取e.namespace会返回undefined。原生事件对象实际上存在于e.originalEvent属性中,但你无法修改依赖库的读取逻辑,所以需要从属性同步机制入手解决。
可行方案
方案1:复用已有jQuery实例触发事件(实现成本最低)
你的依赖库已经基于jQuery开发,说明页面已经加载了jQuery实例,不需要你额外引入依赖,直接调用即可:
// 直接触发带自定义属性的事件 $('span').trigger({ type: 'myEvent', bubbles: true, namespace: '' });
该方案完全符合jQuery的事件机制,不存在兼容性问题。
方案2:注册全局事件钩子自动同步属性(无需修改触发逻辑)
如果你确实不想在事件触发逻辑中调用jQuery,可以全局注册一个事件钩子,让jQuery在构造封装事件对象时自动同步namespace属性,只需要在依赖库初始化完成后添加一段代码即可:
jQuery.event.special['*'] = { handle: function(event) { const original = event.originalEvent; if (original && original.namespace !== undefined) { event.namespace = original.namespace; } return event.handleObj.handler.apply(this, arguments); } };
添加这段代码后,你原来的原生事件创建、挂载属性、触发逻辑完全不需要修改,jQuery监听器就能正常读取到e.namespace。
方案3:将自定义属性设为可枚举(无需修改jQuery配置)
如果前面两个方案都不适用,你可以在创建事件时用Object.defineProperty定义namespace为可枚举属性,jQuery 3.x版本会自动遍历同步可枚举的自定义属性:
const event = new CustomEvent('myEvent', {bubbles: true}); Object.defineProperty(event, 'namespace', { value: '', enumerable: true, writable: true, configurable: true }); document.querySelector('span').dispatchEvent(event);
该方案不需要改动任何jQuery相关逻辑,只修改事件创建部分代码即可,兼容所有主流浏览器。
内容的提问来源于stack exchange,提问作者lz100

