什么是JS事件代理?
JS事件代理(Event Delegation)是一种常用的绑定事件的技巧,它的原理是利用事件冒泡,把原本需要绑定在子元素的响应事件委托给父元素,从而减少事件处理器的数量,提高性能和灵活性。
为什么要使用JS事件代理?
使用JS事件代理有以下几个优点:
- 减少内存消耗,提高性能。如果有很多子元素需要绑定相同的事件,那么使用事件代理可以避免为每个子元素都创建一个事件处理器,从而节省内存空间和注册时间。
- 动态添加或删除子元素时无需重新绑定事件。如果使用事件代理,那么只需要在父元素上绑定一次事件,就可以自动适应子元素的变化,无论是新增还是删除子元素,都不影响事件的触发。
- 方便修改或扩展功能。如果使用事件代理,那么只需要修改或添加父元素的事件处理器,就可以实现对所有子元素的功能修改或扩展,而不需要逐个修改或添加子元素的事件处理器。
JS事件代理的应用场景
最近在学习React所以下面的示例用它来写...
列表或表格中的操作按钮
如果有一个列表或表格中有很多行,每行都有一个操作按钮,例如删除或编辑等,那么可以使用事件代理来为所有的按钮绑定一个点击事件,而不需要为每个按钮都创建一个点击事件。
import React from 'react';
class Table extends React.Component {
handleClick = (e) => {
if (e.target.nodeName === 'BUTTON') {
const id = e.target.getAttribute('data-id');
// 处理点击事件
console.log(`delete row ${id}`);
}
}
render() {
const data = [
{ id: 1, name: 'John Doe', age: 30 },
{ id: 2, name: 'Jane Smith', age: 25 },
{ id: 3, name: 'Bob Johnson', age: 40 }
];
return (
<table onClick={this.handleClick}>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Age</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{data.map(row => (
<tr key={row.id}>
<td>{row.id}</td>
<td>{row.name}</td>
<td>{row.age}</td>
<td><button data-id={row.id}>Delete</button></td>
</tr>
))}
</tbody>
</table>
);
}
}
export default Table;
导航栏或菜单栏中的链接
如果有一个导航栏或菜单栏中有很多链接,每个链接都需要跳转到不同的页面或执行不同的功能,那么可以使用事件代理来为所有的链接绑定一个点击事件,而不需要为每个链接都创建一个点击事件。
import React from 'react';
class Navigation extends React.Component {
handleClick = (e) => {
e.preventDefault();
const path = e.target.getAttribute('href');
// 处理点击事件
console.log(`navigate to ${path}`);
}
render() {
const links = [
{ label: 'Home', path: '/' },
{ label: 'About', path: '/about' },
{ label: 'Contact', path: '/contact' }
];
return (
<nav onClick={this.handleClick}>
<ul>
{links.map(link => (
<li key={link.path}>
<a href={link.path}>{link.label}</a>
</li>
))}
</ul>
</nav>
);
}
}
export default Navigation;
动态生成的内容
如果有一些内容是动态生成的,例如通过AJAX获取数据后渲染到页面上,那么可以使用事件代理来为这些内容绑定事件,而不需要在每次生成内容后都重新绑定事件。
import React, { useState, useEffect } from 'react';
import axios from 'axios';
function List() {
const [list, setList] = useState([]);
useEffect(() => {
async function fetchData() {
const response = await axios.get('https://example.com/api/list');
setList(response.data);
}
fetchData();
}, []);
function handleDelete(e) {
const itemId = e.target.dataset.id;
setList(list.filter(item => item.id !== itemId));
}
return (
<ul onClick={handleDelete}>
{list.map(item => (
<li key={item.id}>
<span>{item.name}</span>
<button data-id={item.id}>Delete</button>
</li>
))}
</ul>
);
}
export default List;
在上面的示例代码中,点击删除按钮会触发父元素ul的click事件,然后通过事件对象获取到被点击的按钮,进一步获取到对应的列表项的id,最后从列表中删除该项。这种方式避免了为每个删除按钮都创建一个点击事件的问题。
总结
使用事件代理存在几个优点:
- 减少整个页面所需的内存,提升整体性能
- 动态绑定,减少重复工作
使用事件代理也存在局限性:
focus、blur这些事件没有事件冒泡机制,所以无法进行委托绑定事件mousemove、mouseout这样的事件,需要不断通过位置去计算定位,对性能消耗高,因此不适合代理