From dd9f5dcaaa027ecfb511cfae43b1d43dabcc381a Mon Sep 17 00:00:00 2001 From: xion Date: Wed, 11 Dec 2024 01:53:15 +0800 Subject: [PATCH] SyntheticEvent --- demo/test-event/event.js | 90 ++++++++++++++++++++++++++++++++++++ demo/test-event/index.html | 22 +++++++++ demo/test-event/index.js | 69 +++++++++++++++++++++++++++ demo/test-event/package.json | 13 ++++++ index.html | 1 + 5 files changed, 195 insertions(+) create mode 100644 demo/test-event/event.js create mode 100644 demo/test-event/index.html create mode 100644 demo/test-event/index.js create mode 100644 demo/test-event/package.json diff --git a/demo/test-event/event.js b/demo/test-event/event.js new file mode 100644 index 0000000..53c8cd6 --- /dev/null +++ b/demo/test-event/event.js @@ -0,0 +1,90 @@ +class SyntheticEvent { + constructor(nativeEvent) { + this.nativeEvent = nativeEvent; + this.target = nativeEvent.target; + this.currentTarget = null; + this.defaultPrevented = false; + this.propagationStopped = false; // 添加冒泡停止标志 + } + + preventDefault() { + this.nativeEvent.preventDefault(); + this.defaultPrevented = true; + } + + stopPropagation() { + this.propagationStopped = true; // 标记 SyntheticEvent 冒泡停止 + } +} + +class EventSystem { + constructor(root) { + this.root = root; + this.handlers = {}; + } + + on(type, selector, handler) { + if (!this.handlers[type]) { + this.handlers[type] = []; + // 绑定到根元素 + this.root.addEventListener(type, (event) => this.dispatch(event, type)); + } + this.handlers[type].push({ selector, handler }); + } + + dispatch(nativeEvent, type) { + const syntheticEvent = new SyntheticEvent(nativeEvent); + let target = nativeEvent.target; + + // 模拟冒泡过程 + while (target && target !== this.root) { + if (syntheticEvent.propagationStopped) { + break; // 如果已停止冒泡,则退出 + } + + this.handlers[type]?.forEach(({ selector, handler }) => { + if (target.matches(selector)) { + syntheticEvent.currentTarget = target; // 当前目标元素 + handler(syntheticEvent); + + if (syntheticEvent.propagationStopped) { + return; // 停止事件的进一步处理 + } + } + }); + + target = target.parentElement; // 向父节点继续冒泡 + } + } +} + +// 获取根节点 +const root = document.getElementById('app'); + +// 创建事件系统 +const eventSystem = new EventSystem(root); + +// 注册事件 +eventSystem.on('click', '.button', (event) => { + console.log('Button clicked:', event.currentTarget.innerText); + event.preventDefault(); +}); + +eventSystem.on('click', '.link', (event) => { + console.log('Link clicked:', event.currentTarget.href); + // event.stopPropagation(); + // event.preventDefault(); +}); + +eventSystem.on('click', '#i2', (event) => { + // console.log('i2 clicked:', event.currentTarget.innerText); + console.log('i2 clicked:', event.currentTarget); + // event.preventDefault(); +}); + +eventSystem.on('click', '#i2-child', (event) => { + // console.log('i2-child clicked:', event.currentTarget.innerText); + console.log('i2-child clicked:', event.currentTarget); + // event.preventDefault(); + event.stopPropagation(); +}); diff --git a/demo/test-event/index.html b/demo/test-event/index.html new file mode 100644 index 0000000..5b05025 --- /dev/null +++ b/demo/test-event/index.html @@ -0,0 +1,22 @@ + + + + +
+
1
+
+ 222 +
222-child
+
+
+
+ + Link +
+ 222 +
222-child
+
+
+ + + \ No newline at end of file diff --git a/demo/test-event/index.js b/demo/test-event/index.js new file mode 100644 index 0000000..fb29618 --- /dev/null +++ b/demo/test-event/index.js @@ -0,0 +1,69 @@ +const root = document.getElementById('root'); + +// 全局事件管理对象 +const eventRegistry = []; + +// 注册事件方法 +function delegateEvent(selector, callback) { + const newEntry = { selector, callback }; + + // 获取新选择器对应的元素 + const newElement = document.querySelector(selector); + if (!newElement) return; // 如果选择器无效,直接返回 + + // 动态插入到 eventRegistry,并根据包含关系排序 + let inserted = false; + for (let i = 0; i < eventRegistry.length; i++) { + const existingElement = document.querySelector(eventRegistry[i].selector); + if (existingElement && existingElement.contains(newElement)) { + // 当前选择器包含新选择器,新选择器应该插在当前选择器后面 + eventRegistry.splice(i + 1, 0, newEntry); + inserted = true; + break; + } else if (newElement.contains(existingElement)) { + // 新选择器包含当前选择器,新选择器应该插在当前选择器前面 + eventRegistry.splice(i, 0, newEntry); + inserted = true; + break; + } + } + + // 如果没有找到合适的位置,直接追加到末尾 + if (!inserted) { + eventRegistry.push(newEntry); + } +} + +// 为 Event 添加一个标志来记录是否已停止冒泡 +(function () { + const originalStopPropagation = Event.prototype.stopPropagation; + Event.prototype.stopPropagation = function () { + this.propagationStopped = true; + originalStopPropagation.call(this); // 调用原始的 stopPropagation 方法 + }; +})(); + +// 统一事件处理函数 +root.addEventListener('click', (event) => { + const target = event.target; + + // 遍历排序后的注册表 + for (const { selector, callback } of eventRegistry.reverse()) { + if (event.propagationStopped) break; // 停止冒泡时中断匹配 + + const element = document.querySelector(selector); + if (element && (element === target || element.contains(target))) { + callback(event); + } + } +}); + +// 示例:为 `222-child` 和 `222` 添加点击事件 +delegateEvent('#root > div:nth-child(2)', (event) => { + console.log('222 被点击'); +}); + +delegateEvent('#root > div:nth-child(2) > div', (event) => { + console.log('222-child 被点击'); + event.stopPropagation(); // 停止冒泡,防止触发父级模块 +}); diff --git a/demo/test-event/package.json b/demo/test-event/package.json new file mode 100644 index 0000000..21cdc96 --- /dev/null +++ b/demo/test-event/package.json @@ -0,0 +1,13 @@ +{ + "name": "test-event", + "version": "0.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "abearxiong ", + "license": "MIT", + "type": "module" +} diff --git a/index.html b/index.html index 2372e2c..ceca23f 100644 --- a/index.html +++ b/index.html @@ -8,6 +8,7 @@ +
sdfsdf