事件处理

掌握用户交互和事件响应机制

核心概念

什么是事件?

事件是浏览器中发生的动作或发生的事情,如点击、键盘输入、鼠标移动等。JavaScript可以监听这些事件并做出响应。

// 事件监听示例 button.addEventListener('click', function(event) { console.log('按钮被点击了!'); console.log('点击位置:', event.clientX, event.clientY); });

常用事件类型

  • 鼠标事件:click, dblclick, mouseover, mouseout
  • 键盘事件:keydown, keyup, keypress
  • 表单事件:submit, focus, blur, change
  • 窗口事件:load, resize, scroll
  • 触摸事件:touchstart, touchmove, touchend

事件监听演示

交互演示区域

click事件

input事件

悬停我

mouseover/mouseout

事件日志

等待事件发生...

事件冒泡和捕获

事件流机制

// 事件捕获阶段 document.addEventListener('click', handler, true); // 事件冒泡阶段 document.addEventListener('click', handler, false); // 阻止事件冒泡 event.stopPropagation(); // 阻止默认行为 event.preventDefault();

演示区域

外层盒子

内层盒子

点击按钮查看事件流

事件委托

什么是事件委托?

事件委托利用事件冒泡机制,将事件监听器添加到父元素上,而不是为每个子元素单独添加监听器。这样可以提高性能,特别是处理大量动态添加的元素时。

// 传统方式:为每个按钮添加监听器 document.querySelectorAll('.btn').forEach(btn => { btn.addEventListener('click', handleClick); }); // 事件委托:为父元素添加监听器 document.getElementById('list').addEventListener('click', function(e) { if (e.target.classList.contains('btn')) { handleClick(e); } });

动态列表演示

动态按钮列表

点击按钮查看结果

键盘事件

键盘事件类型

// 监听键盘按下 document.addEventListener('keydown', function(e) { console.log('按下的键:', e.key); console.log('键码:', e.keyCode); console.log('是否按下了Ctrl:', e.ctrlKey); }); // 监听键盘释放 document.addEventListener('keyup', function(e) { console.log('释放的键:', e.key); }); // 组合键检测 function handleKeyCombination(e) { if (e.ctrlKey && e.key === 's') { e.preventDefault(); console.log('保存快捷键触发'); } }

键盘测试器

按任意键测试
按键历史:

案例:小华的在线计算器

功能需求

  • • 支持数字按钮点击
  • • 支持加减乘除运算
  • • 支持键盘输入
  • • 支持退格和清空
  • • 实时显示计算结果
  • • 错误处理和提示

实现代码

