|
|
|
|
@ -0,0 +1,266 @@
|
|
|
|
|
# 事件监听器的一些总结
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 如何通过调试控制台查看当前页面绑定的事件(例如window的load,message事件等)
|
|
|
|
|
可以通过多种方式在浏览器开发者工具的调试控制台中查看页面绑定的事件:
|
|
|
|
|
|
|
|
|
|
## 1. **使用 `getEventListeners()` API(Chrome/Edge)**
|
|
|
|
|
```javascript
|
|
|
|
|
// 查看window对象上的所有事件监听器
|
|
|
|
|
getEventListeners(window)
|
|
|
|
|
|
|
|
|
|
// 查看特定事件
|
|
|
|
|
getEventListeners(window).load
|
|
|
|
|
getEventListeners(window).message
|
|
|
|
|
|
|
|
|
|
// 查看document的事件
|
|
|
|
|
getEventListeners(document).click
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**注意:** `getEventListeners()` 仅能在控制台中使用,不能在脚本代码中调用。
|
|
|
|
|
|
|
|
|
|
## 2. **手动查看事件监听器(通用方法)**
|
|
|
|
|
|
|
|
|
|
### 查看通过 `addEventListener` 绑定的事件:
|
|
|
|
|
```javascript
|
|
|
|
|
// 使用原型方法查看所有事件监听器
|
|
|
|
|
EventTarget.prototype._originalAddEventListener = EventTarget.prototype.addEventListener;
|
|
|
|
|
EventTarget.prototype.addEventListener = function(type, listener, options) {
|
|
|
|
|
console.log('Event added:', type, listener);
|
|
|
|
|
this._originalAddEventListener(type, listener, options);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 查看当前已经绑定的事件
|
|
|
|
|
console.log('Onload handler:', window.onload);
|
|
|
|
|
console.log('Onmessage handler:', window.onmessage);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 3. **使用开发者工具的事件监听器面板**
|
|
|
|
|
|
|
|
|
|
1. 打开开发者工具(F12)
|
|
|
|
|
2. 切换到 **Elements** 面板
|
|
|
|
|
3. 选中 `<html>` 元素或文档节点
|
|
|
|
|
4. 在右侧面板中找到 **Event Listeners** 标签页
|
|
|
|
|
5. 展开列表查看所有事件监听器
|
|
|
|
|
|
|
|
|
|
## 4. **查看特定元素的完整方法**
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 获取元素的全部事件监听器
|
|
|
|
|
function getAllEventListeners(element) {
|
|
|
|
|
const listeners = {};
|
|
|
|
|
const eventTypes = [
|
|
|
|
|
'load', 'unload', 'message', 'resize', 'scroll',
|
|
|
|
|
'click', 'keydown', 'keyup', 'mouseover', 'mouseout',
|
|
|
|
|
'focus', 'blur', 'change', 'input', 'submit'
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
eventTypes.forEach(eventType => {
|
|
|
|
|
const eventListeners = getEventListeners(element)[eventType];
|
|
|
|
|
if (eventListeners && eventListeners.length > 0) {
|
|
|
|
|
listeners[eventType] = eventListeners;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return listeners;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用示例
|
|
|
|
|
console.log('Window event listeners:', getAllEventListeners(window));
|
|
|
|
|
console.log('Document event listeners:', getAllEventListeners(document));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 5. **监控新事件绑定**
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 监控所有事件绑定
|
|
|
|
|
['window', 'document', 'HTMLDocument', 'XMLDocument'].forEach(objName => {
|
|
|
|
|
const obj = eval(objName);
|
|
|
|
|
const originalAdd = obj.prototype.addEventListener;
|
|
|
|
|
|
|
|
|
|
obj.prototype.addEventListener = function(type, listener, options) {
|
|
|
|
|
console.group('Event Listener Added');
|
|
|
|
|
console.log('Target:', this);
|
|
|
|
|
console.log('Event Type:', type);
|
|
|
|
|
console.log('Listener:', listener);
|
|
|
|
|
console.log('Options:', options);
|
|
|
|
|
console.trace('Call Stack');
|
|
|
|
|
console.groupEnd();
|
|
|
|
|
|
|
|
|
|
return originalAdd.call(this, type, listener, options);
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 6. **使用 Performance 面板**
|
|
|
|
|
1. 打开 **Performance** 面板
|
|
|
|
|
2. 开始录制
|
|
|
|
|
3. 触发页面事件
|
|
|
|
|
4. 停止录制后,在 **Main** 线程中找到事件处理函数
|
|
|
|
|
|
|
|
|
|
## 7. **跨浏览器兼容方案**
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
function listEventListeners(target) {
|
|
|
|
|
// 尝试使用Chrome的getEventListeners
|
|
|
|
|
if (typeof getEventListeners === 'function') {
|
|
|
|
|
return getEventListeners(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 回退方法:检查常见事件属性
|
|
|
|
|
const events = {};
|
|
|
|
|
const eventProps = Object.keys(target).filter(key =>
|
|
|
|
|
key.startsWith('on') && target[key]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
eventProps.forEach(prop => {
|
|
|
|
|
events[prop.slice(2)] = target[prop];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return events;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用
|
|
|
|
|
console.log('事件监听器:', listEventListeners(window));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 注意事项:
|
|
|
|
|
1. **安全性限制**:某些框架(如React)可能使用事件委托,直接在元素上可能看不到监听器
|
|
|
|
|
2. **Chrome专用**:`getEventListeners()` 是Chrome开发者工具API,不能在生产代码中使用
|
|
|
|
|
3. **动态事件**:页面运行期间动态添加的事件需要使用监控方法才能捕获
|
|
|
|
|
|
|
|
|
|
最实用的方法是直接使用开发者工具的 **Event Listeners** 面板,结合控制台的 `getEventListeners()` 函数来调试事件绑定问题。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# window的加载监听器(**始终使用 `addEventListener`**,避免覆盖风险)
|
|
|
|
|
`window.onload` 和 `window.addEventListener('load', ...)` 都是用来监听页面完全加载完成的事件,但它们有一些重要区别:
|
|
|
|
|
|
|
|
|
|
## 1. **基本用法区别**
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// window.onload - 只能绑定一个处理函数
|
|
|
|
|
window.onload = function() {
|
|
|
|
|
console.log('第一个onload');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 这会覆盖上一个
|
|
|
|
|
window.onload = function() {
|
|
|
|
|
console.log('第二个onload - 会覆盖第一个');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// addEventListener - 可以绑定多个处理函数
|
|
|
|
|
window.addEventListener('load', function() {
|
|
|
|
|
console.log('第一个load事件监听器');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
window.addEventListener('load', function() {
|
|
|
|
|
console.log('第二个load事件监听器 - 两个都会执行');
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 2. **主要区别对比**
|
|
|
|
|
|
|
|
|
|
| 特性 | `window.onload` | `window.addEventListener('load', ...)` |
|
|
|
|
|
|------|----------------|----------------------------------------|
|
|
|
|
|
| **多个监听器** | ❌ 只能有一个 | ✅ 可以添加多个 |
|
|
|
|
|
| **覆盖风险** | ✅ 会覆盖之前的 | ❌ 不会覆盖 |
|
|
|
|
|
| **移除监听** | `window.onload = null` | `removeEventListener()` |
|
|
|
|
|
| **事件捕获/冒泡** | ❌ 不支持 | ✅ 支持第三个参数 |
|
|
|
|
|
| **兼容性** | ✅ 所有浏览器 | ✅ 现代浏览器,IE9+ |
|
|
|
|
|
| **代码组织** | ❌ 全局单一 | ✅ 模块化友好 |
|
|
|
|
|
|
|
|
|
|
## 3. **实际示例**
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 不好的做法 - 可能覆盖其他代码的onload
|
|
|
|
|
window.onload = init;
|
|
|
|
|
// 其他地方的代码
|
|
|
|
|
window.onload = otherFunction; // 覆盖了init
|
|
|
|
|
|
|
|
|
|
// 好的做法 - 可以共存
|
|
|
|
|
window.addEventListener('load', init);
|
|
|
|
|
window.addEventListener('load', otherFunction); // 两个都会执行
|
|
|
|
|
|
|
|
|
|
// 如果需要移除监听器
|
|
|
|
|
function handleLoad() {
|
|
|
|
|
console.log('页面加载完成');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
window.addEventListener('load', handleLoad);
|
|
|
|
|
// 之后可以移除
|
|
|
|
|
window.removeEventListener('load', handleLoad);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 4. **执行时机**
|
|
|
|
|
|
|
|
|
|
两者监听的是同一个事件,执行时机完全相同:
|
|
|
|
|
- 页面所有资源(图片、样式、脚本等)加载完成
|
|
|
|
|
- DOM完全构建完成
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 执行顺序取决于添加顺序
|
|
|
|
|
window.addEventListener('load', () => {
|
|
|
|
|
console.log('监听器1');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
window.onload = () => {
|
|
|
|
|
console.log('onload');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window.addEventListener('load', () => {
|
|
|
|
|
console.log('监听器2');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 输出顺序:
|
|
|
|
|
// 监听器1
|
|
|
|
|
// onload (因为addEventListener先添加)
|
|
|
|
|
// 监听器2
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 5. **现代最佳实践**
|
|
|
|
|
|
|
|
|
|
**推荐使用 `addEventListener`:**
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 推荐使用
|
|
|
|
|
window.addEventListener('load', function() {
|
|
|
|
|
// 初始化操作
|
|
|
|
|
initApp();
|
|
|
|
|
|
|
|
|
|
// 可以安全地与其他代码共存
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 或者使用DOMContentLoaded(如果不需要等资源)
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
// DOM就绪,但图片等资源可能还没加载完
|
|
|
|
|
// 更快的交互初始化
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 6. **特殊情况处理**
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 如果代码可能在页面加载后运行
|
|
|
|
|
if (document.readyState === 'complete') {
|
|
|
|
|
// 页面已经加载完成,直接执行
|
|
|
|
|
init();
|
|
|
|
|
} else {
|
|
|
|
|
// 页面还在加载,添加监听器
|
|
|
|
|
window.addEventListener('load', init);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 或者使用更通用的方式
|
|
|
|
|
function onPageLoad(callback) {
|
|
|
|
|
if (document.readyState === 'complete') {
|
|
|
|
|
setTimeout(callback, 0);
|
|
|
|
|
} else {
|
|
|
|
|
window.addEventListener('load', callback);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## **总结建议**
|
|
|
|
|
1. **始终使用 `addEventListener`**,避免覆盖风险
|
|
|
|
|
2. 如果只需要DOM就绪,使用 `DOMContentLoaded` 更快
|
|
|
|
|
3. 考虑使用 `defer` 属性的script标签,减少对load事件的依赖
|
|
|
|
|
4. 对于库/框架开发,一定要用 `addEventListener` 避免破坏用户代码
|