MST

星途 面试题库

面试题:JavaScript事件代理的优势及复杂场景应用

说明JavaScript事件代理的主要优势。假设有一个动态生成的无序列表,列表项会不断增加和删除,并且每个列表项都有点击事件,要求通过事件代理实现该功能,同时考虑如何优化性能以应对大量列表项的情况,用代码示例说明。
34.9万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

JavaScript事件代理的主要优势

  1. 减少内存占用:不必为每个元素都绑定事件处理函数,只在父元素上绑定一个事件处理函数,减少了内存中事件处理函数的数量。
  2. 动态元素支持:对于动态添加或删除的元素,无需重新绑定事件,只要它们在代理元素的子元素范围内,事件代理依然有效。

代码示例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件代理示例</title>
</head>

<body>
    <ul id="myList"></ul>
    <button onclick="addItem()">添加列表项</button>
    <button onclick="removeItem()">删除列表项</button>

    <script>
        const list = document.getElementById('myList');

        list.addEventListener('click', function (event) {
            if (event.target.tagName === 'LI') {
                console.log('点击了列表项: ', event.target.textContent);
            }
        });

        function addItem() {
            const newItem = document.createElement('li');
            newItem.textContent = `新列表项 ${new Date().getTime()}`;
            list.appendChild(newItem);
        }

        function removeItem() {
            const items = list.getElementsByTagName('li');
            if (items.length > 0) {
                list.removeChild(items[0]);
            }
        }
    </script>
</body>

</html>

性能优化

  1. 事件委托到最近的非动态父元素:避免将事件委托到 documentbody 等顶层元素,尽量委托到最近的不会频繁变动的父元素,减少不必要的事件触发。
  2. 使用事件捕获:可以尝试使用事件捕获阶段(addEventListener 的第三个参数设为 true),在事件从顶层向目标元素传播时就处理,可能减少事件处理开销。
list.addEventListener('click', function (event) {
    if (event.target.tagName === 'LI') {
        console.log('点击了列表项: ', event.target.textContent);
    }
}, true);
  1. 防抖和节流:如果列表项点击事件可能频繁触发,可以使用防抖(debounce)或节流(throttle)技术,防止短时间内多次触发事件处理函数带来的性能问题。例如,使用防抖函数:
function debounce(func, delay) {
    let timer;
    return function () {
        const context = this;
        const args = arguments;
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(context, args);
        }, delay);
    };
}

const debouncedClick = debounce(function (event) {
    if (event.target.tagName === 'LI') {
        console.log('点击了列表项: ', event.target.textContent);
    }
}, 300);

list.addEventListener('click', debouncedClick);