SyntheticEvent

This commit is contained in:
xion 2024-12-11 01:53:15 +08:00
parent ad3fe496ef
commit dd9f5dcaaa
5 changed files with 195 additions and 0 deletions

90
demo/test-event/event.js Normal file
View File

@ -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();
});

View File

@ -0,0 +1,22 @@
<head>
<meta charset="UTF-8" />
</head>
<div id="root">
<div>1</div>
<div>
222
<div>222-child</div>
</div>
</div>
<div id="app">
<button class="button">Click Me</button>
<a href="#" class="link">Link</a>
<div id="i2">
222
<div id="i2-child">222-child</div>
</div>
</div>
<!-- <script src="./index.js"></script> -->
<script src="./event.js"></script>

69
demo/test-event/index.js Normal file
View File

@ -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(); // 停止冒泡,防止触发父级模块
});

View File

@ -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 <xiongxiao@xiongxiao.me>",
"license": "MIT",
"type": "module"
}

View File

@ -8,6 +8,7 @@
</head>
<body>
<div class="a-[123]">sdfsdf</div>
<script src="./src/main.tsx" type="module"></script>
</body>