前端开发中的事件处理、Ajax与jQuery使用指南
1. 事件处理
在前端开发里,事件处理是一项关键技能。当我们想要改变默认行为时,可在事件对象上调用
preventDefault()
方法。多数情况下,我们编写的事件处理程序都会调用此方法,除非你明确希望在默认处理程序之外执行其他操作。
因为HTML具有层级结构,所以事件能在多个位置被处理。比如,当你点击一个按钮时,按钮自身、按钮的父元素、父元素的父元素等都有机会处理该事件。这就引出了一个问题:元素响应事件的顺序是怎样的呢?主要有两种选择:
-
捕获(Capturing)
:从最遥远的祖先元素开始处理事件。例如,若按钮是
<div id="content">
的子元素,而
<div id="content">
又是
<body>
的子元素,那么
<body>
就有机会“捕获”针对按钮的事件。
-
冒泡(Bubbling)
:从事件发生的元素开始,然后沿着层级向上,让所有祖先元素都有机会响应事件。
HTML5的事件传播机制同时支持这两种方式,它先允许处理程序捕获事件(从最遥远的祖先元素开始,向下到目标元素),然后事件再从目标元素冒泡回最遥远的祖先元素。
任何处理程序都可以选择执行以下三种操作之一,来影响其他处理程序的调用:
-
preventDefault
:取消事件。取消的事件会继续传播,但它们的
defaultPrevented
属性会被设为
true
。浏览器内置的事件处理程序会尊重这个属性,不执行任何操作,而我们编写的事件处理程序通常可以选择忽略该属性。
-
stopPropagation
:阻止事件进一步传播到当前元素之外(当前元素上附加的所有处理程序仍会被调用,但其他元素上的处理程序不会被调用)。
-
stopImmediatePropagation
:阻止任何其他处理程序被调用(即使这些处理程序在当前元素上)。
以下是一个完整的HTML示例,用于展示事件传播的过程:
<!doctype html>
<html>
<head>
<title>Event Propagation</title>
<meta charset="utf-8">
</head>
<body>
<div>
<button>Click Me!</button>
</div>
<script>
// 创建一个事件处理程序并返回
function logEvent(handlerName, type, cancel, stop, stopImmediate) {
// 实际的事件处理程序
return function(evt) {
if(cancel) evt.preventDefault();
if(stop) evt.stopPropagation();
if(stopImmediate) evt.stopImmediatePropagation();
console.log(`${type}: ${handlerName}` +
(evt.defaultPrevented ? ' (canceled)' : ''));
}
}
// 为元素添加事件记录器
function addEventLogger(elt, type, action) {
const capture = type === 'capture';
elt.addEventListener('click',
logEvent(elt.tagName, type, action==='cancel',
action==='stop', action==='stop!'), capture);
}
const body = document.querySelector('body');
const div = document.querySelector('div');
const button = document.querySelector('button');
addEventLogger(body, 'capture');
addEventLogger(body, 'bubble');
addEventLogger(div, 'capture');
addEventLogger(div, 'bubble');
addEventLogger(button, 'capture');
addEventLogger(button, 'bubble');
</script>
</body>
</html>
当你点击按钮时,控制台会输出如下内容:
capture: BODY
capture: DIV
capture: BUTTON
bubble: BUTTON
bubble: DIV
bubble: BODY
这清晰地展示了捕获传播之后接着是冒泡传播的过程。需要注意的是,在实际触发事件的元素上,处理程序会按照添加的顺序被调用,无论它们是捕获事件还是冒泡事件。
我们还可以通过修改代码来观察不同操作对事件传播的影响:
-
取消传播
:在
<div>
的捕获阶段取消传播。
addEventLogger(body, 'capture');
addEventLogger(body, 'bubble');
addEventLogger(div, 'capture', 'cancel');
addEventLogger(div, 'bubble');
addEventLogger(button, 'capture');
addEventLogger(button, 'bubble');
此时,事件会继续传播,但会被标记为已取消:
capture: BODY
capture: DIV (canceled)
capture: BUTTON (canceled)
bubble: BUTTON (canceled)
bubble: DIV (canceled)
bubble: BODY (canceled)
-
停止传播
:在
<button>的捕获阶段停止传播。
addEventLogger(body, 'capture');
addEventLogger(body, 'bubble');
addEventLogger(div, 'capture', 'cancel');
addEventLogger(div, 'bubble');
addEventLogger(button, 'capture', 'stop');
addEventLogger(button, 'bubble');
事件传播会在
<button>
元素之后停止,但
<button>
的冒泡事件仍然会触发:
capture: BODY
capture: DIV (canceled)
capture: BUTTON (canceled)
bubble: BUTTON (canceled)
-
立即停止传播
:在
<button>的捕获阶段立即停止传播。
addEventLogger(body, 'capture');
addEventLogger(body, 'bubble');
addEventLogger(div, 'capture', 'cancel');
addEventLogger(div, 'bubble');
addEventLogger(button, 'capture', 'stop!');
addEventLogger(button, 'bubble');
此时,事件传播会在
<button>
的捕获阶段完全停止,不会有进一步的传播:
capture: BODY
capture: DIV (canceled)
capture: BUTTON (canceled)
另外,
addEventListener
取代了旧的添加事件的方式(使用“on”属性)。例如,以前添加点击处理程序的方式是
elt.onclick = function(evt) { /* handler */ }
,这种方法的主要缺点是一次只能注册一个处理程序。
在使用jQuery事件监听器时,从处理程序中显式返回
false
等同于调用
stopPropagation
,不过这只是jQuery的约定,在DOM API中此快捷方式并不适用。
2. 事件分类
MDN对所有DOM事件进行了出色的分类整理,以下是一些常用的事件类别:
| 事件类别 | 描述 |
| ---- | ---- |
| 拖放事件(Drag events) | 允许实现拖放界面,包含
dragstart
、
drag
、
dragend
、
drop
等事件。 |
| 焦点事件(Focus events) | 当用户与可编辑元素(如表单字段)交互时触发。
focus
事件在用户“进入”字段时触发(通过点击、按Tab键或触摸),
blur
事件在用户“离开”字段时触发(通过点击其他地方、按Tab键或触摸其他位置),
change
事件在用户对字段进行更改时触发。 |
| 表单事件(Form events) | 当用户提交表单时(通过按下提交按钮或在正确的上下文中按Enter键),表单上会触发
submit
事件。 |
| 输入设备事件(Input device events) | 除了常见的
click
事件,还有鼠标事件(
mousedown
、
move
、
mouseup
、
mouseenter
、
mouseleave
、
mouseover
、
mousewheel
)和键盘事件(
keydown
、
keypress
、
keyup
)。对于支持触摸的设备,“触摸”事件优先于鼠标事件,如果触摸事件未被处理,则会触发鼠标事件。例如,用户触摸按钮且未显式处理触摸事件时,会触发
click
事件。 |
| 媒体事件(Media events) | 允许跟踪用户与HTML5视频和音频播放器的交互(如
pause
、
play
等)。 |
| 进度事件(Progress events) | 告知浏览器加载内容的进度。最常见的是
load
事件,在浏览器加载完元素及其所有依赖资源后触发。
error
事件也很有用,当元素不可用时(如损坏的图像链接),可以利用该事件采取相应措施。 |
| 触摸事件(Touch events) | 为支持触摸的设备提供复杂的支持,允许同时进行多次触摸(可在事件中查看
touches
属性),从而实现复杂的触摸处理,如支持手势(捏合、滑动等)。 |
3. Ajax
Ajax(最初是“Asynchronous JavaScript and XML”的缩写)实现了与服务器的异步通信,允许在不重新加载整个页面的情况下,用服务器的数据刷新页面上的元素。这一创新得益于2000年代初引入的
XMLHttpRequest
对象,开启了所谓的“Web 2.0”时代。
Ajax的核心概念很简单:浏览器端的JavaScript通过编程方式向服务器发送HTTP请求,服务器返回数据,通常是JSON格式(在JavaScript中处理JSON比XML容易得多)。这些数据用于在浏览器上实现特定功能。虽然Ajax基于HTTP(与非Ajax网页相同),但它减少了页面传输和渲染的开销,使Web应用程序的性能看起来更快。
要使用Ajax,我们需要一个服务器。以下是一个用Node.js编写的非常简单的服务器示例,它暴露了一个Ajax端点:
const http = require('http');
const server = http.createServer(function(req, res) {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
res.end(JSON.stringify({
platform: process.platform,
nodeVersion: process.version,
uptime: Math.round(process.uptime()),
}));
});
const port = 7070;
server.listen(port, function() {
console.log(`Ajax server started on port ${port}`);
});
这个服务器会报告平台信息(如“linux”、“darwin”、“win32”等)、Node.js的版本以及服务器的运行时间。
Ajax引入了一种安全漏洞,即跨域资源共享(CORS)。在上述示例中,我们添加了一个
Access-Control-Allow-Origin
头,值为
*
,这向客户端(浏览器)表明不要出于安全原因阻止该调用。在生产服务器上,你可以选择使用相同的协议、域名和端口(默认情况下会被允许),或者明确指定哪些协议、域名和端口可以访问该端点。为了演示目的,这样禁用CORS检查是安全的。
要启动这个服务器,只需运行以下命令:
$ babel-node ajaxServer.js
在浏览器中访问
http://localhost:7070
,你将看到服务器的输出。
现在我们有了服务器,就可以在示例HTML页面中编写Ajax代码。首先,在页面的
<body>
中添加一个占位符,用于接收服务器信息:
<div class="serverInfo">
Server is running on <span data-replace="platform">???</span>
with Node <span data-replace="nodeVersion">???</span>. It has
been up for <span data-replace="uptime">???</span> seconds.
</div>
然后,在HTML文件的底部(关闭
</body>
标签之前)添加以下脚本,使用
XMLHttpRequest
执行Ajax调用:
<script type="application/javascript;version=1.8">
function refreshServerInfo() {
const req = new XMLHttpRequest();
req.addEventListener('load', function() {
// TODO: put these values into HTML
console.log(this.responseText);
});
req.open('GET', 'http://localhost:7070', true);
req.send();
}
refreshServerInfo();
</script>
这个脚本执行了一个基本的Ajax调用。我们首先创建一个新的
XMLHttpRequest
对象,然后添加一个监听器,监听
load
事件(如果Ajax调用成功,该事件会被触发)。目前,我们只是将服务器响应(存储在
this.responseText
中)打印到控制台。接着调用
open
方法,建立与服务器的连接,指定这是一个HTTP GET请求,并提供服务器的URL。最后调用
send
方法,实际执行请求。在这个例子中,我们没有显式地向服务器发送任何数据,但可以这么做。
如果运行这个示例,你会在控制台看到服务器返回的数据。下一步是将这些数据插入到HTML中。我们对HTML进行了结构化处理,以便可以简单地查找具有
data-replace
属性的元素,并将其内容替换为服务器返回的数据。以下是实现该功能的代码:
req.addEventListener('load', function() {
// this.responseText是包含JSON的字符串,使用JSON.parse将其转换为对象
const data = JSON.parse(this.responseText);
// 在这个例子中,我们只希望替换具有类"serverInfo"的<div>内的文本
const serverInfo = document.querySelector('.serverInfo');
// 遍历服务器返回对象的键("platform", "nodeVersion", 和 "uptime")
Object.keys(data).forEach(p => {
// 查找要替换的元素(如果有)
const replacements =
serverInfo.querySelectorAll(`[data-replace="${p}"]`);
// 将所有元素的内容替换为服务器返回的值
for(let r of replacements) {
r.textContent = data[p];
}
});
});
由于
refreshServerInfo
是一个函数,我们可以在任何时候调用它。例如,若想每秒更新服务器信息5次(每200毫秒更新一次),可以添加以下代码:
setInterval(refreshServerInfo, 200);
这样,我们就能在浏览器中实时看到服务器运行时间的增加。
在这个示例中,当页面最初加载时,
<div class="serverInfo">
中包含带有问号的占位符文本。在慢速网络连接下,用户可能会在这些文本被服务器信息替换之前瞬间看到这些问号,这是“无样式内容闪烁”(FOUC)问题的一种变体。解决方法有两种:一是让服务器渲染包含正确值的初始页面;二是在内容更新之前隐藏整个元素,虽然这可能仍然会有一些突兀的效果,但比显示无意义的问号要好。
以上内容仅涵盖了进行Ajax请求的基本概念,若想了解更多信息,可查看MDN文章“Using XMLHttpRequest”。
4. jQuery
jQuery是一个流行的用于操作DOM和执行Ajax请求的库。虽然使用jQuery能做的事情用DOM API也能完成(毕竟jQuery是基于DOM API构建的),但它有三个主要优势:
-
兼容性
:jQuery能帮你避免不同浏览器(尤其是旧浏览器)在实现DOM API时的特性差异。
-
简化Ajax API
:提供了更简单的Ajax API,这在如今大量使用Ajax的网站中非常受欢迎。
-
增强DOM API
:对内置的DOM API提供了许多强大而简洁的增强功能。
尽管随着DOM API的改进和浏览器质量的提高,越来越多的Web开发者认为jQuery不再必要,他们推崇“原生JavaScript”的性能和纯粹性。但实际上,浏览器特性差异的问题并没有完全消失,jQuery仍然具有重要意义,它提供的许多功能若用DOM API重新实现,将非常耗时。无论你是否选择使用jQuery,由于它的广泛使用,很难完全避开它,明智的Web开发者应该了解其基础知识。
5. jQuery的使用
5.1 美元符号($)
jQuery是最早利用JavaScript中美元符号作为标识符的库之一。最初做出这个决定可能有些自负,但鉴于如今jQuery的广泛使用,这个决定显得很有远见。当你在项目中引入jQuery时,可以使用
jQuery
变量,也可以使用更简短的
$
别名。在本文中,我们使用
$
别名。
5.2 引入jQuery
引入jQuery最简单的方法是使用CDN:
<script src="//code.jquery.com/jquery-2.1.4.min.js"></script>
jQuery 2.x不再支持Internet Explorer 6、7和8。如果你需要支持这些浏览器,必须使用jQuery 1.x。jQuery 2.x由于无需支持这些旧浏览器,因此体积更小、更简单。
5.3 等待DOM加载
浏览器读取、解释和渲染HTML文件的过程很复杂,许多粗心的Web开发者在浏览器还未加载完DOM元素时就尝试以编程方式访问它们,从而陷入困境。jQuery允许你将代码放在一个回调函数中,只有在浏览器完全加载页面并构建好DOM后,该回调函数才会被调用:
$(document).ready(function() {
// 这里的代码在所有HTML加载完成且DOM构建好后运行
});
可以安全地多次使用这种技术,允许你将jQuery代码放在不同的位置,同时确保它能安全地等待DOM加载。还有一个等效的快捷方式:
$(function() {
// 这里的代码在所有HTML加载完成且DOM构建好后运行
});
在使用jQuery时,将所有代码放在这样的块中是一种常见的做法。
5.4 jQuery包装的DOM元素
使用jQuery操作DOM的主要方法是使用jQuery包装的DOM元素。任何使用jQuery进行的DOM操作都始于创建一个“包装”一组DOM元素的jQuery对象(要记住,这个集合可能为空,也可能只包含一个元素)。
jQuery函数(
$
或
jQuery
)创建一个jQuery包装的DOM元素集合(从这里开始,我们简单地将其称为“jQuery对象”,只需记住jQuery对象包含一组DOM元素)。jQuery函数主要有两种调用方式:使用CSS选择器或使用HTML。
-
使用CSS选择器调用
:返回与该选择器匹配的jQuery对象(类似于
document.querySelectorAll
的返回结果)。例如,要获取匹配所有
<p>
标签的jQuery对象,只需这样做:
const $paras = $('p');
$paras.length; // 匹配的段落标签数量
typeof $paras; // "object"
$paras instanceof $; // true
$paras instanceof jQuery; // true
-
使用HTML调用
:根据你提供的HTML创建新的DOM元素(类似于设置元素的
innerHTML属性时的情况):
const $newPara = $('<p>Newly created paragraph...</p>');
你会注意到,在这两个例子中,我们将jQuery对象赋值给的变量都以美元符号开头。这不是必需的,但这是一个很好的约定,能让你快速识别出是jQuery对象的变量。
5.5 操作元素
现在我们有了一些jQuery对象,能对它们做些什么呢?jQuery让添加和删除内容变得非常容易。最好的学习方式是在浏览器中加载示例HTML,并在控制台中执行这些示例。要做好重新加载文件的准备,因为我们会随意地删除、添加和修改内容。
jQuery提供了
text
和
html
方法,它们大致相当于给DOM元素的
textContent
和
innerHTML
属性赋值。例如,要将所有段落替换为相同的文本:
$('p').text('ALL PARAGRAPHS REPLACED');
同样,我们可以使用
html
方法来使用HTML内容:
$('p').html('<i>ALL</i> PARAGRAPHS REPLACED');
这引出了关于jQuery的一个重要点:jQuery让同时操作多个元素变得非常容易。使用DOM API时,
document.querySelectorAll
会返回多个元素,但我们需要自己遍历它们并执行所需的操作。而jQuery会为你处理所有的遍历,默认情况下假设你想对jQuery对象中的每个元素执行操作。如果你只想修改第三个段落,jQuery提供了
eq
方法,它返回一个包含单个元素的新jQuery对象:
$('p') // 匹配所有段落
.eq(2) // 第三个段落(索引从0开始)
.html('<i>THIRD</i> PARAGRAPH REPLACED');
要删除元素,只需在jQuery对象上调用
remove
方法。要删除所有段落:
$('p').remove();
这展示了jQuery开发中的另一个重要范式:链式调用。所有jQuery方法都返回一个jQuery对象,这允许我们像上面那样链式调用方法。链式调用能实现强大而简洁的多元素操作。
jQuery提供了许多添加新内容的方法。其中一个方法是
append
,它将提供的内容追加到jQuery对象中的每个元素上。例如,如果我们想给每个段落添加一个脚注,可以很容易地做到:
$('p')
.append('<sup>*</sup>');
append
方法会给匹配的元素添加子元素,我们还可以使用
before
或
after
方法插入兄弟元素。以下是一个在每个段落前后添加
<hr>
元素的示例:
$('p')
.after('<hr>')
.before('<hr>');
这些插入方法还有对应的反向方法
appendTo
、
insertBefore
和
insertAfter
,在某些情况下很有用。例如:
$('<sup>*</sup>').appendTo('p'); // 等同于 $('p').append('<sup>*</sup>')
$('<hr>').insertBefore('p'); // 等同于 $('p').before('<hr>')
$('<hr>').insertAfter('p'); // 等同于 $('p').after('<hr>');
jQuery还让修改元素的样式变得非常容易。你可以使用
addClass
方法添加类,使用
removeClass
方法删除类,或者使用
toggleClass
方法切换类(如果元素没有该类,则添加;如果有,则删除)。
综上所述,无论是事件处理、Ajax通信还是使用jQuery操作DOM,都是前端开发中不可或缺的技能。掌握这些知识,能让你在Web开发领域更加得心应手。
6. jQuery操作元素的更多示例与技巧
6.1 元素的显示与隐藏
jQuery 提供了
show
和
hide
方法来控制元素的显示与隐藏。这两个方法可以接受一个可选的参数,用于指定动画的持续时间。
// 隐藏所有段落
$('p').hide();
// 显示所有段落,动画持续时间为 500 毫秒
$('p').show(500);
此外,
toggle
方法可以在显示和隐藏状态之间切换元素:
// 切换所有段落的显示状态,动画持续时间为 300 毫秒
$('p').toggle(300);
6.2 元素的属性操作
可以使用
attr
方法来获取或设置元素的属性。例如,获取图片的
src
属性,或者设置链接的
href
属性:
// 获取第一张图片的 src 属性
const imgSrc = $('img:first').attr('src');
// 设置所有链接的 href 属性
$('a').attr('href', 'https://example.com');
如果要移除属性,可以使用
removeAttr
方法:
// 移除所有图片的 alt 属性
$('img').removeAttr('alt');
6.3 元素的类操作
除了前面提到的
addClass
、
removeClass
和
toggleClass
方法,还可以使用
hasClass
方法来检查元素是否具有某个类:
// 检查第一个段落是否有 "highlight" 类
if ($('p:first').hasClass('highlight')) {
console.log('第一个段落有 highlight 类');
}
7. jQuery 中的事件绑定
jQuery 简化了事件绑定的过程,提供了多种方式来绑定事件。
7.1 基本事件绑定
可以使用
on
方法来绑定事件。例如,绑定点击事件:
$('button').on('click', function() {
alert('按钮被点击了!');
});
on
方法还可以绑定多个事件,用空格分隔:
$('input').on('focus blur', function() {
$(this).toggleClass('active');
});
7.2 事件委托
事件委托是一种强大的技术,它允许我们将事件处理程序绑定到一个父元素上,而不是每个子元素。当子元素上的事件触发时,事件会冒泡到父元素,由父元素的事件处理程序来处理。
<ul id="list">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
$('#list').on('click', 'li', function() {
alert('你点击了列表项:' + $(this).text());
});
这样,即使后续动态添加了新的列表项,点击事件仍然会被正确处理。
8. jQuery 中的动画效果
jQuery 提供了丰富的动画效果,可以让页面更加生动。
8.1 基本动画
除了前面提到的
show
、
hide
和
toggle
方法,还有
slideDown
、
slideUp
和
slideToggle
方法用于实现滑动效果:
// 下滑显示所有段落,动画持续时间为 400 毫秒
$('p').slideDown(400);
// 上滑隐藏所有段落,动画持续时间为 300 毫秒
$('p').slideUp(300);
// 切换所有段落的滑动显示状态,动画持续时间为 500 毫秒
$('p').slideToggle(500);
还有
fadeIn
、
fadeOut
和
fadeToggle
方法用于实现淡入淡出效果:
// 淡入显示所有段落,动画持续时间为 600 毫秒
$('p').fadeIn(600);
// 淡出隐藏所有段落,动画持续时间为 400 毫秒
$('p').fadeOut(400);
// 切换所有段落的淡入淡出状态,动画持续时间为 500 毫秒
$('p').fadeToggle(500);
8.2 自定义动画
可以使用
animate
方法来创建自定义动画。
animate
方法接受一个包含 CSS 属性和目标值的对象,以及一个可选的动画持续时间和回调函数。
$('div').animate({
width: '500px',
height: '300px',
opacity: 0.5
}, 1000, function() {
alert('动画完成!');
});
9. jQuery 中的 Ajax 请求
jQuery 提供了简化的 Ajax API,使得发送 Ajax 请求变得更加容易。
9.1
$.get
和
$.post
方法
$.get
方法用于发送 GET 请求,
$.post
方法用于发送 POST 请求。
// 发送 GET 请求
$.get('https://api.example.com/data', function(data) {
console.log('收到的数据:', data);
});
// 发送 POST 请求
$.post('https://api.example.com/submit', { name: 'John', age: 30 }, function(response) {
console.log('服务器响应:', response);
});
9.2
$.ajax
方法
$.ajax
方法是一个更强大的方法,可以用于发送各种类型的 Ajax 请求。它接受一个包含请求配置的对象。
$.ajax({
url: 'https://api.example.com/data',
method: 'GET',
dataType: 'json',
success: function(data) {
console.log('请求成功,收到的数据:', data);
},
error: function(error) {
console.error('请求出错:', error);
}
});
10. 总结
前端开发中的事件处理、Ajax 和 jQuery 是非常重要的知识点。事件处理让我们能够响应用户的交互,Ajax 实现了与服务器的异步通信,而 jQuery 则简化了 DOM 操作和 Ajax 请求。
在实际开发中,我们可以根据具体的需求选择合适的技术。对于简单的项目,使用原生的 DOM API 可能就足够了;但对于复杂的项目,jQuery 可以大大提高开发效率,减少兼容性问题。
同时,我们也应该不断学习和掌握新的前端技术,以适应不断变化的市场需求。希望本文能对你在前端开发的学习和实践中有所帮助。
以下是一个简单的流程图,展示了 jQuery 中事件绑定和处理的基本流程:
graph LR
A[选择元素] --> B[绑定事件]
B --> C{事件触发}
C -->|是| D[执行事件处理程序]
C -->|否| B
通过这个流程图,我们可以清晰地看到 jQuery 中事件绑定和处理的基本逻辑。先选择要绑定事件的元素,然后绑定事件,当事件触发时,执行相应的事件处理程序。如果事件未触发,则继续等待。
超级会员免费看
914

被折叠的 条评论
为什么被折叠?



