## 事件委托
“过多事件处理程序”的解决方案是使用**事件委托**。事件委托利用事件冒泡,可以只使用一个事件处理程序来管理一种类型的事件。例如,click 事件冒泡到 document。这意味着可以为整个页面指定一个 onclick 事件处理程序,而不用为每个可点击元素分别指定事件处理程序。比如有以下 HTML:
```html
<ul id="myLinks">
<li id="goSomewhere">Go somewhere</li>
<li id="doSomething">Do something</li>
<li id="sayHi">Say hi</li>
</ul>
```
这里的 HTML 包含 3 个列表项,在被点击时应该执行某个操作。对此,通常的做法是像这样指定 3 个事件处理程序:
```js
let item1 = document.getElementById("goSomewhere");
let item2 = document.getElementById("doSomething");
let item3 = document.getElementById("sayHi");
item1.addEventListener("click", (event) => {
location.href = "http:// www.wrox.com";
});
item2.addEventListener("click", (event) => {
document.title = "I changed the document's title";
});
item3.addEventListener("click", (event) => {
console.log("hi");
});
```
如果对页面中所有需要使用 onclick 事件处理程序的元素都如法炮制,结果就会出现大片雷同的只为指定事件处理程序的代码。使用事件委托,只要给所有元素共同的祖先节点添加一个事件处理程序,就可以解决问题。比如:
```js
let list = document.getElementById("myLinks");
list.addEventListener("click", (event) => {
let target = event.target;
switch(target.id) {
case "doSomething":
document.title = "I changed the document's title";
break;
case "goSomewhere":
location.href = "http:// www.wrox.com";
break;
case "sayHi":
console.log("hi");
break;
}
});
```
这里只给`<ul id="myLinks">`元素添加了一个 onclick 事件处理程序。因为所有列表项都是这个元素的后代,所以它们的事件会向上冒泡,最终都会由这个函数来处理。但事件目标是每个被点击的列表项,只要检查 event 对象的 id 属性就可以确定,然后再执行相应的操作即可。相对于前面不使用事件委托的代码,这里的代码不会导致先期延迟,因为只访问了一个 DOM 元素和添加了一个事件处理程序。结果对用户来说没有区别,但这种方式占用内存更少。所有使用按钮的事件(大多数鼠标事件和键盘事件)都适用于这个解决方案。
只要可行,就应该考虑只给 document 添加一个事件处理程序,通过它处理页面中所有某种类型的事件。相对于之前的技术,事件委托具有如下优点。
* document 对象随时可用,任何时候都可以给它添加事件处理程序(不用等待 DOMContentLoaded 或 load 事件)。这意味着只要页面渲染出可点击的元素,就可以无延迟地起作用
* 节省花在设置页面事件处理程序上的时间。只指定一个事件处理程序既可以节省 DOM 引用,也可以节省时间
* 减少整个页面所需的内存,提升整体性能
最适合使用事件委托的事件包括:click、mousedown、mouseup、keydown 和 keypress。mouseover 和 mouseout 事件冒泡,但很难适当处理,且经常需要计算元素位置(因为 mouseout 会在光标从一个元素移动到它的一个后代节点以及移出元素之外时触发)。
- Svg
- 01.svgIcon组件封装
- 02.clipPathSprites小图标
- html5
- 01.属性-input
- 02.属性-contenteditable
- 03.属性-draggable
- 04.标签-语义化标签
- 05.标签-canvas
- 06.标签-svg
- 07.标签-video
- 11.API-requestAnimationFrame
- 12.API-Storage
- css3
- 01.selector
- 02.border
- 03.background
- 04.color-gradient
- 05.text
- 06.box
- 07.flex
- 08.transition
- 09.cubic-bezier
- 10.animation
- 11.step
- 12.transform-rotate
- 13.transform-scale
- 14.transform-skew
- 15.transform
- 16.matrix
- 17.性能优化
- 18.css数据类型
- demo-animation
- demo-step
- demo-transform
- JavaScript
- 异步加载JS
- 函数
- 函数基础
- 函数名
- 理解参数
- 默认参数值
- 参数扩展与收集
- 函数内部
- 函数属性与方法
- 函数表达式
- 递归
- 闭包
- 立即调用的函数表达式
- 私有变量
- 函数的应用
- JS基础
- JS语言概述
- 数据与数据类型
- 操作符
- 变量声明
- 通过变量使用原始值与引用值
- 确定类型
- 标识符查找
- 执行上下文与作用域
- 垃圾回收
- Date
- 基本引用类型
- 包装类型-Boolean
- 包装类型-Number
- 包装类型-String
- 单例内置对象-Global
- 单例内置对象-Math
- 模版字符串
- try-catch与错误对象
- 对象
- 理解对象
- 创建对象
- 原型模式
- 继承
- 类
- 符号
- 普通符号
- 共享符号
- 知名符号
- 解构
- 对象解构
- 数组解构
- 数组
- 数组概述
- 数组方法
- 定型数组
- Map和Set
- Map
- Set
- WeakMap
- WeakSet
- 迭代器和生成器
- 迭代器
- 生成器
- 迭代与扩展操作
- 代理与反射
- 代理
- 反射
- 代理模式
- 异步处理
- 事件循环
- 事件和回调函数的缺陷
- 异步处理的通用模型
- Promise的基本使用
- Promise的API
- Promise的连锁与合成
- Promise扩展
- 异步函数
- 异步函数策略
- 其他案例
- 网络请求与远程资源
- 跨源资源共享
- XMLHttpRequest对象
- FetchAPI-基本用法
- FetchAPI-常见请求模式
- FetchAPI-Headers
- FetchAPI-Request
- FetchAPI-Response
- FetchAPI-body混入
- 安全
- BOM
- 窗口
- 视口
- 导航与打开新窗口
- location对象
- 操作地址
- navigator对象
- screen对象
- history对象
- DOM
- 节点层级
- 节点层级概述
- Node类型
- Document类型
- Element类型
- Text类型
- Comment类型
- DocumentType类型
- DocumentFragment类型
- CDATASection类型
- Attr类型
- DOM编程
- 动态脚本
- 动态样式
- 操作表格
- 使用NodeList
- MutationObserver
- MutationObserver接口
- MutationObserverInit与观察范围
- 异步回调与记录队列
- 性能和内存与垃圾回收
- DOM扩展
- SelectorsAPI
- 元素遍历
- CSS类扩展
- 焦点管理
- HTMLDocument扩展
- 插入标记
- 滚动
- 专有扩展
- DOM2和DOM3
- DOM的演进
- 样式
- 元素尺寸
- 事件
- 事件流
- 事件处理程序
- 事件对象
- 事件类型
- 事件委托
- 删除事件处理程序
- 模拟事件
- 用户界面事件
- load事件
- unload事件
- resize事件
- scroll事件
- 设备事件
- orientationchange事件
- deviceorientation事件
- devicemotion事件
- 焦点事件
- 鼠标和滚轮事件
- 键盘与输入事件
- HTML5事件
- contextmenu事件
- beforeunload事件
- DOMContentLoaded事件
- readystatechange事件
- pageshow与pagehide事件
- hashchange事件
- 触摸及手势事件
- 触摸事件
- 手势事件
- vue2
- mysite项目
- 01.搭建工程
- 02.Icon组件
- 03.Pager组件
- 04.测试组件
- 05.Empty组件
- 06.ImageLoader组件
- 07.Contact组件
- 08.Menu组件
- 09.SiteAside组件
- 10.Layout组件
- 11.路由
- 12.弹出消息
- 13.获取远程数据
- 14.Home组件
- 15.vLoading指令
- 16.组件混入
- 17.组件递归
- 18.文章列表页
- 19.文章详情页-1
- 20.文章详情页-2
- 21.文章详情页-3
- 22.事件总线
- 23.文章详情页-4
- 24.文章详情页-5
- 25.图片懒加载
- 26.使用vuex
- 27.标题统一处理
- 28.关于我
- 29.项目&效果
- 30.留言板
- 31.打包结果分析
- 32.异步组件
- 33.无数据显示
- 34.404页面
- 35.vuex优化
- mysite项目后台
- 01.项目准备
- 01.核心概念
- 02.组件
- 03.插槽
- 04.动态组件
- 05.异步组件
- 06.计算属性
- 07.指令
- 08.事件处理
- 09.filter过滤器
- 10.组件生命周期
- 11.自定义指令
- 12.组件混入
- 13.$listeners
- 14.v-model
- 15.过渡和动画
- 16.keep-alive
- 17.插件
- 89.mini观察者
- 90.render
- 91.父子组件通信
- 92.虚拟dom详解
- 93.数据响应原理
- 94.diff
- 95.Vue项目优化
- 96.路由
- 97.数据共享
- 98.vuex案例
- 99.vue-cli
- vue3
- 01.搭建工程
- 02.vue3的重大变化
- 03.vite原理
- 04.效率的提升
- 05.API和数据响应式的变化
- 06.模板中的变化
- 07.组件中的变化
- 08.ReactivityApi
- 09.CompositionApi
- 10.共享数据
- 11.slot透传
- 面试题
- 网络
- js
- git
- 01.基本概览
- 02.git命令
- 03.远程分支
- 04.source tree
- 05.git部署pages
- 06.git工作流
- vscode
- 插件推荐
- 快捷键
- 正则表达式
- 正则基础
- 包管理工具
- npm
- 01.概述
- 02.包的安装
- 03.包配置
- 04.包的使用
- 05.语义版本
- 06.npm脚本
- 07.运行环境配置
- 08.其他npm命令
- 09.发布包
- yarn
- 01.yarn简介
- 02.yarn的核心命令
- 03.yarn的其他命令
- 其他包管理器
- 01.cnpm
- 02.nvm
- 03.pnpm
- 模块化
- 01.模块化发展史
- 02.CommonJS
- 03.AMD和CMD
- 04.ES6模块化
- webpack
- 核心功能
- 01.如何在浏览器端实现模块化
- 02.webpack安装与使用
- 03.模块化兼容性
- 04.编译结果分析
- 05.配置文件
- 06.devtool配置
- 07.编译过程
- 08.入口和出口
- 09.入口和出口的最佳实践
- 10.loader
- 11.plugin
- 12.区分环境
- loader-处理图片
- loader-处理样式
- plugin-添加文件列表
- 13.其他细节配置
- 计算机组成原理
- 系统总线
- 存储器
- TypeScript
- 在node中搭建ts开发环境
- 基本类型检查
- 类型别名
- 枚举
- 模块化
- 接口
- 类
- 泛型
- 类型兼容性
- 面向对象
- 装饰器
- 类型演算
- 声明文件
- TS常用配置
- 数据结构与算法
- 线性数据结构
- 排序
- 栈和队列
- 树
- 图
- 动态规划
- webApp
- 移动端基础
- 移动端适配
- 移动端事件
- 移动端手势库
- 在本地配置https证书
- h5调用本地摄像头
- h5重力加速度
- h5横竖屏切换
- h5方向变化事件
- h5获取地理信息
- 移动端常见问题
- nodejs
- nodejs-概述.md
- nodejs-全局对象
- nodejs-模块化
- nodejs-内置模块
- nodejs-文件
- nodejs-生命周期
- nodejs-组成原理
- 进程和线程
- cookie
- JWT
- CORS
- CSRF攻击
- XSS攻击
- websocket
- 缓存的基本原理
- 常见案例
- 接口文件下载xlsx
- _设置
- image