1. eventoverview

event is user and 网页交互时发生 动作, such as点击鼠标, 按 under 键盘, move鼠标etc.. JavaScript 可以througheventprocessing来response这些user交互.

eventprocessing is JavaScript inimplementation交互functions coremechanism, 它使网页able to响application户 operation并做出相应 反应.

2. commoneventclass型

JavaScript support many 种class型 event, 以 under is 一些common eventclass型:

2.1 鼠标event

  • click - 鼠标点击元素时触发
  • dblclick - 鼠标双击元素时触发
  • mouseover - 鼠标指针move to 元素 on 时触发
  • mouseout - 鼠标指针离开元素时触发
  • mousemove - 鼠标指针 in 元素 on move时触发
  • mousedown - 鼠标按钮 in 元素 on 按 under 时触发
  • mouseup - 鼠标按钮 in 元素 on 释放时触发

2.2 键盘event

  • keydown - 键盘按键按 under 时触发
  • keyup - 键盘按键释放时触发
  • keypress - 键盘按键按 under 并释放时触发 (已废弃)

2.3 表单event

  • submit - 表单submitting时触发
  • change - 表单元素 值发生变化时触发
  • input - 表单元素输入时触发
  • focus - 表单元素获得焦点时触发
  • blur - 表单元素失去焦点时触发

2.4 documentation/窗口event

  • load - 页面 or resource加载completion时触发
  • unload - 页面卸载时触发
  • resize - 窗口 big small 改变时触发
  • scroll - 页面滚动时触发
  • DOMContentLoaded - DOM 加载completion时触发

3. event监听器

event监听器 is amechanism, 用于监听specific event并 in event发生时执行相应 processingfunction.

3.1 添加event监听器

// 获取元素 const button = document.getElementById('myButton'); // 添加event监听器 button.addEventListener('click', function() { console.log('按钮被点击了!'); }); // using命名function function handleClick() { console.log('按钮被点击了 (命名function) !'); } button.addEventListener('click', handleClick); // using箭头function button.addEventListener('click', () => { console.log('按钮被点击了 (箭头function) !'); });

3.2 移除event监听器

// 获取元素 const button = document.getElementById('myButton'); // 定义eventprocessingfunction function handleClick() { console.log('按钮被点击了!'); } // 添加event监听器 button.addEventListener('click', handleClick); // 移除event监听器 button.removeEventListener('click', handleClick); // 注意: 匿名function无法被移除 button.addEventListener('click', function() { console.log('这个监听器无法被移除!'); }); // under 面 code不会生效 button.removeEventListener('click', function() { console.log('这个监听器无法被移除!'); });

3.3 event监听器 第三个parameter

// 获取元素 const button = document.getElementById('myButton'); // 添加event监听器, 第三个parameter控制event 捕获/冒泡阶段 button.addEventListener('click', handleClick, { capture: false, // is 否 in 捕获阶段触发 once: false, // is 否只触发一次 passive: false // is 否不阻止默认behavior }); // example: 只触发一次 event监听器 button.addEventListener('click', function() { console.log('这个监听器只会触发一次!'); }, { once: true });

4. eventobject

当event触发时, 浏览器会creation一个eventobject (Event Object) , 并将其serving asparameter传递给eventprocessingfunction. eventobjectpackage含了 and event相关 information.

// 获取元素 const button = document.getElementById('myButton'); // 添加event监听器 button.addEventListener('click', function(event) { console.log('eventobject:', event); console.log('eventclass型:', event.type); console.log('触发event 元素:', event.target); console.log('当 before processingevent 元素:', event.currentTarget); console.log('鼠标点击 X坐标:', event.clientX); console.log('鼠标点击 Y坐标:', event.clientY); console.log(' is 否按 under 了Ctrl键:', event.ctrlKey); console.log(' is 否按 under 了Shift键:', event.shiftKey); console.log(' is 否按 under 了Alt键:', event.altKey); });

4.1 eventobject 常用method

// 阻止默认behavior event.preventDefault(); // 阻止event冒泡 event.stopPropagation(); // 阻止event 进一步捕获 or 冒泡, 并阻止任何otherevent监听器被调用 event.stopImmediatePropagation(); // 获取event 目标元素 event.target; // 获取当 before processingevent 元素 event.currentTarget;

5. event传播

event传播 is 指event from 触发元素向 DOM tree other部分传播 过程. event传播分 for 三个阶段:

