⚡ 第十四章:事件系统

掌握事件监听、冒泡、捕获和委托

14.1 事件监听

// 添加事件监听器
const button = document.querySelector('button');

// 基本语法
button.addEventListener('click', function(event) {
    console.log('Button clicked!');
});

// 使用箭头函数
button.addEventListener('click', (event) => {
    console.log('Button clicked!');
});

// 移除事件监听器
function handleClick(event) {
    console.log('Button clicked!');
}

button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick);

14.2 事件对象

// 事件对象
button.addEventListener('click', (event) => {
    // 事件类型
    console.log(event.type); // 'click'
    
    // 目标元素
    console.log(event.target); // button element
    
    // 当前元素
    console.log(event.currentTarget); // button element
    
    // 阻止默认行为
    event.preventDefault();
    
    // 阻止冒泡
    event.stopPropagation();
    
    // 鼠标位置
    console.log(event.clientX, event.clientY);
    console.log(event.pageX, event.pageY);
});

14.3 事件冒泡、捕获与委托

事件冒泡与捕获

// HTML 结构
// <div id="outer">
//   <div id="inner">
//     <button id="btn">Click me</button>
//
//
const outer = document.getElementById('outer'); const inner = document.getElementById('inner'); const btn = document.getElementById('btn'); // 冒泡阶段(默认) outer.addEventListener('click', () => { console.log('Outer clicked'); }); inner.addEventListener('click', () => { console.log('Inner clicked'); }); btn.addEventListener('click', () => { console.log('Button clicked'); }); // 点击按钮时的输出顺序: // Button clicked // Inner clicked // Outer clicked // 捕获阶段 outer.addEventListener('click', () => { console.log('Outer clicked (capture)'); }, true); inner.addEventListener('click', () => { console.log('Inner clicked (capture)'); }, true); btn.addEventListener('click', () => { console.log('Button clicked (capture)'); }, true); // 点击按钮时的输出顺序: // Outer clicked (capture) // Inner clicked (capture) // Button clicked (capture)

事件委托

事件委托是一种优化技术,将事件监听器添加到父元素,而不是每个子元素。
// HTML 结构
// <ul id="list">
//   <li>Item 1</li>
//   <li>Item 2</li>
//   <li>Item 3</li>
// </ul>


const list = document.getElementById('list');

// 使用事件委托
list.addEventListener('click', (event) => {
    if (event.target.tagName === 'LI') {
        console.log('List item clicked:', event.target.textContent);
    }
});

// 动态添加的元素也会触发事件
const newItem = document.createElement('li');
newItem.textContent = 'Item 4';
list.appendChild(newItem);

14.4 自定义事件

// 创建自定义事件
const customEvent = new CustomEvent('customEvent', {
    detail: {
        message: 'Hello from custom event',
        time: new Date()
    },
    bubbles: true,
    cancelable: true
});

// 监听自定义事件
const element = document.querySelector('.container');
element.addEventListener('customEvent', (event) => {
    console.log(event.detail.message);
    console.log(event.detail.time);
});

// 触发自定义事件
element.dispatchEvent(customEvent);

14.5 常见交互模式

防抖 (Debounce)

// 防抖函数
function debounce(func, wait) {
    let timeout;
    return function(...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            func.apply(this, args);
        }, wait);
    };
}

// 使用防抖
const handleInput = debounce((event) => {
    console.log('Input value:', event.target.value);
}, 300);

const input = document.querySelector('input');
input.addEventListener('input', handleInput);

节流 (Throttle)

// 节流函数
function throttle(func, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => {
                inThrottle = false;
            }, limit);
        }
    };
}

// 使用节流
const handleScroll = throttle(() => {
    console.log('Scroll position:', window.scrollY);
}, 100);

window.addEventListener('scroll', handleScroll);

动手练习

练习 1:事件监听

  1. 添加事件监听器到按钮
  2. 测试不同类型的事件
  3. 移除事件监听器

练习 2:事件对象

  1. 使用事件对象的属性
  2. 阻止默认行为
  3. 阻止事件冒泡

练习 3:事件委托

  1. 创建一个列表
  2. 使用事件委托处理列表项点击
  3. 动态添加列表项并测试事件

练习 4:防抖与节流

  1. 实现防抖函数
  2. 实现节流函数
  3. 测试在输入和滚动事件中的应用