Appearance
事件处理
事件是用户或浏览器执行的某种动作,JavaScript 可以通过事件处理来响应用户交互。
事件绑定
HTML 属性(不推荐)
html
<button onclick="alert('点击了')">点击</button>DOM 属性
javascript
const button = document.getElementById('myButton');
button.onclick = function() {
console.log('点击了');
};
// 移除事件
button.onclick = null;addEventListener(推荐)
javascript
const button = document.getElementById('myButton');
function handleClick(event) {
console.log('点击了', event);
}
// 添加事件监听
button.addEventListener('click', handleClick);
// 移除事件监听
button.removeEventListener('click', handleClick);
// 添加一次性事件
button.addEventListener('click', function() {
console.log('只执行一次');
}, { once: true });事件选项
javascript
element.addEventListener('click', handler, {
capture: false, // 是否在捕获阶段触发
once: true, // 是否只执行一次
passive: true // 是否不调用 preventDefault
});事件对象
javascript
element.addEventListener('click', function(event) {
// 事件类型
console.log(event.type); // 'click'
// 触发事件的元素
console.log(event.target);
// 绑定事件的元素
console.log(event.currentTarget);
// 事件时间戳
console.log(event.timeStamp);
// 阻止默认行为
event.preventDefault();
// 阻止事件冒泡
event.stopPropagation();
// 阻止后续监听器
event.stopImmediatePropagation();
});常用事件
鼠标事件
javascript
// 点击
element.addEventListener('click', handler);
element.addEventListener('dblclick', handler);
// 鼠标按下/抬起
element.addEventListener('mousedown', handler);
element.addEventListener('mouseup', handler);
// 鼠标移入/移出
element.addEventListener('mouseenter', handler); // 不冒泡
element.addEventListener('mouseleave', handler); // 不冒泡
element.addEventListener('mouseover', handler); // 冒泡
element.addEventListener('mouseout', handler); // 冒泡
// 鼠标移动
element.addEventListener('mousemove', handler);
// 鼠标坐标
element.addEventListener('click', function(e) {
console.log('相对元素:', e.offsetX, e.offsetY);
console.log('相对视口:', e.clientX, e.clientY);
console.log('相对页面:', e.pageX, e.pageY);
console.log('相对屏幕:', e.screenX, e.screenY);
});键盘事件
javascript
// 按下
document.addEventListener('keydown', function(e) {
console.log('按键:', e.key);
console.log('键码:', e.code);
console.log('KeyCode:', e.keyCode); // 已废弃
console.log('Ctrl:', e.ctrlKey);
console.log('Shift:', e.shiftKey);
console.log('Alt:', e.altKey);
console.log('Meta:', e.metaKey);
});
// 抬起
document.addEventListener('keyup', handler);
// 输入(字符键)
input.addEventListener('keypress', handler); // 已废弃表单事件
javascript
const form = document.getElementById('myForm');
const input = document.getElementById('myInput');
// 提交
form.addEventListener('submit', function(e) {
e.preventDefault(); // 阻止表单提交
console.log('表单提交');
});
// 重置
form.addEventListener('reset', handler);
// 输入
input.addEventListener('input', function(e) {
console.log('输入值:', e.target.value);
});
// 值改变(失去焦点时)
input.addEventListener('change', handler);
// 获得焦点
input.addEventListener('focus', handler);
input.addEventListener('focusin', handler); // 冒泡
// 失去焦点
input.addEventListener('blur', handler);
input.addEventListener('focusout', handler); // 冒泡
// 选择文本
input.addEventListener('select', handler);焦点事件
javascript
// 获得焦点
element.addEventListener('focus', function(e) {
console.log('获得焦点');
});
// 失去焦点
element.addEventListener('blur', function(e) {
console.log('失去焦点');
});滚动事件
javascript
window.addEventListener('scroll', function(e) {
console.log('滚动位置:', window.scrollY);
});
// 节流优化
let ticking = false;
window.addEventListener('scroll', function() {
if (!ticking) {
window.requestAnimationFrame(function() {
console.log('滚动位置:', window.scrollY);
ticking = false;
});
ticking = true;
}
});窗口事件
javascript
// 页面加载完成
window.addEventListener('load', handler);
// DOM 加载完成
document.addEventListener('DOMContentLoaded', handler);
// 窗口大小改变
window.addEventListener('resize', handler);
// 页面卸载
window.addEventListener('beforeunload', function(e) {
e.preventDefault();
e.returnValue = '确定离开吗?';
});
// 页面隐藏/显示
document.addEventListener('visibilitychange', function() {
console.log('页面可见:', !document.hidden);
});触摸事件
javascript
// 触摸开始
element.addEventListener('touchstart', function(e) {
console.log('触摸点:', e.touches);
});
// 触摸移动
element.addEventListener('touchmove', function(e) {
e.preventDefault(); // 阻止滚动
});
// 触摸结束
element.addEventListener('touchend', handler);
// 触摸取消
element.addEventListener('touchcancel', handler);事件委托
利用事件冒泡,在父元素上处理子元素的事件:
javascript
// 不推荐:为每个元素绑定事件
document.querySelectorAll('.item').forEach(item => {
item.addEventListener('click', handler);
});
// 推荐:事件委托
document.getElementById('list').addEventListener('click', function(e) {
if (e.target.classList.contains('item')) {
console.log('点击了:', e.target);
}
// 使用 matches
if (e.target.matches('.item')) {
console.log('点击了:', e.target);
}
// 使用 closest
const item = e.target.closest('.item');
if (item) {
console.log('点击了:', item);
}
});自定义事件
javascript
// 创建自定义事件
const event = new CustomEvent('myEvent', {
detail: { message: 'Hello' },
bubbles: true,
cancelable: true
});
// 监听自定义事件
element.addEventListener('myEvent', function(e) {
console.log('自定义事件:', e.detail.message);
});
// 触发自定义事件
element.dispatchEvent(event);事件流
事件流分为三个阶段:捕获阶段、目标阶段、冒泡阶段。
javascript
// 冒泡阶段(默认)
parent.addEventListener('click', function() {
console.log('父元素 - 冒泡');
});
// 捕获阶段
parent.addEventListener('click', function() {
console.log('父元素 - 捕获');
}, true);
// 点击子元素时的执行顺序:
// 1. 父元素 - 捕获
// 2. 子元素 - 目标
// 3. 父元素 - 冒泡实践示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>事件处理示例</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.box {
width: 200px;
height: 200px;
background: linear-gradient(135deg, #667eea, #764ba2);
margin: 20px 0;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 18px;
user-select: none;
transition: transform 0.1s;
}
.box:active {
transform: scale(0.98);
}
.keyboard-display {
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
margin: 20px 0;
}
.key {
display: inline-block;
padding: 10px 15px;
background: white;
border: 1px solid #ddd;
border-radius: 4px;
margin: 5px;
min-width: 40px;
text-align: center;
}
.key.active {
background: #007bff;
color: white;
border-color: #007bff;
}
.form-demo {
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
margin: 20px 0;
}
.form-demo input {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
margin: 5px;
}
.log {
height: 150px;
overflow-y: auto;
background: #1e1e1e;
color: #d4d4d4;
padding: 10px;
border-radius: 4px;
font-family: monospace;
font-size: 14px;
margin-top: 20px;
}
.log-entry {
margin: 5px 0;
}
.log-entry.mouse { color: #4ec9b0; }
.log-entry.keyboard { color: #ce9178; }
.log-entry.form { color: #b5cea8; }
</style>
</head>
<body>
<h1>事件处理示例</h1>
<h2>鼠标事件</h2>
<div class="box" id="mouseBox">点击或拖动</div>
<h2>键盘事件</h2>
<div class="keyboard-display">
<span class="key" data-key="Control">Ctrl</span>
<span class="key" data-key="Shift">Shift</span>
<span class="key" data-key="Alt">Alt</span>
<span class="key" data-key="Enter">Enter</span>
<span class="key" data-key="Space">Space</span>
</div>
<p>按下键盘按键查看效果</p>
<h2>表单事件</h2>
<div class="form-demo">
<input type="text" id="textInput" placeholder="输入内容...">
<input type="checkbox" id="checkboxInput">
<label for="checkboxInput">复选框</label>
</div>
<h2>事件日志</h2>
<div class="log" id="log"></div>
<script>
// 日志函数
function log(message, type = '') {
const logDiv = document.getElementById('log');
const entry = document.createElement('div');
entry.className = 'log-entry ' + type;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logDiv.appendChild(entry);
logDiv.scrollTop = logDiv.scrollHeight;
}
// 鼠标事件
const mouseBox = document.getElementById('mouseBox');
mouseBox.addEventListener('click', (e) => {
log(`点击: (${e.offsetX}, ${e.offsetY})`, 'mouse');
});
mouseBox.addEventListener('dblclick', () => {
log('双击', 'mouse');
});
mouseBox.addEventListener('mouseenter', () => {
log('鼠标进入', 'mouse');
});
mouseBox.addEventListener('mouseleave', () => {
log('鼠标离开', 'mouse');
});
// 键盘事件
document.addEventListener('keydown', (e) => {
const keyElement = document.querySelector(`.key[data-key="${e.key}"]`);
if (keyElement) {
keyElement.classList.add('active');
}
log(`按下: ${e.key} (Ctrl: ${e.ctrlKey}, Shift: ${e.shiftKey})`, 'keyboard');
});
document.addEventListener('keyup', (e) => {
const keyElement = document.querySelector(`.key[data-key="${e.key}"]`);
if (keyElement) {
keyElement.classList.remove('active');
}
});
// 表单事件
const textInput = document.getElementById('textInput');
const checkboxInput = document.getElementById('checkboxInput');
textInput.addEventListener('focus', () => {
log('输入框获得焦点', 'form');
});
textInput.addEventListener('blur', () => {
log('输入框失去焦点', 'form');
});
textInput.addEventListener('input', (e) => {
log(`输入: "${e.target.value}"`, 'form');
});
checkboxInput.addEventListener('change', (e) => {
log(`复选框: ${e.target.checked ? '选中' : '取消'}`, 'form');
});
// 滚动事件(节流)
let scrollTicking = false;
window.addEventListener('scroll', () => {
if (!scrollTicking) {
requestAnimationFrame(() => {
log(`滚动: ${Math.round(window.scrollY)}px`, 'mouse');
scrollTicking = false;
});
scrollTicking = true;
}
});
</script>
</body>
</html>