在掌握jQuery基礎(chǔ)選擇器、DOM操作和簡(jiǎn)單事件后,進(jìn)階的關(guān)鍵是理解“為什么這么用”和“如何更高效地用”。本文圍繞jQuery進(jìn)階開發(fā)的四大核心能力——鏈?zhǔn)秸{(diào)用原理、事件委托深化、Ajax全場(chǎng)景實(shí)戰(zhàn)、插件開發(fā)入門,通過(guò)“原理拆解+實(shí)戰(zhàn)案例+性能優(yōu)化”的結(jié)構(gòu),幫你突破基礎(chǔ)瓶頸,用jQuery解決更復(fù)雜的前端需求,同時(shí)理解背后的設(shè)計(jì)邏輯。
一、進(jìn)階核心1:吃透鏈?zhǔn)秸{(diào)用——從“會(huì)用”到“理解原理”
基礎(chǔ)開發(fā)中,我們常用$('#box').css('color','red').text('內(nèi)容')這類鏈?zhǔn)秸{(diào)用,但很少思考“為什么能鏈?zhǔn)秸{(diào)用”。理解其原理,能幫你避免鏈?zhǔn)秸{(diào)用中的常見錯(cuò)誤,甚至自定義支持鏈?zhǔn)秸{(diào)用的方法。
1. 鏈?zhǔn)秸{(diào)用的核心原理
jQuery的鏈?zhǔn)秸{(diào)用本質(zhì)是“方法執(zhí)行后返回當(dāng)前jQuery對(duì)象(this)”,而非返回undefined或其他值。例如:
// jQuery內(nèi)部方法簡(jiǎn)化邏輯
jQuery.fn.css = function(prop, value) {
? // 執(zhí)行設(shè)置樣式的邏輯...
? return this; // 關(guān)鍵:返回當(dāng)前jQuery對(duì)象
};
jQuery.fn.text = function(text) {
? // 執(zhí)行設(shè)置文本的邏輯...
? return this; // 繼續(xù)返回當(dāng)前jQuery對(duì)象
};
// 因此可鏈?zhǔn)秸{(diào)用:方法返回的對(duì)象繼續(xù)調(diào)用下一個(gè)方法
$('#box').css('color','red').text('內(nèi)容');
注意:并非所有方法都支持鏈?zhǔn)秸{(diào)用
部分方法因需要返回“具體結(jié)果”(而非jQuery對(duì)象),不支持鏈?zhǔn)秸{(diào)用,例如:
val():獲取輸入框值時(shí),返回字符串(如$('#input').val()),無(wú)法鏈?zhǔn)秸{(diào)用;
text():獲取文本時(shí),返回字符串(如$('#box').text()),無(wú)法鏈?zhǔn)秸{(diào)用;
find():返回新的jQuery對(duì)象(匹配子元素),支持鏈?zhǔn)秸{(diào)用,但操作的是新對(duì)象。
示例:鏈?zhǔn)秸{(diào)用的正確與錯(cuò)誤用法
// 正確:設(shè)置值的方法支持鏈?zhǔn)秸{(diào)用(返回jQuery對(duì)象)
$('#input')
? .val('進(jìn)階學(xué)習(xí)') // 設(shè)置值,返回jQuery對(duì)象
? .css('color','#2563eb') // 繼續(xù)操作當(dāng)前對(duì)象
? .attr('placeholder','請(qǐng)輸入內(nèi)容'); // 繼續(xù)操作
// 錯(cuò)誤:獲取值的方法不支持鏈?zhǔn)秸{(diào)用(返回字符串)
$('#input')
? .val() // 獲取值,返回字符串
? .css('color','red'); // 報(bào)錯(cuò):字符串沒(méi)有css()方法
2. 鏈?zhǔn)秸{(diào)用的進(jìn)階技巧:end()與add()
當(dāng)鏈?zhǔn)秸{(diào)用中使用find()、filter()等方法切換了操作對(duì)象后,可通過(guò)end()返回上一級(jí)對(duì)象,add()添加新的操作對(duì)象,避免重復(fù)選擇元素。
方法作用示例代碼
end()返回上一級(jí)jQuery對(duì)象$('#box').find('p').css('color','red').end().css('border','1px solid #ddd')
add()給當(dāng)前jQuery對(duì)象添加新元素$('p').add('div').css('margin','10px')
實(shí)戰(zhàn)案例:用end()簡(jiǎn)化多元素操作
<div id="box">
? <p class="item">子元素1</p>
? <p class="item">子元素2</p>
</div>
<script>
? $(function() {
? ? // 需求:給#box添加邊框,同時(shí)給內(nèi)部p標(biāo)簽設(shè)置紅色文本
? ? // 方法1:重復(fù)選擇元素(低效)
? ? $('#box').css('border', '1px solid #ddd');
? ? $('#box').find('p').css('color', 'red');
? ? // 方法2:用end()鏈?zhǔn)秸{(diào)用(高效,僅選擇一次#box)
? ? $('#box')
? ? ? .css('border', '1px solid #ddd') // 操作#box
? ? ? .find('p') // 切換到p標(biāo)簽
? ? ? .css('color', 'red') // 操作p標(biāo)簽
? ? ? .end(); // 返回上一級(jí)#box(若后續(xù)需繼續(xù)操作#box)
? });
</script>
二、進(jìn)階核心2:事件委托深化——解決動(dòng)態(tài)元素與性能問(wèn)題
基礎(chǔ)開發(fā)中,我們常用click()直接綁定事件,但面對(duì)“動(dòng)態(tài)生成的元素”或“大量元素”時(shí),這種方式會(huì)失效或?qū)е滦阅軉?wèn)題。進(jìn)階的關(guān)鍵是掌握on()方法的事件委托機(jī)制。
1. 為什么需要事件委托?
直接綁定事件(如$('.item').click())的兩大問(wèn)題:
動(dòng)態(tài)元素?zé)o效:事件綁定在初始元素上,后續(xù)動(dòng)態(tài)添加的元素不會(huì)繼承事件;
性能開銷大:若有1000個(gè).item元素,會(huì)綁定1000個(gè)事件處理函數(shù),占用大量?jī)?nèi)存。
事件委托的原理是“將事件綁定在靜態(tài)父元素上,通過(guò)事件冒泡觸發(fā)”,只需綁定1個(gè)事件,即可處理所有子元素的事件,包括動(dòng)態(tài)添加的子元素。
2. 事件委托的正確語(yǔ)法:on()的三參數(shù)用法
基礎(chǔ)綁定語(yǔ)法:$('selector').on('event', handler)(直接綁定)
事件委托語(yǔ)法:$('parentSelector').on('event', 'childSelector', handler)(委托綁定)
參數(shù)位置作用說(shuō)明
第一個(gè)參數(shù)事件名(如'click'、'input')支持多個(gè)事件,用空格分隔(如'click mouseenter')
第二個(gè)參數(shù)子元素選擇器(委托目標(biāo))必須是父元素的子元素,動(dòng)態(tài)添加的子元素也會(huì)生效
第三個(gè)參數(shù)事件處理函數(shù)函數(shù)中的this指向子元素(委托目標(biāo))
實(shí)戰(zhàn)案例1:處理動(dòng)態(tài)元素事件
<ul id="list">
? <!-- 初始元素 -->
? <li class="item">初始項(xiàng)1</li>
? <li class="item">初始項(xiàng)2</li>
<?href="zhiq.zhaopin.com/moment/82695346 ?">
<?href="zhiq.zhaopin.com/moment/82695381 ?">
<?href="zhiq.zhaopin.com/moment/82695400 ?">
<?href="zhiq.zhaopin.com/moment/82695413 ?">
<?href="zhiq.zhaopin.com/moment/82695426 ?">
<?href="zhiq.zhaopin.com/moment/82695442 ?">
<?href="zhiq.zhaopin.com/moment/82695450 ?">
<?href="zhiq.zhaopin.com/moment/82695452 ?">
<?href="zhiq.zhaopin.com/moment/82695457 ?">
<?href="zhiq.zhaopin.com/moment/82695461 ?">
</ul>
<button id="addItem">添加新項(xiàng)</button>
<script>
? $(function() {
? ? // 1. 錯(cuò)誤:直接綁定事件,動(dòng)態(tài)添加的項(xiàng)無(wú)效
? ? // $('.item').click(function() {
? ? //? alert($(this).text());
? ? // });
? ? // 2. 正確:事件委托,綁定在靜態(tài)父元素#list上
? ? $('#list').on('click', '.item', function() {
? ? ? alert($(this).text()); // this指向被點(diǎn)擊的.item(包括動(dòng)態(tài)添加的)
? ? });
? ? // 動(dòng)態(tài)添加項(xiàng)
? ? $('#addItem').click(function() {
? ? ? const newItem = $(`<li class="item">動(dòng)態(tài)項(xiàng)${$('#list .item').length + 1}</li>`);
? ? ? $('#list').append(newItem);
? ? });
? });
</script>
實(shí)戰(zhàn)案例2:優(yōu)化大量元素事件性能
<!-- 假設(shè)有100個(gè)列表項(xiàng) -->
<ul id="bigList">
? <!-- 100個(gè)<li class="item">...</li> -->
</ul>
<script>
? $(function() {
? ? // 1. 低效:直接綁定,創(chuàng)建100個(gè)事件處理函數(shù)
? ? // $('#bigList .item').click(function() {
? ? //? $(this).toggleClass('active');
? ? // });
? ? // 2. 高效:事件委托,僅創(chuàng)建1個(gè)事件處理函數(shù)
? ? $('#bigList').on('click', '.item', function() {
? ? ? $(this).toggleClass('active');
? ? });
? });
</script>
3. 事件委托的進(jìn)階技巧:事件數(shù)據(jù)與阻止冒泡
(1)傳遞事件數(shù)據(jù)
通過(guò)on()的第二個(gè)參數(shù)(或第三個(gè)參數(shù),取決于是否委托)傳遞數(shù)據(jù),在事件處理函數(shù)中通過(guò)event.data獲取,避免使用全局變量。
// 傳遞數(shù)據(jù):給事件處理函數(shù)傳遞用戶ID
$('#userList').on('click', '.user-item', { role: 'admin' }, function(event) {
? const userId = $(this).data('user-id');
? const userRole = event.data.role; // 獲取傳遞的數(shù)據(jù)
? console.log(`用戶ID:${userId},角色:${userRole}`);
});
(2)阻止事件冒泡與默認(rèn)行為
在事件委托中,若需要阻止事件冒泡(避免父元素事件觸發(fā))或阻止默認(rèn)行為(如鏈接跳轉(zhuǎn)),需在事件處理函數(shù)中使用event.stopPropagation()和event.preventDefault()。
<div id="outer">
? <a class="link">jQuery官網(wǎng)</a>
</div>
<script>
? $(function() {
? ? // 父元素事件
? ? $('#outer').on('click', function() {
? ? ? alert('點(diǎn)擊了外層div');
? ? });
? ? // 子元素事件委托,阻止冒泡和默認(rèn)行為
? ? $('#outer').on('click', '.link', function(event) {
? ? ? event.stopPropagation(); // 阻止事件冒泡,外層div事件不觸發(fā)
? ? ? event.preventDefault(); // 阻止默認(rèn)行為,鏈接不跳轉(zhuǎn)
? ? ? alert('點(diǎn)擊了鏈接,但不跳轉(zhuǎn)');
? ? });
? });
</script>
三、進(jìn)階核心3:Ajax全場(chǎng)景實(shí)戰(zhàn)——從基礎(chǔ)請(qǐng)求到復(fù)雜交互
jQuery的Ajax模塊是其核心優(yōu)勢(shì)之一,基礎(chǔ)開發(fā)中可能用過(guò)$.get()或$.post(),但進(jìn)階開發(fā)需要掌握更靈活的$.ajax()方法,處理“請(qǐng)求參數(shù)、響應(yīng)解析、錯(cuò)誤處理、加載狀態(tài)”等全場(chǎng)景需求。
1. jQuery Ajax的核心方法對(duì)比
jQuery提供了多個(gè)Ajax相關(guān)方法,不同方法適用于不同場(chǎng)景:
方法作用適用場(chǎng)景語(yǔ)法示例
$.get(url, data, success)發(fā)送GET請(qǐng)求(獲取數(shù)據(jù))簡(jiǎn)單數(shù)據(jù)獲?。ㄈ缌斜頂?shù)據(jù))$.get('/api/list', { page: 1 }, function(res) { ... })
$.post(url, data, success)發(fā)送POST請(qǐng)求(提交數(shù)據(jù))簡(jiǎn)單數(shù)據(jù)提交(如表單提交)$.post('/api/login', { username: 'admin' }, function(res) { ... })
$.ajax(options)通用Ajax方法(支持所有配置)復(fù)雜需求(如文件上傳、超時(shí)設(shè)置)$.ajax({ url: '/api/data', type: 'GET', ... })
2.$.ajax()全配置實(shí)戰(zhàn):處理復(fù)雜請(qǐng)求
$.ajax()通過(guò)配置對(duì)象支持“請(qǐng)求類型、參數(shù)、響應(yīng)類型、超時(shí)、錯(cuò)誤處理”等所有Ajax需求,是進(jìn)階開發(fā)的首選方法。
核心配置項(xiàng)說(shuō)明
配置項(xiàng)類型作用示例值
urlString請(qǐng)求地址/api/user
typeString請(qǐng)求類型(GET/POST/PUT/DELETE)'POST'
dataObject/String請(qǐng)求參數(shù){ id: 1, name: 'admin' }
dataTypeString預(yù)期響應(yīng)數(shù)據(jù)類型(json/text/html)'json'(自動(dòng)解析JSON)
contentTypeString請(qǐng)求頭Content-Type'application/json'(JSON格式提交)
timeoutNumber請(qǐng)求超時(shí)時(shí)間(毫秒)5000(5秒超時(shí))
beforeSendFunction請(qǐng)求發(fā)送前執(zhí)行(如設(shè)置loading)function(xhr) { $('#loading').show(); }
successFunction請(qǐng)求成功執(zhí)行function(res) { console.log(res); }
errorFunction請(qǐng)求失敗執(zhí)行(如網(wǎng)絡(luò)錯(cuò)誤、超時(shí))function(xhr, status) { alert('請(qǐng)求失敗'); }
completeFunction請(qǐng)求完成后執(zhí)行(無(wú)論成功失?。ゝunction() { $('#loading').hide(); }
實(shí)戰(zhàn)案例1:JSON格式提交與響應(yīng)解析
<!-- 表單 -->
<form id="userForm">
? <input type="text" name="username" placeholder="用戶名">
? <input type="password" name="password" placeholder="密碼">
<?href="zhiq.zhaopin.com/moment/82695464 ?">
<?href="zhiq.zhaopin.com/moment/82695468 ?">
<?href="zhiq.zhaopin.com/moment/82695473 ?">
<?href="zhiq.zhaopin.com/moment/82695476 ?">
<?href="zhiq.zhaopin.com/moment/82695479 ?">
<?href="zhiq.zhaopin.com/moment/82695481 ?">
<?href="zhiq.zhaopin.com/moment/82695485 ?">
<?href="zhiq.zhaopin.com/moment/82695521 ?">
<?href="zhiq.zhaopin.com/moment/82695529 ?">
<?href="zhiq.zhaopin.com/moment/82695541 ?">
? <button type="submit">提交</button>
</form>
<div id="loading" style="display: none;">加載中...</div>
<script>
? $(function() {
? ? $('#userForm').on('submit', function(e) {
? ? ? e.preventDefault();
? ? ? // 1. 獲取表單數(shù)據(jù)
? ? ? const formData = {
? ? ? ? username: $('[name="username"]').val().trim(),
? ? ? ? password: $('[name="password"]').val().trim()
? ? ? };
? ? ? // 2. 發(fā)送Ajax請(qǐng)求
? ? ? $.ajax({
? ? ? ? url: '/api/login', // 實(shí)際項(xiàng)目中替換為真實(shí)接口地址
? ? ? ? type: 'POST',
? ? ? ? dataType: 'json', // 預(yù)期響應(yīng)是JSON格式
? ? ? ? contentType: 'application/json', // 請(qǐng)求體是JSON格式
? ? ? ? data: JSON.stringify(formData), // 將對(duì)象轉(zhuǎn)為JSON字符串
? ? ? ? timeout: 5000, // 5秒超時(shí)
? ? ? ? beforeSend: function() {
? ? ? ? ? // 請(qǐng)求前顯示loading
? ? ? ? ? $('#loading').show();
? ? ? ? },
? ? ? ? success: function(res) {
? ? ? ? ? // 請(qǐng)求成功:根據(jù)響應(yīng)處理
? ? ? ? ? if (res.code === 200) {
? ? ? ? ? ? alert('登錄成功!');
? ? ? ? ? ? window.location.href = '/home'; // 跳轉(zhuǎn)到首頁(yè)
? ? ? ? ? } else {
? ? ? ? ? ? alert('登錄失?。? + res.msg);
? ? ? ? ? }
? ? ? ? },
? ? ? ? error: function(xhr, status) {
? ? ? ? ? // 請(qǐng)求失?。禾幚砭W(wǎng)絡(luò)錯(cuò)誤、超時(shí)等
? ? ? ? ? if (status === 'timeout') {
? ? ? ? ? ? alert('請(qǐng)求超時(shí),請(qǐng)稍后重試');
? ? ? ? ? } else {
? ? ? ? ? ? alert('網(wǎng)絡(luò)錯(cuò)誤,請(qǐng)求失敗');
? ? ? ? ? }
? ? ? ? },
? ? ? ? complete: function() {
? ? ? ? ? // 請(qǐng)求完成:隱藏loading(無(wú)論成功失?。?/p>
? ? ? ? ? $('#loading').hide();
? ? ? ? }
? ? ? });
? ? });
? });
</script>
實(shí)戰(zhàn)案例2:文件上傳(FormData格式)
文件上傳需用FormData格式提交,$.ajax()需配置contentType: false和processData: false,避免jQuery自動(dòng)處理請(qǐng)求體。
<!-- 文件上傳表單 -->
<input type="file" id="fileInput" accept="image/*">
<button id="uploadBtn">上傳圖片</button>
<div id="progress" style="width: 300px; height: 10px; border: 1px solid #ddd; margin-top: 10px; display: none;">
? <div id="progressBar" style="width: 0; height: 100%; background: #4096ff;"></div>
</div>
<script>
? $(function() {
? ? $('#uploadBtn').click(function() {
? ? ? const file = $('#fileInput')[0].files[0]; // 獲取選擇的文件
? ? ? if (!file) {
? ? ? ? alert('請(qǐng)選擇圖片文件');
? ? ? ? return;
? ? ? }
? ? ? // 1. 創(chuàng)建FormData對(duì)象,添加文件和其他參數(shù)
? ? ? const formData = new FormData();
? ? ? formData.append('file', file); // 添加文件
? ? ? formData.append('type', 'image'); // 添加其他參數(shù)
? ? ? // 2. 發(fā)送文件上傳請(qǐng)求
? ? ? $.ajax({
? ? ? ? url: '/api/upload', // 實(shí)際項(xiàng)目中替換為真實(shí)接口地址
? ? ? ? type: 'POST',
? ? ? ? data: formData,
? ? ? ? contentType: false, // 關(guān)鍵:不設(shè)置Content-Type,由瀏覽器自動(dòng)處理
? ? ? ? processData: false, // 關(guān)鍵:不處理請(qǐng)求數(shù)據(jù)(避免將FormData轉(zhuǎn)為字符串)
? ? ? ? xhr: function() {
? ? ? ? ? // 自定義XHR對(duì)象,監(jiān)聽上傳進(jìn)度
? ? ? ? ? const xhr = $.ajaxSettings.xhr();
? ? ? ? ? if (xhr.upload) {
? ? ? ? ? ? xhr.upload.addEventListener('progress', function(e) {
? ? ? ? ? ? ? if (e.lengthComputable) {
? ? ? ? ? ? ? ? const percent = Math.round((e.loaded / e.total) * 100); // 計(jì)算上傳進(jìn)度
? ? ? ? ? ? ? ? $('#progress').show();
? ? ? ? ? ? ? ? $('#progressBar').css('width', percent + '%');
? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? ? }
? ? ? ? ? return xhr;
? ? ? ? },
? ? ? ? success: function(res) {
? ? ? ? ? if (res.code === 200) {
? ? ? ? ? ? alert('上傳成功!圖片地址:' + res.data.url);
? ? ? ? ? ? $('#progressBar').css('width', '100%');
? ? ? ? ? } else {
? ? ? ? ? ? alert('上傳失?。? + res.msg);
? ? ? ? ? }
? ? ? ? },
? ? ? ? error: function() {
? ? ? ? ? alert('上傳失敗,請(qǐng)稍后重試');
? ? ? ? }
? ? ? });
? ? });
? });
</script>
3. Ajax進(jìn)階技巧:全局事件與請(qǐng)求攔截
(1)全局Ajax事件
jQuery提供全局Ajax事件,可在所有Ajax請(qǐng)求中統(tǒng)一</doubaocanvas>