5.1 捕获阶段 (Capture Phase)

event from documentation根node向 under 传播 to 目标元素.

5.2 目标阶段 (Target Phase)

event to 达目标元素.

5.3 冒泡阶段 (Bubbling Phase)

event from 目标元素向 on 传播回documentation根node.

// HTML structure //
//
//
点击我
//
//
// 获取元素 const outer = document.getElementById('outer'); const middle = document.getElementById('middle'); const inner = document.getElementById('inner'); // 添加捕获阶段 监听器 outer.addEventListener('click', function() { console.log('Outer - 捕获阶段'); }, { capture: true }); middle.addEventListener('click', function() { console.log('Middle - 捕获阶段'); }, { capture: true }); inner.addEventListener('click', function() { console.log('Inner - 捕获阶段'); }, { capture: true }); // 添加冒泡阶段 监听器 outer.addEventListener('click', function() { console.log('Outer - 冒泡阶段'); }, { capture: false }); middle.addEventListener('click', function() { console.log('Middle - 冒泡阶段'); }, { capture: false }); inner.addEventListener('click', function() { console.log('Inner - 冒泡阶段'); }, { capture: false }); // 点击 inner 元素时 输出顺序: // Outer - 捕获阶段 // Middle - 捕获阶段 // Inner - 捕获阶段 // Inner - 冒泡阶段 // Middle - 冒泡阶段 // Outer - 冒泡阶段

6. event委托

event委托 is aeventprocessing模式, 它利用event冒泡 features, 将event监听器添加 to 父元素 on , 而不 is 每个子元素 on . 当子元素触发event时, event会冒泡 to 父元素, 父元素 event监听器可以processing这些event.

event委托 优点:

  • reducingevent监听器 数量, improvingperformance
  • 可以processing动态添加 子元素 event
  • 简化codestructure
// HTML structure //
    //
  • project 1
  • //
  • project 2
  • //
  • project 3
  • //
// 获取父元素 const todoList = document.getElementById('todo-list'); // 添加event委托 todoList.addEventListener('click', function(event) { // check点击 元素 is 否 is delete按钮 if (event.target.classList.contains('delete')) { // 获取 for 应 list项 const listItem = event.target.closest('li'); if (listItem) { listItem.remove(); console.log('delete了project'); } } }); // 动态添加 new 项 function addTodoItem(text) { const li = document.createElement('li'); li.textContent = text; const deleteButton = document.createElement('button'); deleteButton.textContent = 'delete'; deleteButton.className = 'delete'; li.appendChild(deleteButton); todoList.appendChild(li); } // 添加 new 项 addTodoItem('project 4'); addTodoItem('project 5'); // new 添加 项也会responsedeleteevent, 因 for event委托 in 父元素 on

7. eventprocessingtechniques

7.1 in 联eventprocessing器

直接 in HTML 元素in添加eventprocessingcode (不推荐) .

7.2 DOM propertyeventprocessing器

through JavaScript 设置元素 eventproperty (不推荐) .

// 获取元素 const button = document.getElementById('myButton'); // 设置eventproperty button.onclick = function() { console.log('按钮被点击了!'); }; // 注意: 这种方式只能添加一个eventprocessing器 button.onclick = function() { console.log('这会覆盖之 before 点击eventprocessing器!'); };

7.3 event监听器 (推荐)

using addEventListener() method添加event监听器 (推荐) .

// 获取元素 const button = document.getElementById('myButton'); // 添加 many 个event监听器 button.addEventListener('click', function() { console.log('第一个点击eventprocessing器'); }); button.addEventListener('click', function() { console.log('第二个点击eventprocessing器'); }); // 两个processing器都会执行

8. 自定义event

除了浏览器 in 置 event, JavaScript 还允许creation and 触发自定义event.

// creation自定义event const customEvent = new Event('customEvent', { bubbles: true, // is 否冒泡 cancelable: true, // is 否可取消 composed: false // is 否可以穿越 Shadow DOM edge界 }); // creation带自定义data event const customEventWithData = new CustomEvent('customEventWithData', { bubbles: true, cancelable: true, detail: { message: '这 is 自定义event data', timestamp: new Date().getTime() } }); // 获取元素 const element = document.getElementById('myElement'); // 添加event监听器 element.addEventListener('customEvent', function(event) { console.log('自定义event被触发了!'); }); element.addEventListener('customEventWithData', function(event) { console.log('带data 自定义event被触发了!'); console.log('eventdata:', event.detail); }); // 触发event element.dispatchEvent(customEvent); element.dispatchEvent(customEventWithData);