class Calculator { constructor() { this.display = document.getElementById('display'); this.currentValue = '0'; this.previousValue = ''; this.operator = ''; this.shouldResetDisplay = false; this.init(); } init() { // 数字按钮事件 document.querySelectorAll('.number').forEach(btn => { btn.addEventListener('click', (e) => { this.appendNumber(e.target.textContent); }); }); // 运算符事件 document.querySelectorAll('.operator').forEach(btn => { btn.addEventListener('click', (e) => { this.chooseOperation(e.target.textContent); }); }); // 键盘事件 document.addEventListener('keydown', (e) => { if (e.key >= '0' && e.key <= '9') { this.appendNumber(e.key); } else if (['+', '-', '*', '/'].includes(e.key)) { this.chooseOperation(e.key); } else if (e.key === 'Enter') { this.compute(); } else if (e.key === 'Escape') { this.clear(); } }); } appendNumber(number) { if (this.shouldResetDisplay) { this.currentValue = ''; this.shouldResetDisplay = false; } this.currentValue += number; this.updateDisplay(); } chooseOperation(op) { if (this.currentValue === '') return; if (this.previousValue !== '') { this.compute(); } this.operator = op; this.previousValue = this.currentValue; this.shouldResetDisplay = true; } compute() { let computation; const prev = parseFloat(this.previousValue); const current = parseFloat(this.currentValue); if (isNaN(prev) || isNaN(current)) return; switch (this.operator) { case '+': computation = prev + current; break; case '-': computation = prev - current; break; case '*': computation = prev * current; break; case '/': computation = prev / current; break; default: return; } this.currentValue = computation.toString(); this.operator = ''; this.previousValue = ''; this.shouldResetDisplay = true; this.updateDisplay(); } clear() { this.currentValue = '0'; this.previousValue = ''; this.operator = ''; this.updateDisplay(); } updateDisplay() { this.display.textContent = this.currentValue; } }

实际演示:在线计算器

0

练习题(8个实战练习)

练习1:图片画廊点击事件

创建一个图片画廊,点击图片显示大图,支持键盘导航。

class ImageGallery { constructor(containerId) { this.container = document.getElementById(containerId); this.images = [ 'image1.jpg', 'image2.jpg', 'image3.jpg' ]; this.currentIndex = 0; this.init(); } init() { this.renderThumbnails(); this.bindEvents(); } renderThumbnails() { this.container.innerHTML = ` `; } bindEvents() { // 缩略图点击 this.container.addEventListener('click', (e) => { if (e.target.classList.contains('thumbnail')) { this.showLightbox(parseInt(e.target.dataset.index)); } }); // 键盘事件 document.addEventListener('keydown', (e) => { if (!document.getElementById('lightbox').classList.contains('hidden')) { switch(e.key) { case 'ArrowLeft': this.prevImage(); break; case 'ArrowRight': this.nextImage(); break; case 'Escape': this.closeLightbox(); break; } } }); // 关闭按钮 document.getElementById('closeBtn').addEventListener('click', () => { this.closeLightbox(); }); // 导航按钮 document.getElementById('prevBtn').addEventListener('click', () => { this.prevImage(); }); document.getElementById('nextBtn').addEventListener('click', () => { this.nextImage(); }); } showLightbox(index) { this.currentIndex = index; const lightbox = document.getElementById('lightbox'); const lightboxImage = document.getElementById('lightboxImage'); lightboxImage.src = this.images[index]; lightbox.classList.remove('hidden'); } closeLightbox() { document.getElementById('lightbox').classList.add('hidden'); } prevImage() { this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length; document.getElementById('lightboxImage').src = this.images[this.currentIndex]; } nextImage() { this.currentIndex = (this.currentIndex + 1) % this.images.length; document.getElementById('lightboxImage').src = this.images[this.currentIndex]; } }

练习2:拖拽排序功能

实现一个可拖拽排序的列表。

class DraggableList { constructor(containerId) { this.container = document.getElementById(containerId); this.items = ['项目1', '项目2', '项目3', '项目4']; this.init(); } init() { this.render(); this.bindDragEvents(); } render() { this.container.innerHTML = `
    ${this.items.map((item, index) => `
  • ${item}
  • `).join('')}
`; } bindDragEvents() { const items = this.container.querySelectorAll('.draggable-item'); items.forEach(item => { item.addEventListener('dragstart', this.handleDragStart.bind(this)); item.addEventListener('dragover', this.handleDragOver.bind(this)); item.addEventListener('drop', this.handleDrop.bind(this)); item.addEventListener('dragend', this.handleDragEnd.bind(this)); }); } handleDragStart(e) { e.dataTransfer.setData('text/plain', e.target.dataset.index); e.target.classList.add('dragging'); } handleDragOver(e) { e.preventDefault(); } handleDrop(e) { e.preventDefault(); const draggedIndex = parseInt(e.dataTransfer.getData('text/plain')); const targetIndex = parseInt(e.target.dataset.index); if (draggedIndex !== targetIndex) { const draggedItem = this.items[draggedIndex]; this.items.splice(draggedIndex, 1); this.items.splice(targetIndex, 0, draggedItem); this.render(); this.bindDragEvents(); } } handleDragEnd(e) { e.target.classList.remove('dragging'); } }

练习3-5:基础事件

  • • 表单验证事件
  • • 鼠标悬停提示
  • • 滚动事件处理

练习6-8:高级交互

  • • 实时搜索建议
  • • 手势识别
  • • 自定义右键菜单