9.1 事件委托
在JavaScript中处理事件可能需要复杂的管理,以优化性能和改进应用程序的架构。
在JavaScript中优化事件处理对创建高效的web应用程序至关重要,尤其是在处理大量元素时。在这种背景下,事件委托和避免多余的处理器起着关键作用。
事件委托是一种模式,其中事件处理器设置在父元素上,而不是每个单独的子元素。这使得能够有效地管理大量子元素的事件,因为处理器只为父元素调用一次,而不是为每个元素调用。
没有事件委托的示例:
<!DOCTYPE html>
<html>
<head>
<title>事件委托示例</title>
</head>
<body>
<ul id="list">
<li>元素 1</li>
<li>元素 2</li>
<li>元素 3</li>
</ul>
<script>
const list = document.querySelector('ul');
const items = document.querySelectorAll('#list li');
items.forEach(item => {
item.addEventListener('click', function(event) {
const li = document.createElement('li');
li.innerText = "新元素,未触发事件";
list.appendChild(li);
alert(`你点击了: ${event.target.textContent}`);
});
});
</script>
</body>
</html>
问题: 如果元素列表动态增加,需要为每个新元素添加处理器。
解决方案 — 事件委托示例:
<!DOCTYPE html>
<html>
<head>
<title>事件委托示例</title>
</head>
<body>
<ul id="list">
<li>元素 1</li>
<li>元素 2</li>
<li>元素 3</li>
</ul>
<script>
const list = document.getElementById('list');
list.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
const li = document.createElement('li');
li.innerText = "新元素,事件已触发";
list.appendChild(li);
alert(`你点击了: ${event.target.textContent}`);
}
});
</script>
</body>
</html>
优点:
- 处理器仅添加到父元素
- 动态添加的元素将自动支持事件处理器
9.2 避免多余的处理器
为了避免性能下降,重要的是最小化事件处理器的数量,特别是如果它们附加到多个元素或频繁调用时。几个优化方法:
1. 减少处理器数量: 使用事件委托来减少处理器数量。
2. 在addEventListener中使用 once: 如果事件处理器只需执行一次,使用{ once: true }
选项添加处理器。
示例:
<!DOCTYPE html>
<html>
<head>
<title>一次性事件示例</title>
</head>
<body>
<button id="myButton">点击我!</button>
<script>
const button = document.getElementById('myButton');
button.addEventListener('click', function(event) {
alert('你点击了按钮');
}, { once: true });
</script>
</body>
</html>
3. 防抖和节流: 这些技术对于经常被调用的事件处理器是非常有用的,比如scroll
或resize
。
9.3 防抖
防抖将多个连续的函数调用合并为一个,仅在事件流结束后执行。
示例:
function debounce(func, wait) {
let timeout;
return function(...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
window.addEventListener('resize', debounce(() => {
console.log('窗口大小已调整');
}, 300));
试着改变小部件的宽度以查看结果
防抖示例
在这个例子中,只有当用户停止输入300毫秒后,搜索函数才会被调用。
<!DOCTYPE html>
<html>
<head>
<title>防抖示例</title>
</head>
<body>
<div style="min-height: 55px">
<input type="text" id="searchInput" placeholder="开始输入查询...">
<div id="results"></div>
</div>
<script>
const searchInput = document.getElementById('searchInput');
const results = document.getElementById('results');
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function performSearch(event) {
const query = event.target.value;
results.textContent = '搜索: ' + query;
// 模拟服务器请求
setTimeout(() => {
results.textContent = '你搜索了: ' + query;
}, 500);
}
const debouncedSearch = debounce(performSearch, 300);
searchInput.addEventListener('input', debouncedSearch);
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>防抖示例</title>
</head>
<body>
<input type="text" id="searchInput" placeholder="开始输入查询...">
<div id="results"></div>
<script>
const searchInput = document.getElementById('searchInput');
const results = document.getElementById('results');
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function performSearch(event) {
const query = event.target.value;
results.textContent = 'Searching for: ' + query;
// 模拟服务器请求
setTimeout(() => {
results.textContent = '你搜索了: ' + query;
}, 500);
}
const debouncedSearch = debounce(performSearch, 300);
searchInput.addEventListener('input', debouncedSearch);
</script>
</body>
</html>
9.4 节流
戏弄 节流确保函数在指定的时间间隔内最多调用一次。
示例:
function throttle(func, limit) {
let inThrottle;
return function(...args) {
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
window.addEventListener('scroll', throttle(() => {
console.log('窗口滚动');
}, 300));
另一个节流示例
在这个例子中,滚动处理器不会频繁调用,最多每200毫秒调用一次,有助于防止浏览器因频繁的滚动事件而超载。
<!DOCTYPE html>
<html>
<head>
<title>节流示例</title>
</head>
<body>
<div id="scrollContainer" style="height: 200px; overflow-y: scroll;">
<div style="height: 1000px;"></div>
</div>
<div id="status">开始滚动以查看效果</div>
<script>
const scrollContainer = document.getElementById('scrollContainer');
const status = document.getElementById('status');
function throttle(func, limit) {
let lastFunc;
let lastRan;
return function(...args) {
const context = this;
if (!lastRan) {
func.apply(context, args);
lastRan = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
if ((Date.now() - lastRan) >= limit) {
func.apply(context, args);
lastRan = Date.now();
}
}, limit - (Date.now() - lastRan));
}
};
}
function handleScroll(event) {
status.textContent = '滚动中... ' + new Date().toLocaleTimeString();
}
const throttledScroll = throttle(handleScroll, 200);
scrollContainer.addEventListener('scroll', throttledScroll);
</script>
</body>
</html>
9.5 使用被动监听器优化事件
被动事件监听器 (passive event listeners) 用于提高性能,特别是在处理滚动事件(scroll
)时。当事件处理器被设置为被动时,这意味着它不会调用preventDefault()
,从而允许浏览器优化性能。
示例:
window.addEventListener('scroll', function(event) {
console.log('滚动中');
}, { passive: true });
GO TO FULL VERSION