9. eventperformanceoptimization

eventprocessing可能会影响页面performance, 以 under is 一些optimizationtechniques:

9.1 usingevent委托

such as before 所述, usingevent委托reducingevent监听器 数量.

9.2 移除不需要 event监听器

// component销毁时移除event监听器 class MyComponent { constructor(element) { this.element = element; this.handleClick = this.handleClick.bind(this); this.element.addEventListener('click', this.handleClick); } handleClick() { console.log('元素被点击了'); } destroy() { // 移除event监听器 this.element.removeEventListener('click', this.handleClick); } }

9.3 using passive event监听器

// using passive event监听器improving滚动performance window.addEventListener('scroll', function() { // 滚动eventprocessingcode }, { passive: true }); // 注意: passive for true 时, 不能调用 preventDefault()

9.4 节流 and 防抖

// 防抖function function debounce(func, wait) { let timeout; return function() { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(() => { func.apply(context, args); }, wait); }; } // 节流function function throttle(func, limit) { let inThrottle; return function() { const context = this; const args = arguments; if (!inThrottle) { func.apply(context, args); inThrottle = true; setTimeout(() => { inThrottle = false; }, limit); } }; } // using防抖processing输入event const input = document.getElementById('search-input'); input.addEventListener('input', debounce(function() { console.log('搜索:', this.value); // 执行搜索operation }, 300)); // using节流processing滚动event window.addEventListener('scroll', throttle(function() { console.log('滚动位置:', window.scrollY); // 执行滚动相关operation }, 100));

实践case: 交互式graph片library

creation一个交互式graph片library, implementation以 under functions:

  1. 显示graph片缩略graph
  2. 点击缩略graph时显示 big graph
  3. implementationgraph片 on 一张/ under 一张切换
  4. implementation键盘导航 ( left right 箭头键)
  5. 点击 big graph out 部关闭预览
// HTML structure // // // graph片data const images = [ 'image1.jpg', 'image2.jpg', 'image3.jpg', 'image4.jpg', 'image5.jpg' ]; // 获取元素 const gallery = document.getElementById('gallery'); const lightbox = document.getElementById('lightbox'); const lightboxImg = document.getElementById('lightbox-img'); const closeBtn = document.querySelector('.close'); const prevBtn = document.querySelector('.prev'); const nextBtn = document.querySelector('.next'); let currentIndex = 0; // 显示 big graph function showLightbox(index) { currentIndex = index; lightboxImg.src = images[index]; lightbox.style.display = 'block'; } // 关闭 big graph function closeLightbox() { lightbox.style.display = 'none'; } // 显示 on 一张 function showPrev() { currentIndex = (currentIndex - 1 + images.length) % images.length; lightboxImg.src = images[currentIndex]; } // 显示 under 一张 function showNext() { currentIndex = (currentIndex + 1) % images.length; lightboxImg.src = images[currentIndex]; } // 添加event监听器 // 点击缩略graph gallery.addEventListener('click', function(event) { const thumbnail = event.target.closest('.thumbnail'); if (thumbnail) { const index = parseInt(thumbnail.dataset.index); showLightbox(index); } }); // 关闭按钮 closeBtn.addEventListener('click', closeLightbox); // on 一张按钮 prevBtn.addEventListener('click', showPrev); // under 一张按钮 nextBtn.addEventListener('click', showNext); // 点击 big graph out 部关闭 lightbox.addEventListener('click', function(event) { if (event.target === lightbox) { closeLightbox(); } }); // 键盘导航 window.addEventListener('keydown', function(event) { if (lightbox.style.display === 'block') { if (event.key === 'Escape') { closeLightbox(); } else if (event.key === 'ArrowLeft') { showPrev(); } else if (event.key === 'ArrowRight') { showNext(); } } });

互动练习: eventprocessing实践

请completion以 under eventprocessingtask:

  1. creation一个计数器, 初始值 for 0
  2. 添加 "增加" and "reducing" 按钮
  3. implementation点击按钮时update计数器值
  4. 添加键盘support (+ 键增加, - 键reducing)
  5. 添加reset按钮
  6. 显示operationhistory记录

计数器

0

提示: using键盘 + and - 键也可以operation计数器

operationhistory