事件处理是Web开发的核心部分,它允许网页对用户的交互做出响应。jQuery简化了事件处理,提供了跨浏览器兼容的解决方案。
click - 点击事件dblclick - 双击事件mouseenter - 鼠标进入mouseleave - 鼠标离开mousemove - 鼠标移动keydown - 键按下keyup - 键释放keypress - 键按下(字符键)focus - 获得焦点blur - 失去焦点change - 值改变submit - 表单提交load - 加载完成resize - 调整大小scroll - 滚动事件jQuery提供了多种事件绑定方法,每种方法都有其适用场景。
jQuery 1.7+ 推荐的事件绑定方法,功能最强大,支持所有事件类型。
// 绑定点击事件
$("#button").on("click", function() {
alert("按钮被点击了!");
});
// 绑定多个事件
$("#element").on("mouseenter mouseleave", function(event) {
if (event.type === "mouseenter") {
$(this).css("background-color", "yellow");
} else {
$(this).css("background-color", "white");
}
});
// 事件委托(处理动态添加的元素)
$("#list").on("click", "li", function() {
alert("列表项被点击: " + $(this).text());
});
// 传递数据给事件处理函数
$("#button").on("click", {name: "张三", age: 25}, function(event) {
console.log("数据:", event.data); // {name: "张三", age: 25}
});
// 使用命名空间
$("#element").on("click.myNamespace", function() {
console.log("带命名空间的点击事件");
});
// 一次性事件
$("#button").one("click", function() {
alert("这个按钮只能点击一次!");
});
移除通过 on() 方法绑定的事件处理程序。
// 定义事件处理函数
function handleClick() {
console.log("按钮被点击");
}
// 绑定事件
$("#button").on("click", handleClick);
// 移除事件
$("#button").off("click", handleClick);
// 绑定多个事件
$("#element").on("click mouseenter", function() {
console.log("事件触发");
});
// 移除所有click事件
$("#element").off("click");
// 移除所有事件
$("#element").off();
// 使用命名空间
$("#element").on("click.myNamespace", function() {
console.log("命名空间事件");
});
// 移除特定命名空间的事件
$("#element").off(".myNamespace");
// 事件委托的移除
$("#list").on("click", "li", function() {
console.log("列表项点击");
});
// 移除事件委托
$("#list").off("click", "li");
// 移除特定处理函数的事件委托
function handleListItemClick() {
console.log("列表项点击");
}
$("#list").on("click", "li", handleListItemClick);
$("#list").off("click", "li", handleListItemClick);
触发绑定到元素的事件处理程序。
// 绑定点击事件
$("#button").on("click", function(event, param1, param2) {
console.log("按钮被点击", param1, param2);
});
// 触发点击事件
$("#button").trigger("click");
// 触发事件并传递参数
$("#button").trigger("click", ["参数1", "参数2"]);
// 触发自定义事件
$("#element").on("myCustomEvent", function() {
console.log("自定义事件被触发");
});
$("#element").trigger("myCustomEvent");
// 触发多个事件
$("#element").on("event1 event2", function(event) {
console.log(event.type + " 被触发");
});
$("#element").trigger("event1 event2");
// 触发表单提交事件
$("#form").trigger("submit");
// 触发带命名空间的事件
$("#element").on("click.myNamespace", function() {
console.log("命名空间事件");
});
$("#element").trigger("click.myNamespace");
jQuery为常用事件提供了快捷方法,语法更简洁。
// 快捷方法绑定事件
$("#button").click(function() {
alert("按钮被点击");
});
$("#input").focus(function() {
$(this).css("border-color", "blue");
});
$("#input").blur(function() {
$(this).css("border-color", "#ccc");
});
$("#element").mouseenter(function() {
$(this).css("background-color", "yellow");
}).mouseleave(function() {
$(this).css("background-color", "white");
});
// 快捷方法触发事件
$("#button").click(); // 触发点击事件
$("#input").focus(); // 触发聚焦事件
// 快捷方法移除事件
$("#button").off("click"); // 只能这样移除,没有.unclick()方法
// 注意:快捷方法内部也是调用on()方法
// 以下两行代码是等价的:
$("#button").click(function() {});
$("#button").on("click", function() {});
| 方法 | 描述 | 版本 | 推荐度 |
|---|---|---|---|
on() |
统一的事件绑定方法,功能最全 | 1.7+ | ★★★★★ |
off() |
移除通过on()绑定的事件 | 1.7+ | ★★★★★ |
bind() |
旧版事件绑定方法(已过时) | 1.0-3.0 | ★☆☆☆☆ |
unbind() |
旧版事件移除方法(已过时) | 1.0-3.0 | ★☆☆☆☆ |
delegate() |
旧版事件委托方法(已过时) | 1.4.2-3.0 | ★☆☆☆☆ |
undelegate() |
旧版事件委托移除(已过时) | 1.4.2-3.0 | ★☆☆☆☆ |
| 快捷方法 | click(), mouseenter()等 | 1.0+ | ★★★★☆ |
鼠标事件在用户使用鼠标与元素交互时触发。
点击事件(按下并释放鼠标按钮)
$("#element").click(function() {
console.log("元素被点击");
});
双击事件
$("#element").dblclick(function() {
console.log("元素被双击");
});
鼠标按钮按下
$("#element").mousedown(function(event) {
console.log("按钮按下:", event.which);
});
鼠标按钮释放
$("#element").mouseup(function() {
console.log("按钮释放");
});
鼠标进入元素区域
$("#element").mouseenter(function() {
$(this).addClass("hover");
});
鼠标离开元素区域
$("#element").mouseleave(function() {
$(this).removeClass("hover");
});
鼠标移动到元素上(会冒泡)
$("#element").mouseover(function() {
console.log("mouseover");
});
鼠标移出元素(会冒泡)
$("#element").mouseout(function() {
console.log("mouseout");
});
鼠标在元素上移动
$("#element").mousemove(function(event) {
console.log("位置:", event.pageX, event.pageY);
});
右键菜单事件
$("#element").contextmenu(function(event) {
event.preventDefault(); // 阻止默认右键菜单
showCustomMenu();
});
展示鼠标事件的常见用法和事件对象属性。
// 鼠标事件综合示例
$("#demo-element")
// 点击事件
.on("click", function(event) {
console.log("点击事件");
console.log("鼠标按钮:", event.which); // 1=左键, 2=中键, 3=右键
console.log("坐标:", event.pageX, event.pageY);
console.log("目标元素:", event.target.tagName);
})
// 双击事件
.on("dblclick", function() {
console.log("双击事件");
$(this).toggleClass("highlight");
})
// 鼠标进入/离开
.on("mouseenter mouseleave", function(event) {
if (event.type === "mouseenter") {
$(this).css("border-color", "#3498db");
} else {
$(this).css("border-color", "#ddd");
}
})
// 鼠标移动
.on("mousemove", function(event) {
$("#coords").text(event.pageX + ", " + event.pageY);
})
// 鼠标按下/释放
.on("mousedown mouseup", function(event) {
if (event.type === "mousedown") {
$(this).css("background-color", "#e74c3c");
} else {
$(this).css("background-color", "#3498db");
}
console.log("按钮:", event.which);
})
// 右键菜单
.on("contextmenu", function(event) {
event.preventDefault();
alert("自定义右键菜单");
return false;
});
// 获取鼠标位置
$(document).on("mousemove", function(event) {
// event.pageX/Y: 相对于文档的坐标
// event.clientX/Y: 相对于视口的坐标
// event.screenX/Y: 相对于屏幕的坐标
console.log("文档坐标:", event.pageX, event.pageY);
console.log("视口坐标:", event.clientX, event.clientY);
console.log("屏幕坐标:", event.screenX, event.screenY);
});
// 鼠标事件对象属性示例
$("#element").on("click", function(event) {
console.log("事件类型:", event.type); // "click"
console.log("时间戳:", event.timeStamp); // 事件发生的时间戳
console.log("目标元素:", event.target); // 触发事件的元素
console.log("当前元素:", event.currentTarget); // 绑定事件处理程序的元素
console.log("是否按下了Shift键:", event.shiftKey);
console.log("是否按下了Ctrl键:", event.ctrlKey);
console.log("是否按下了Alt键:", event.altKey);
console.log("是否按下了Meta键:", event.metaKey);
console.log("鼠标按钮:", event.button); // 0=左键, 1=中键, 2=右键
console.log("相关元素:", event.relatedTarget); // 对于mouseenter/mouseleave事件
});
| 特性 | mouseenter/mouseleave | mouseover/mouseout |
|---|---|---|
| 事件冒泡 | 不会冒泡 | 会冒泡 |
| 触发频率 | 进入/离开元素时触发一次 | 进入/离开元素及其子元素时都会触发 |
| 推荐场景 | 鼠标悬停效果 | 需要事件冒泡的场景 |
| jQuery版本 | 1.0+ | 1.0+ |
| 示例 |
|
|
键盘事件在用户按下或释放键盘按键时触发。
键按下时触发(任何键)
$("#input").keydown(function(event) {
console.log("键按下:", event.key);
});
键释放时触发
$("#input").keyup(function(event) {
console.log("键释放:", event.key);
});
字符键按下时触发
$("#input").keypress(function(event) {
console.log("字符键:", event.which);
});
展示键盘事件的常见用法和事件对象属性。
// 键盘事件综合示例
$("#text-input").on({
// keydown - 键按下
keydown: function(event) {
console.log("keydown - 键码:", event.keyCode);
console.log("键:", event.key);
console.log("是否Ctrl键:", event.ctrlKey);
// 检测特定按键
if (event.keyCode === 13) { // Enter键
console.log("Enter键被按下");
event.preventDefault(); // 阻止默认行为
}
if (event.keyCode === 27) { // ESC键
console.log("ESC键被按下");
$(this).blur();
}
// 组合键检测
if (event.ctrlKey && event.keyCode === 83) { // Ctrl+S
event.preventDefault();
saveContent();
}
},
// keyup - 键释放
keyup: function(event) {
console.log("keyup - 键:", event.key);
// 实时显示输入长度
var length = $(this).val().length;
$("#char-count").text(length);
},
// keypress - 字符键按下
keypress: function(event) {
console.log("keypress - 字符码:", event.which);
// 限制只能输入数字
if (!/[0-9]/.test(String.fromCharCode(event.which))) {
event.preventDefault();
}
}
});
// 全局键盘事件
$(document).on("keydown", function(event) {
// 快捷键支持
switch(event.keyCode) {
case 37: // 左箭头
navigateLeft();
break;
case 39: // 右箭头
navigateRight();
break;
case 38: // 上箭头
navigateUp();
break;
case 40: // 下箭头
navigateDown();
break;
}
});
// 键盘事件对象属性
$("#input").on("keydown", function(event) {
console.log("事件类型:", event.type); // "keydown"
console.log("键码:", event.keyCode); // 按键的键码
console.log("字符码:", event.which); // 字符码(已弃用,但jQuery标准化)
console.log("键:", event.key); // 按键的字符串表示(推荐)
console.log("代码:", event.code); // 物理按键代码
console.log("是否重复:", event.repeat); // 是否按住不放
console.log("是否Shift键:", event.shiftKey);
console.log("是否Ctrl键:", event.ctrlKey);
console.log("是否Alt键:", event.altKey);
console.log("是否Meta键:", event.metaKey);
console.log("是否大写锁定:", event.capsLock);
console.log("是否数字锁定:", event.numLock);
// 阻止默认行为
if (event.key === "Escape") {
event.preventDefault();
$(this).val("");
}
});
// 常用键码
var keyCodes = {
ENTER: 13,
ESC: 27,
SPACE: 32,
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
TAB: 9,
BACKSPACE: 8,
DELETE: 46,
HOME: 36,
END: 35,
PAGE_UP: 33,
PAGE_DOWN: 34,
F1: 112,
F12: 123
};
// 检测组合键
$(document).on("keydown", function(event) {
// Ctrl + C
if (event.ctrlKey && event.key === "c") {
console.log("复制");
}
// Ctrl + V
if (event.ctrlKey && event.key === "v") {
console.log("粘贴");
}
// Ctrl + Z
if (event.ctrlKey && event.key === "z") {
console.log("撤销");
event.preventDefault();
}
// Alt + 键
if (event.altKey && event.key === "s") {
console.log("Alt+S 保存");
event.preventDefault();
}
});
| 事件 | 触发时机 | 支持的键 | 推荐用途 |
|---|---|---|---|
keydown |
键按下时 | 所有键 | 检测功能键、快捷键 |
keyup |
键释放时 | 所有键 | 完成输入、检测键释放 |
keypress |
字符键按下时 | 字符键(a-z, 0-9等) | 字符输入处理(已弃用) |
表单事件在表单元素与用户交互时触发。
元素获得焦点时触发
$("input").focus(function() {
$(this).addClass("focused");
});
元素失去焦点时触发
$("input").blur(function() {
validateField(this);
});
元素值改变并失去焦点时触发
$("select").change(function() {
updateSelection($(this).val());
});
元素值改变时立即触发
$("textarea").on("input", function() {
updateCharCount($(this).val());
});
表单提交时触发
$("form").submit(function(event) {
if (!validateForm()) {
event.preventDefault();
}
});
文本被选择时触发
$("input[type='text']").select(function() {
console.log("文本被选择");
});
展示表单事件的常见用法和表单验证。
// 表单事件综合示例
$("#myForm").on({
// 提交事件
submit: function(event) {
event.preventDefault(); // 阻止默认提交
if (validateForm()) {
// 验证通过,手动提交
console.log("表单提交:", $(this).serialize());
// Ajax提交
$.ajax({
url: $(this).attr("action"),
method: $(this).attr("method"),
data: $(this).serialize(),
success: function(response) {
console.log("提交成功:", response);
}
});
} else {
console.log("表单验证失败");
}
},
// 重置事件
reset: function() {
console.log("表单被重置");
$(".error-message").hide();
}
});
// 输入框事件
$("#username")
// 获得焦点
.focus(function() {
$(this).css("border-color", "#3498db");
$("#username-help").show();
})
// 失去焦点
.blur(function() {
$(this).css("border-color", "#ddd");
$("#username-help").hide();
// 验证用户名
validateUsername($(this).val());
})
// 输入时实时验证
.on("input", function() {
var value = $(this).val();
$("#username-length").text(value.length);
if (value.length > 0 && value.length < 3) {
showError($(this), "用户名至少3个字符");
} else {
clearError($(this));
}
})
// 值改变(失去焦点后)
.change(function() {
console.log("用户名改变为:", $(this).val());
});
// 下拉框事件
$("#country")
.change(function() {
var country = $(this).val();
console.log("选择的国家:", country);
// 动态加载城市
loadCities(country);
});
// 复选框事件
$("input[type='checkbox']")
.change(function() {
var isChecked = $(this).prop("checked");
console.log("复选框状态:", isChecked);
// 更新选中计数
var checkedCount = $("input[type='checkbox']:checked").length;
$("#selected-count").text(checkedCount);
});
// 单选框事件
$("input[type='radio']")
.change(function() {
var value = $(this).val();
console.log("选择的值:", value);
});
// 表单验证函数
function validateForm() {
var isValid = true;
// 验证用户名
if ($("#username").val().trim() === "") {
showError($("#username"), "用户名不能为空");
isValid = false;
}
// 验证邮箱
var email = $("#email").val();
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showError($("#email"), "邮箱格式不正确");
isValid = false;
}
// 验证密码
var password = $("#password").val();
if (password.length < 6) {
showError($("#password"), "密码至少6个字符");
isValid = false;
}
return isValid;
}
function showError($element, message) {
$element.css("border-color", "#e74c3c");
$element.next(".error-message").remove();
$element.after('');
}
function clearError($element) {
$element.css("border-color", "#ddd");
$element.next(".error-message").remove();
}
// 获取表单数据
$("#submit-btn").click(function() {
// 序列化表单数据
var formData = $("#myForm").serialize();
console.log("表单数据:", formData);
// 序列化为数组
var formArray = $("#myForm").serializeArray();
console.log("表单数组:", formArray);
// 获取特定字段值
var username = $("#username").val();
var email = $("#email").val();
var newsletter = $("#newsletter").prop("checked");
// 获取复选框值(多选)
var hobbies = [];
$("input[name='hobbies']:checked").each(function() {
hobbies.push($(this).val());
});
});
窗口事件在浏览器窗口状态改变时触发。
页面完全加载后触发
$(window).load(function() {
console.log("页面完全加载");
});
窗口大小改变时触发
$(window).resize(function() {
console.log("窗口大小:", $(this).width());
});
窗口滚动时触发
$(window).scroll(function() {
console.log("滚动位置:", $(this).scrollTop());
});
页面卸载时触发(已弃用)
$(window).on("unload", function() {
// 清理工作
});
页面卸载前触发
$(window).on("beforeunload", function() {
return "确定离开页面吗?";
});
展示窗口事件的常见用法和响应式设计。
// 窗口事件综合示例
$(window)
// 页面加载完成
.on("load", function() {
console.log("页面加载完成");
// 初始化操作
initPage();
// 隐藏加载动画
$("#loading").fadeOut();
})
// 窗口大小改变
.on("resize", function() {
var width = $(this).width();
var height = $(this).height();
console.log("窗口尺寸:", width + "x" + height);
// 响应式设计
if (width < 768) {
$("body").addClass("mobile").removeClass("desktop");
} else {
$("body").addClass("desktop").removeClass("mobile");
}
// 调整布局
adjustLayout(width);
})
// 窗口滚动
.on("scroll", function() {
var scrollTop = $(this).scrollTop();
var windowHeight = $(this).height();
var documentHeight = $(document).height();
console.log("滚动位置:", scrollTop);
// 滚动到顶部按钮
if (scrollTop > 300) {
$("#back-to-top").fadeIn();
} else {
$("#back-to-top").fadeOut();
}
// 无限滚动
if (scrollTop + windowHeight >= documentHeight - 100) {
loadMoreContent();
}
// 视差滚动效果
$(".parallax").css("transform", "translateY(" + scrollTop * 0.5 + "px)");
// 固定导航栏
if (scrollTop > $("#header").height()) {
$("#navbar").addClass("fixed");
} else {
$("#navbar").removeClass("fixed");
}
});
// 页面加载状态
$(document).ready(function() {
console.log("DOM准备就绪");
// DOM操作可以在这里进行
$("#content").html("页面已加载");
});
// 注意:$(document).ready() vs $(window).load()
// ready: DOM加载完成,图片等资源可能还没加载
// load: 所有资源(图片、样式等)都加载完成
// 页面卸载前
$(window).on("beforeunload", function(event) {
// 如果有未保存的数据
if (hasUnsavedChanges()) {
var message = "您有未保存的更改,确定要离开吗?";
event.returnValue = message; // 标准方法
return message; // 兼容旧浏览器
}
});
// 页面卸载
$(window).on("unload", function() {
// 清理工作(注意:限制很多)
// 可以发送一个同步的Ajax请求
// 但更推荐使用visibilitychange事件
});
// 页面可见性API
$(document).on("visibilitychange", function() {
if (document.hidden) {
console.log("页面被隐藏");
// 暂停视频、动画等
} else {
console.log("页面可见");
// 恢复视频、动画等
}
});
// 获取窗口信息
function getWindowInfo() {
return {
width: $(window).width(), // 视口宽度
height: $(window).height(), // 视口高度
scrollTop: $(window).scrollTop(), // 垂直滚动位置
scrollLeft: $(window).scrollLeft(), // 水平滚动位置
screenWidth: screen.width, // 屏幕宽度
screenHeight: screen.height, // 屏幕高度
devicePixelRatio: window.devicePixelRatio // 设备像素比
};
}
// 响应式断点检测
function checkBreakpoint() {
var width = $(window).width();
if (width >= 1200) {
return "xl"; // 特大屏幕
} else if (width >= 992) {
return "lg"; // 大屏幕
} else if (width >= 768) {
return "md"; // 中等屏幕
} else if (width >= 576) {
return "sm"; // 小屏幕
} else {
return "xs"; // 超小屏幕
}
}
// 监听断点变化
var currentBreakpoint = checkBreakpoint();
$(window).on("resize", function() {
var newBreakpoint = checkBreakpoint();
if (newBreakpoint !== currentBreakpoint) {
console.log("断点变化:", currentBreakpoint, "->", newBreakpoint);
currentBreakpoint = newBreakpoint;
// 执行断点相关的操作
onBreakpointChange(newBreakpoint);
}
});
滚动此区域查看滚动事件
继续滚动...
触摸事件在触摸屏设备上与元素交互时触发。
触摸开始时触发
$("#element").on("touchstart", function(event) {
console.log("触摸开始");
});
触摸移动时触发
$("#element").on("touchmove", function(event) {
event.preventDefault();
});
触摸结束时触发
$("#element").on("touchend", function(event) {
console.log("触摸结束");
});
触摸被取消时触发
$("#element").on("touchcancel", function(event) {
console.log("触摸取消");
});
展示触摸事件的常见用法和移动端手势识别。
// 触摸事件综合示例
var touchStartX = 0;
var touchStartY = 0;
var touchEndX = 0;
var touchEndY = 0;
$("#touch-element")
// 触摸开始
.on("touchstart", function(event) {
event.preventDefault();
var touch = event.originalEvent.touches[0];
touchStartX = touch.pageX;
touchStartY = touch.pageY;
console.log("触摸开始:", touchStartX, touchStartY);
$(this).addClass("touching");
// 记录触摸时间
$(this).data("touchStartTime", Date.now());
})
// 触摸移动
.on("touchmove", function(event) {
event.preventDefault();
var touch = event.originalEvent.touches[0];
var currentX = touch.pageX;
var currentY = touch.pageY;
console.log("触摸移动:", currentX, currentY);
// 计算移动距离
var deltaX = currentX - touchStartX;
var deltaY = currentY - touchStartY;
// 拖拽效果
$(this).css({
transform: "translate(" + deltaX + "px, " + deltaY + "px)"
});
})
// 触摸结束
.on("touchend", function(event) {
event.preventDefault();
var touch = event.originalEvent.changedTouches[0];
touchEndX = touch.pageX;
touchEndY = touch.pageY;
console.log("触摸结束:", touchEndX, touchEndY);
$(this).removeClass("touching");
// 检测手势
detectGesture();
// 恢复位置
$(this).css({
transform: "translate(0, 0)",
transition: "transform 0.3s"
});
// 移除过渡效果
setTimeout(function() {
$("#touch-element").css("transition", "none");
}, 300);
})
// 触摸取消
.on("touchcancel", function(event) {
console.log("触摸取消");
$(this).removeClass("touching");
});
// 手势识别函数
function detectGesture() {
var deltaX = touchEndX - touchStartX;
var deltaY = touchEndY - touchStartY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var duration = Date.now() - $("#touch-element").data("touchStartTime");
console.log("手势参数:", {
deltaX: deltaX,
deltaY: deltaY,
distance: distance,
duration: duration
});
// 滑动检测
if (Math.abs(deltaX) > 50 && Math.abs(deltaY) < 30) {
if (deltaX > 0) {
console.log("向右滑动");
swipeRight();
} else {
console.log("向左滑动");
swipeLeft();
}
}
// 垂直滑动
if (Math.abs(deltaY) > 50 && Math.abs(deltaX) < 30) {
if (deltaY > 0) {
console.log("向下滑动");
swipeDown();
} else {
console.log("向上滑动");
swipeUp();
}
}
// 轻触检测(快速触摸)
if (distance < 10 && duration < 300) {
console.log("轻触");
handleTap();
}
// 长按检测
if (duration > 1000) {
console.log("长按");
handleLongPress();
}
}
// 多点触控
$("#multi-touch").on("touchstart", function(event) {
var touches = event.originalEvent.touches;
console.log("触摸点数:", touches.length);
if (touches.length === 2) {
// 双指手势
console.log("双指触摸");
var touch1 = touches[0];
var touch2 = touches[1];
// 计算初始距离
var initialDistance = getDistance(
touch1.pageX, touch1.pageY,
touch2.pageX, touch2.pageY
);
$(this).data("initialDistance", initialDistance);
}
});
$("#multi-touch").on("touchmove", function(event) {
var touches = event.originalEvent.touches;
if (touches.length === 2) {
event.preventDefault();
var touch1 = touches[0];
var touch2 = touches[1];
// 计算当前距离
var currentDistance = getDistance(
touch1.pageX, touch1.pageY,
touch2.pageX, touch2.pageY
);
var initialDistance = $(this).data("initialDistance");
var scale = currentDistance / initialDistance;
console.log("缩放比例:", scale);
// 应用缩放
$(this).css("transform", "scale(" + scale + ")");
}
});
function getDistance(x1, y1, x2, y2) {
var dx = x2 - x1;
var dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
// 移动端点击延迟处理
// 使用fastclick库或以下方法
$("#mobile-button").on("touchstart click", function(event) {
// 阻止触摸事件后的click事件
if (event.type === "touchstart") {
$(this).data("touchStarted", true);
} else if (event.type === "click" && $(this).data("touchStarted")) {
event.preventDefault();
event.stopPropagation();
$(this).removeData("touchStarted");
return false;
}
// 处理点击逻辑
handleButtonClick();
});
// 检测设备是否支持触摸
function isTouchDevice() {
return 'ontouchstart' in window ||
navigator.maxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0;
}
if (isTouchDevice()) {
$("body").addClass("touch-device");
console.log("触摸设备");
} else {
$("body").addClass("no-touch");
console.log("非触摸设备");
}
在此区域触摸/点击
(在移动设备或模拟触摸的设备上效果更好)event.preventDefault()防止触摸时的默认行为(如滚动)事件对象包含事件相关的所有信息,在事件处理函数中作为参数传递。
jQuery事件对象是对原生事件对象的封装,提供跨浏览器兼容的属性。
| 属性 | 描述 | 示例 |
|---|---|---|
type |
事件类型(如 "click") | event.type |
target |
触发事件的元素 | event.target |
currentTarget |
当前处理事件的元素 | event.currentTarget |
relatedTarget |
相关元素(如mouseenter的离开元素) | event.relatedTarget |
pageX/pageY |
鼠标相对于文档的坐标 | event.pageX |
clientX/clientY |
鼠标相对于视口的坐标 | event.clientX |
screenX/screenY |
鼠标相对于屏幕的坐标 | event.screenX |
which |
鼠标按钮或键盘键码 | event.which |
key/keyCode |
键盘按键信息 | event.key |
shiftKey |
是否按下了Shift键 | event.shiftKey |
ctrlKey |
是否按下了Ctrl键 | event.ctrlKey |
altKey |
是否按下了Alt键 | event.altKey |
metaKey |
是否按下了Meta键 | event.metaKey |
timeStamp |
事件发生的时间戳 | event.timeStamp |
data |
通过on()方法传递的数据 | event.data |
result |
上一个事件处理函数的返回值 | event.result |
originalEvent |
原始的事件对象 | event.originalEvent |
| 方法 | 描述 | 示例 |
|---|---|---|
preventDefault() |
阻止事件的默认行为 | event.preventDefault() |
stopPropagation() |
阻止事件冒泡 | event.stopPropagation() |
stopImmediatePropagation() |
阻止事件冒泡并阻止同类型其他处理函数执行 | event.stopImmediatePropagation() |
isDefaultPrevented() |
检查是否调用了preventDefault() | event.isDefaultPrevented() |
isPropagationStopped() |
检查是否调用了stopPropagation() | event.isPropagationStopped() |
// 事件对象使用示例
$("#element").on("click", function(event) {
// 获取事件信息
console.log("事件类型:", event.type);
console.log("目标元素:", event.target.tagName);
console.log("当前元素:", event.currentTarget.tagName);
console.log("鼠标坐标:", event.pageX, event.pageY);
console.log("时间戳:", event.timeStamp);
// 检查按键状态
if (event.shiftKey) {
console.log("按下了Shift键");
}
if (event.ctrlKey) {
console.log("按下了Ctrl键");
}
// 阻止默认行为
event.preventDefault();
// 阻止事件冒泡
event.stopPropagation();
// 检查方法是否已被调用
console.log("默认行为已阻止?", event.isDefaultPrevented());
console.log("事件冒泡已阻止?", event.isPropagationStopped());
// 访问原始事件对象
console.log("原始事件:", event.originalEvent);
// 返回结果(可供后续事件处理函数访问)
return "处理完成";
});
// 链式事件处理
$("#element")
.on("click", function(event) {
console.log("第一个处理函数");
// 这个返回值可以在event.result中访问
return "第一个返回值";
})
.on("click", function(event) {
console.log("第二个处理函数");
console.log("上一个函数的返回值:", event.result); // "第一个返回值"
return "第二个返回值";
});
// 使用事件数据
$("#button").on("click", {user: "张三", action: "提交"}, function(event) {
console.log("用户:", event.data.user); // "张三"
console.log("操作:", event.data.action); // "提交"
});
// 鼠标事件对象
$("#element").on("mousedown", function(event) {
console.log("鼠标按钮:", event.which); // 1=左键, 2=中键, 3=右键
console.log("按钮:", event.button); // 同上
});
// 键盘事件对象
$("#input").on("keydown", function(event) {
console.log("键码:", event.keyCode);
console.log("键:", event.key);
console.log("代码:", event.code);
console.log("是否重复:", event.repeat);
});
事件委托是将事件处理程序附加到父元素,利用事件冒泡处理子元素的事件。
事件委托可以提高性能,特别是对于动态添加的元素。
// 传统方式 - 直接绑定(不推荐用于动态元素)
$("li").click(function() {
console.log("列表项点击");
});
// 问题:动态添加的li元素不会有点击事件
$("#list").append("新项目 "); // 这个新li没有点击事件
// 解决方案1 - 每次添加元素后重新绑定(性能差)
function addListItem(text) {
var $li = $("").text(text);
$("#list").append($li);
$li.click(function() { // 为每个新元素绑定事件
console.log("点击:", text);
});
}
// 解决方案2 - 事件委托(推荐)
$("#list").on("click", "li", function() {
console.log("点击:", $(this).text());
});
// 现在动态添加的li元素也会触发事件
$("#list").append(" 新项目 "); // 这个新li也有点击事件
// 1. 处理动态添加的元素
$("#dynamic-list").on("click", "li.item", function() {
// 即使li.item是动态添加的,也会触发事件
console.log("动态项目被点击");
});
// 动态添加元素
setInterval(function() {
$("#dynamic-list").append('新项目' + Date.now() + ' ');
}, 1000);
// 2. 减少事件处理程序数量
// 传统方式:为1000个元素绑定1000个事件处理程序
$("td").click(function() { // 性能差
console.log("单元格点击");
});
// 事件委托:只绑定1个事件处理程序
$("#table").on("click", "td", function() { // 性能好
console.log("单元格点击");
});
// 3. 简化事件管理
$("#container").on({
click: function(event) {
var $target = $(event.target);
// 根据目标元素执行不同操作
if ($target.is("button.delete")) {
deleteItem($target.closest(".item"));
} else if ($target.is("button.edit")) {
editItem($target.closest(".item"));
} else if ($target.is(".item")) {
selectItem($target);
}
},
mouseenter: function(event) {
if ($(event.target).is(".item")) {
$(event.target).addClass("hover");
}
},
mouseleave: function(event) {
if ($(event.target).is(".item")) {
$(event.target).removeClass("hover");
}
}
}, ".item, button"); // 只代理.item和button元素的事件
// 4. 内存管理更简单
// 移除元素时不需要特别处理事件
$(".item").remove(); // 事件处理程序仍然在#container上
// 如果要移除所有事件,只需:
$("#container").off();
// 表格行操作
$("#data-table").on("click", "td", function(event) {
event.stopPropagation(); // 阻止事件冒泡到tr
var $td = $(this);
var columnIndex = $td.index();
var rowIndex = $td.closest("tr").index();
console.log("点击单元格:", rowIndex, columnIndex);
// 根据不同列执行不同操作
if (columnIndex === 0) {
toggleCheckbox($td);
} else if (columnIndex === 1) {
editCell($td);
} else if (columnIndex === 2) {
deleteRow($td.closest("tr"));
}
});
// 列表操作
$("#todo-list").on({
click: function(event) {
var $target = $(event.target);
if ($target.is(".delete-btn")) {
// 删除项目
$target.closest(".todo-item").remove();
} else if ($target.is(".edit-btn")) {
// 编辑项目
editTodoItem($target.closest(".todo-item"));
} else if ($target.is(".todo-item")) {
// 切换完成状态
$target.toggleClass("completed");
}
},
dblclick: function(event) {
if ($(event.target).is(".todo-text")) {
// 双击文本编辑
editTodoText($(event.target));
}
}
}, ".todo-item, .delete-btn, .edit-btn, .todo-text");
// 表单动态字段
$("#dynamic-form").on({
focus: function() {
$(this).addClass("focused");
},
blur: function() {
$(this).removeClass("focused");
validateField($(this));
},
change: function() {
updateFormState();
}
}, "input, select, textarea"); // 代理所有表单元素
// 动态添加表单字段
$("#add-field").click(function() {
var fieldId = "field-" + Date.now();
$("#dynamic-form").append(
'' +
' ' +
' ' +
''
);
});
// 删除动态字段(也需要事件委托)
$("#dynamic-form").on("click", ".remove-field", function() {
$(this).closest(".form-group").remove();
updateFormState();
});
// 性能对比示例
console.time("直接绑定");
for (var i = 0; i < 1000; i++) {
$("项目" + i + "")
.appendTo("#container")
.click(function() { // 为每个元素绑定事件
console.log("点击");
});
}
console.timeEnd("直接绑定"); // 较慢
console.time("事件委托");
$("#container").empty();
for (var i = 0; i < 1000; i++) {
$("项目" + i + "").appendTo("#container");
}
$("#container").on("click", ".item", function() { // 只绑定一次
console.log("点击");
});
console.timeEnd("事件委托"); // 较快
event.stopPropagation()要谨慎,可能影响其他事件处理事件流描述事件在DOM树中传播的顺序。
DOM事件流包括三个阶段:捕获阶段、目标阶段、冒泡阶段。
<!-- HTML结构 -->
<div id="grandparent">
<div id="parent">
<button id="child">点击我</button>
</div>
</div>
// 事件流演示
// 默认情况下,事件处理在冒泡阶段执行
$("#child").click(function(event) {
console.log("子元素 - 目标阶段");
});
$("#parent").click(function(event) {
console.log("父元素 - 冒泡阶段");
});
$("#grandparent").click(function(event) {
console.log("祖父元素 - 冒泡阶段");
});
$(document).click(function(event) {
console.log("文档 - 冒泡阶段");
});
// 点击按钮时输出顺序:
// 1. 子元素 - 目标阶段
// 2. 父元素 - 冒泡阶段
// 3. 祖父元素 - 冒泡阶段
// 4. 文档 - 冒泡阶段
// 使用捕获阶段
// jQuery默认不支持捕获阶段,需要使用原生addEventListener
document.getElementById("grandparent").addEventListener("click", function(event) {
console.log("祖父元素 - 捕获阶段");
}, true); // true表示在捕获阶段处理
document.getElementById("parent").addEventListener("click", function(event) {
console.log("父元素 - 捕获阶段");
}, true);
// 现在点击按钮时输出顺序:
// 1. 祖父元素 - 捕获阶段
// 2. 父元素 - 捕获阶段
// 3. 子元素 - 目标阶段(jQuery)
// 4. 父元素 - 冒泡阶段(jQuery)
// 5. 祖父元素 - 冒泡阶段(jQuery)
// 6. 文档 - 冒泡阶段(jQuery)
// 阻止事件冒泡
$("#child").click(function(event) {
console.log("子元素点击");
event.stopPropagation(); // 阻止事件冒泡
// 现在只有这个处理函数会执行
});
$("#parent").click(function(event) {
console.log("这个不会执行,因为事件冒泡被阻止了");
});
// 阻止默认行为
$("a").click(function(event) {
event.preventDefault(); // 阻止链接跳转
console.log("链接被点击,但不会跳转");
});
// 同时阻止默认行为和冒泡
$("a").click(function(event) {
event.preventDefault();
event.stopPropagation();
// 或者使用 return false
// return false; // 等同于上面两行
});
// 事件委托中的事件流
$("#grandparent").on("click", "#child", function(event) {
console.log("事件委托处理");
// 事件仍然会冒泡到#grandparent
});
// 检查事件阶段
document.getElementById("child").addEventListener("click", function(event) {
console.log("事件阶段:", event.eventPhase);
// 1: 捕获阶段
// 2: 目标阶段
// 3: 冒泡阶段
}, true);
// 事件委托和事件流的结合
$("#container").on("click", function(event) {
console.log("容器点击,目标:", event.target.tagName);
// 根据event.target决定如何处理
if ($(event.target).is("button")) {
console.log("点击了按钮");
} else if ($(event.target).is("input")) {
console.log("点击了输入框");
}
});
// 事件流可视化
function logEventFlow(event) {
var phase;
switch(event.eventPhase) {
case Event.CAPTURING_PHASE: phase = "捕获"; break;
case Event.AT_TARGET: phase = "目标"; break;
case Event.BUBBLING_PHASE: phase = "冒泡"; break;
}
console.log(event.currentTarget.id + " - " + phase + "阶段");
}
// 为所有元素添加事件监听
$("#grandparent, #parent, #child").each(function() {
this.addEventListener("click", logEventFlow, true); // 捕获阶段
this.addEventListener("click", logEventFlow, false); // 冒泡阶段
});
自定义事件允许创建和触发自己的事件类型。
自定义事件可以实现组件间的松耦合通信。
// 创建自定义事件
$("#element").on("myCustomEvent", function(event, param1, param2) {
console.log("自定义事件触发", param1, param2);
});
// 触发自定义事件
$("#element").trigger("myCustomEvent", ["参数1", "参数2"]);
// 带命名空间的自定义事件
$("#element").on("myEvent.namespace1", function() {
console.log("命名空间1的事件");
});
$("#element").on("myEvent.namespace2", function() {
console.log("命名空间2的事件");
});
// 触发特定命名空间的事件
$("#element").trigger("myEvent.namespace1");
// 移除特定命名空间的事件
$("#element").off(".namespace1");
// 创建自定义事件对象
var customEvent = $.Event("customEvent", {
pageX: 100,
pageY: 200,
data: {key: "value"}
});
// 触发带自定义数据的事件
$("#element").trigger(customEvent);
// 在事件处理函数中访问自定义数据
$("#element").on("customEvent", function(event) {
console.log("自定义数据:", event.data); // {key: "value"}
console.log("页面坐标:", event.pageX, event.pageY); // 100, 200
});
// 1. 组件间通信
// 购物车组件
var ShoppingCart = {
init: function() {
this.bindEvents();
},
bindEvents: function() {
// 监听商品添加事件
$(document).on("productAdded", function(event, product) {
ShoppingCart.addProduct(product);
});
// 监听商品移除事件
$(document).on("productRemoved", function(event, productId) {
ShoppingCart.removeProduct(productId);
});
},
addProduct: function(product) {
console.log("添加商品:", product);
// 更新购物车UI
this.updateUI();
// 触发购物车更新事件
$(document).trigger("cartUpdated", [this.getCartItems()]);
},
removeProduct: function(productId) {
console.log("移除商品:", productId);
// 更新购物车UI
this.updateUI();
// 触发购物车更新事件
$(document).trigger("cartUpdated", [this.getCartItems()]);
},
updateUI: function() {
// 更新购物车显示
},
getCartItems: function() {
// 返回购物车商品列表
return [];
}
};
// 商品列表组件
$(".add-to-cart").click(function() {
var product = {
id: $(this).data("id"),
name: $(this).data("name"),
price: $(this).data("price")
};
// 触发商品添加事件
$(document).trigger("productAdded", [product]);
});
// 2. 状态变化通知
var ApplicationState = {
currentUser: null,
login: function(user) {
this.currentUser = user;
// 触发登录事件
$(document).trigger("userLoggedIn", [user]);
},
logout: function() {
var oldUser = this.currentUser;
this.currentUser = null;
// 触发登出事件
$(document).trigger("userLoggedOut", [oldUser]);
}
};
// 监听登录状态变化
$(document).on("userLoggedIn", function(event, user) {
console.log("用户登录:", user.name);
updateUIForLoggedInUser(user);
});
$(document).on("userLoggedOut", function(event, user) {
console.log("用户登出:", user.name);
updateUIForLoggedOutUser();
});
// 3. 异步操作完成通知
function loadUserData(userId) {
// 模拟异步加载
setTimeout(function() {
var userData = {
id: userId,
name: "张三",
email: "zhangsan@example.com"
};
// 数据加载完成,触发事件
$(document).trigger("userDataLoaded", [userData]);
}, 1000);
}
// 监听数据加载完成
$(document).on("userDataLoaded", function(event, userData) {
console.log("用户数据加载完成:", userData);
renderUserProfile(userData);
});
// 开始加载数据
loadUserData(123);
// 4. 表单验证事件
$("#registration-form").on("submit", function(event) {
event.preventDefault();
var formData = $(this).serializeArray();
var errors = validateFormData(formData);
if (errors.length === 0) {
// 表单验证通过,触发事件
$(this).trigger("formValid", [formData]);
} else {
// 表单验证失败,触发事件
$(this).trigger("formInvalid", [errors]);
}
});
// 监听表单验证结果
$("#registration-form")
.on("formValid", function(event, formData) {
console.log("表单验证通过,提交数据");
submitForm(formData);
})
.on("formInvalid", function(event, errors) {
console.log("表单验证失败:", errors);
displayErrors(errors);
});
// 5. 自定义事件命名空间管理
// 为不同模块使用不同命名空间
var UserModule = {
namespace: "userModule",
init: function() {
this.bindEvents();
},
bindEvents: function() {
$(document).on("login." + this.namespace, this.onLogin.bind(this));
$(document).on("logout." + this.namespace, this.onLogout.bind(this));
},
onLogin: function(event, user) {
console.log("用户模块: 用户登录", user);
},
onLogout: function(event, user) {
console.log("用户模块: 用户登出", user);
},
destroy: function() {
// 清理事件
$(document).off("." + this.namespace);
}
};
var CartModule = {
namespace: "cartModule",
init: function() {
this.bindEvents();
},
bindEvents: function() {
$(document).on("productAdded." + this.namespace, this.onProductAdded.bind(this));
},
onProductAdded: function(event, product) {
console.log("购物车模块: 商品添加", product);
},
destroy: function() {
// 清理事件
$(document).off("." + this.namespace);
}
};
// 初始化模块
UserModule.init();
CartModule.init();
// 触发事件(所有模块都会收到)
$(document).trigger("login", [{id: 1, name: "张三"}]);
$(document).trigger("productAdded", [{id: 101, name: "商品A"}]);
// 销毁特定模块
UserModule.destroy(); // 只移除用户模块的事件
综合演示各种事件处理技术。
on() 方法绑定事件,off() 方法移除事件preventDefault() 和 stopPropagation()$(parent).on(event, childSelector, handler) 处理动态元素trigger() 触发自定义事件,实现组件间通信