入门基础
引入 jQuery
通过 CDN 引入 jQuery 可以实现快速、缓存的加载。压缩版(.min.js)用于生产环境;未压缩版用于调试。务必提供本地回退方案,以防 CDN 不可访问。使用 npm 时,可以将 jQuery 作为模块导入——对于 Webpack/Vite 等打包工具很有用。jQuery 3.7.x 是当前版本线;不再支持旧版 IE。
<!-- Production (CDN, minified) -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- Development (CDN, uncompressed) -->
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<!-- Fallback: load local if CDN fails -->
<script>
window.jQuery || document.write(
'<script src="js/jquery-3.7.1.min.js"><\/script>'
)
</script>
<!-- npm install -->
<!-- npm install jquery -->文档就绪
将操作 DOM 的代码包装在 $(document).ready() 中,使其仅在 DOM 树解析完成后运行(无需等待图片)。$(fn) 是简写形式。如果 <script> 位于 <body> 末尾,DOM 已经可用,可以跳过 ready。jQuery 的 ready 类似于 DOMContentLoaded——它在 window.load(等待图片/iframe)之前触发。
// run code after DOM is fully parsed
$(document).ready(function () {
console.log('DOM ready')
})
// shorter equivalent
$(function () {
console.log('DOM ready')
})
// modern preferred (no ready needed at end of body)
// <script> at the end of <body> runs after DOM exists
// pass a function to $ — jQuery treats it as ready handler
$(function () {
$('.btn').on('click', function () {
alert('clicked')
})
})jQuery 对象($)
$ 只是 jQuery 的别名——两者工作方式完全相同。作为函数,$ 用于选择元素(返回 jQuery 集合)。作为命名空间,$.foo 提供工具方法。如果其他库使用了 $,调用 $.noConflict() 释放它;你仍然可以使用 jQuery 全名,或将 $ 传入 ready 回调以在局部使用别名。
// $ is an alias for jQuery
console.log($ === jQuery) // true
// $ as a function: select elements
$('p') // all paragraphs
$('#main') // element with id main
$('.btn.primary') // elements with class btn and primary
// $ as a namespace: utility methods
$.each([1, 2, 3], function (i, v) { console.log(i, v) })
$.extend({}, defaults, options)
$.type([]) // 'array'
// noConflict: release $ back to other libraries
$.noConflict()
jQuery(function ($) { /* $ available here */ })链式调用
大多数 jQuery 方法返回同一个 jQuery 集合,因此可以链式调用——这是 jQuery 的标志性特性。使用 .end() 在遍历方法(.find()、.filter())之后弹回上一个集合。缩进使链式遍更易读。避免过长的链——它们会变得难以调试。对于不可链式的方法(如 .width() getter),链会中断,因为它们返回值而非集合。
// most methods return the jQuery object, enabling chains
$('.box')
.addClass('active')
.css('color', 'red')
.slideDown(300)
.text('Hello')
// break across lines for readability
$('#nav')
.find('.item')
.addClass('highlight')
.end()
.find('.link')
.attr('target', '_blank')
// .end() pops the current filter back to previous set无冲突模式
$.noConflict() 将 $ 的控制权归还给最先定义它的库。调用后,使用完整的 jQuery 名称,或将 jQuery 赋值给自定义变量。IIFE 模式 (function($){...})(jQuery) 是在模块内保持 $ 为局部别名的经典方式,同时将全局 $ 留给其他库——在 WordPress 和遗留环境中必不可少。
// release $ to other libraries (e.g., Prototype)
$.noConflict()
// use jQuery full name
jQuery('.btn').on('click', function () { /* ... */ })
// or assign jQuery to a custom alias
const jq = $.noConflict()
jq('.btn').hide()
// IIFE pattern: keep $ inside a local scope
(function ($) {
$('.btn').on('click', function () { /* ... */ })
})(jQuery)选择器
基础选择器
jQuery 使用 CSS 选择器(大部分与 querySelectorAll 兼容)。三大基础:标签名、#id、.class。逗号组合选择器(并集)。#id 最快(使用 getElementById)。通配符 * 很慢——避免在大文档上使用。选择器结果始终是 jQuery 集合(类数组),即使匹配 0 或 1 个元素。
// element selector
$('div') // all <div> elements
$('p') // all <p> elements
// id selector
$('#header') // element with id="header"
// class selector
$('.btn') // all elements with class="btn"
$('.btn.primary') // .btn AND .primary
// multiple (comma = OR)
$('div, p, .btn') // divs, paragraphs, and .btn
// universal
$('*') // all elements (slow — avoid)层级与组合器
组合器描述关系:空格(后代)、>(直接子元素)、+(相邻兄弟)、~(后续兄弟)。从右到左的求值意味着 $('ul li') 先找到所有 <li>,再筛选出 <ul> 内的——因此右侧具体的选择器性能更好。避免在大型文档上使用深层组合器;通过 $(container).find(...) 限定范围以获得更好的性能。
// descendant: space (any depth)
$('ul li') // <li> anywhere inside <ul>
// child: > (direct children only)
$('ul > li') // <li> directly inside <ul>
// adjacent sibling: + (immediately next)
$('h2 + p') // <p> right after <h2>
// general sibling: ~ (all following siblings)
$('h2 ~ p') // all <p> siblings after <h2>
// multiple levels
$('#nav .item > a span')属性选择器
属性选择器按属性值定位元素:[attr](存在)、[attr=val](精确)、[attr^=val](开头)、[attr$=val](结尾)、[attr*=val](包含)、[attr~=val](单词)。值加引号更安全。[attr!=val] 是 jQuery 扩展(使用 .not('[attr=val]') 以符合 CSS 规范)。适用于表单(input 类型)、链接(href 模式)和 data-* 属性。
// has attribute
$('[href]') // any element with href
$('[data-id]') // any element with data-id
// exact value
$('[type="text"]') // type="text"
$('[href="https://example.com"]')
// substring matches
$('[href^="https"]') // starts with "https"
$('[href$=".pdf"]') // ends with ".pdf"
$('[href*="example"]') // contains "example"
// whitespace-separated word
$('[class~="active"]') // class contains word "active"
// inequality (jQuery extension, not CSS)
$('[href!="https://x.com"]')表单与输入选择器
表单伪选择器(:input、:text、:checked 等)是 jQuery 扩展——不属于 CSS,因此无法使用 querySelectorAll 的快速路径。在大型表单上为了性能,使用 $('input[type=text]') 而非 $(':text')。:checked 和 :selected 对于读取表单状态至关重要。它们是动态的——每次访问都会重新求值。
// :input matches all form controls
$(':input') // input, textarea, select, button
// by type
$(':text') // <input type="text">
$(':password')
$(':checkbox')
$(':radio')
$(':submit')
$(':file')
// by state
$(':checked') // checked checkbox/radio or selected option
$(':selected') // selected <option>
$(':disabled')
$(':enabled')
$(':focus')
// combined with class
$('input.required:visible')过滤与内容选择器
过滤器缩小选择范围::first/:last、:even/:odd(0 索引)、:eq(n)、:gt(n)、:lt(n)。内容过滤器(:contains、:has、:empty、:parent)基于内容匹配。:visible 和 :hidden 基于 offsetWidth/Height 和 CSS(注意:visibility:hidden 的元素在 jQuery 3+ 中是 :hidden;以前被视为 :visible)。许多是 jQuery 扩展——为获得更好性能,建议使用 .filter()、.first()、.eq() 方法。
// basic filters
$('li:first') // first <li>
$('li:last') // last <li>
$('li:even') // even-indexed (0, 2, 4...)
$('li:odd') // odd-indexed
$('li:eq(2)') // 3rd <li> (0-indexed)
$('li:gt(2)') // <li> after index 2
$('li:lt(2)') // <li> before index 2
// content filters
$('div:contains("Hello")') // divs containing text "Hello"
$('div:empty') // divs with no children
$('div:has(p)') // divs containing <p>
$('div:parent') // divs that have children
// visibility
$(':visible')
$(':hidden')事件
使用 .on() 绑定事件
.on() 是现代的、多合一事件绑定方法(取代了 .bind/.delegate/.live)。传入事件名和处理函数。处理函数内的 'this' 是 DOM 元素(非 jQuery 包装——需要时用 $(this) 包装)。使用事件命名空间(click.myapp)可以解绑特定处理函数而不影响其他。多个事件可共享一个处理函数(空格分隔)或有独立处理函数(对象形式)。
// basic binding
$('.btn').on('click', function (e) {
console.log('clicked!', this) // 'this' is the DOM element
})
// multiple events, same handler
$('.box').on('mouseenter mouseleave', function () {
$(this).toggleClass('hover')
})
// multiple events, different handlers
$('.input').on({
focus: function () { $(this).addClass('focused') },
blur: function () { $(this).removeClass('focused') }
})
// named events (for targeted unbinding)
$('.btn').on('click.myapp', handler)
$('.btn').off('click.myapp') // only removes this one事件委托
事件委托在父元素上绑定一个处理函数,通过冒泡捕获子元素的事件。.on() 的第二个选择器参数('li')过滤哪些子元素触发处理函数。优点:一个处理函数代替 N 个,自动支持动态添加的元素,更低内存。对于长列表或 SPA 必不可少。'this' 是匹配的子元素(li),不是父元素。使用 e.delegateTarget 访问父元素。
// delegate: handle events on children, now or future
$('#list').on('click', 'li', function (e) {
// 'this' is the <li> that was clicked
$(this).toggleClass('done')
})
// works for <li> added AFTER binding too
$('#list').append('<li>New item (also clickable)</li>')
// WHY: instead of binding to each <li>,
// bind ONCE to the parent; clicks bubble up
// - 1 handler instead of N
// - works for dynamically added elements
// - lower memory, faster setup事件对象与方法
事件对象(e)携带信息和控制方法。preventDefault 阻止浏览器默认行为(链接导航、表单提交)。stopPropagation 阻止冒泡;stopImmediatePropagation 还阻止同一元素上的其他处理函数。e.target 是实际点击的元素(可能是子元素);e.currentTarget 是绑定处理函数的元素(= this)。通过 .on() 的可选第二个参数向处理函数传递数据。
$('.link').on('click', function (e) {
e.preventDefault() // stop default action (e.g., navigation)
e.stopPropagation() // stop bubbling to parents
e.stopImmediatePropagation() // stop other handlers on same element
console.log(e.type) // 'click'
console.log(e.target) // the actual clicked element
console.log(e.currentTarget) // the element with the handler (= this)
console.log(e.which) // mouse button or key code
console.log(e.pageX, e.pageY) // mouse coordinates
// data passed via .on(name, data, handler)
})
$('.btn').on('click', { greeting: 'hi' }, function (e) {
console.log(e.data.greeting) // 'hi'
})简写事件方法
简写方法(.click、.focus、.submit 等)传入函数时绑定处理函数,不传参时触发事件。.hover(enter, leave) 是 mouseenter+mouseleave 的便捷方法。它们等同于 .on('event', fn) 但更短。一些简写(.load、.error)在 jQuery 3 中被移除——使用 .on('load', ...) 代替。新代码推荐使用 .on(),它更明确且支持委托。
// shorthand: bind a handler (or trigger if no handler)
$('.btn').click(function () { /* ... */ })
$('.btn').click() // trigger click
// common shorthands
$('.box').hover(enterFn, leaveFn) // mouseenter + mouseleave
$('.input').focus(function () { /* ... */ })
$('.input').blur(function () { /* ... */ })
$('.form').submit(function (e) {
e.preventDefault()
/* ... */
})
$('.el').mouseenter(fn).mouseleave(fn).mousemove(fn)
$(window).resize(fn).scroll(fn)
// deprecated: .load(), .unload(), .error() (removed in jQuery 3)触发与一次性事件
.trigger('event') 触发处理函数和原生行为(如导航);.triggerHandler() 只触发 jQuery 绑定的处理函数并返回处理函数的返回值(不可链式)。.one() 绑定一个在首次调用后自动移除的处理函数——非常适合首次点击提示、一次性设置。自定义事件让组件通信:$(document).trigger('myapp:ready'),监听者用 .on('myapp:ready', fn) 订阅。
// trigger an event programmatically
$('.btn').trigger('click')
$('.btn').click() // shorthand
// trigger with extra data
$('.box').trigger('custom', [1, 2, 3])
// custom events
$('.box').on('custom', function (e, a, b, c) {
console.log(a, b, c) // 1 2 3
})
// fire handler only once
$('.btn').one('click', function () {
alert('This fires only once')
})
// trigger only native handlers, not custom
$('.link').triggerHandler('click')DOM 操作
获取与设置内容
.html() 处理 HTML 标记(类似 innerHTML)——标签会被解析。.text() 用于纯文本——标签被转义并按字面显示(对用户输入更安全,防 XSS)。.val() 用于表单控件(input、select、textarea)。三者在不传参时为 getter(返回第一个元素的值),传参时为 setter(应用到所有匹配元素)。Setter 接受回调 (index, oldValue) => newValue。
// .html(): get or set HTML content
$('#box').html() // get innerHTML
$('#box').html('<p>Hello</p>') // set innerHTML
// .text(): get or set text (HTML-escaped)
$('#box').text() // get text content
$('#box').text('<p>Not parsed</p>') // displays literally
// setter with callback (index, old value)
$('p').text(function (i, oldText) {
return i + ': ' + oldText
})
// .val(): form value
$('input').val() // get
$('input').val('new value') // set
$('select').val() // get selected value
$('select').val(['a', 'b']) // multi-select插入元素
通过向 $() 传 HTML 来创建元素:$('<div class="x">...</div>')。append/prepend 在内部插入(末尾/开头);before/after 在外部插入(作为兄弟)。'To' 变体(appendTo、prependTo、insertBefore、insertAfter)反转主体——便于在新元素上链式调用。新插入的元素如果通过委托绑定则继承事件处理函数,但在插入前直接绑定的则不会。
// create new element
const $li = $('<li class="item">New item</li>')
// add as last child of each matched element
$('ul').append($li)
$('ul').append('<li>Direct string</li>')
// add as first child
$('ul').prepend('<li>First</li>')
// insert before/after matched elements
$('.box').before('<hr>')
$('.box').after('<hr>')
// reverse direction (insert target into container)
$('<p>Hi</p>').appendTo('#main') // same as $('#main').append(...)
$('<p>Hi</p>').insertBefore('#nav')移除与替换
.remove() 删除元素并清理其数据/事件。.detach() 从 DOM 移除但保留数据/事件——用于临时重定位元素。.empty() 清除子元素但保留元素本身。.replaceWith() 用新内容替换每个元素。.unwrap() 移除父元素,将元素上移。始终使用 .remove() 或 .detach()——绝不要在父元素上用 .html(''),那会导致子元素的数据/事件泄漏。
// remove element (and its data + events)
$('.box').remove()
// detach: remove but keep events/data (for re-insertion)
const $el = $('.box').detach()
// ... later
$('body').append($el) // events still work
// empty: remove all children (keep element)
$('.box').empty()
// replace with new content
$('.old').replaceWith('<div class="new">New</div>')
// unwrap: remove parent (keep element)
$('.box').unwrap()包装与克隆
.wrap() 逐个包装每个元素;.wrapAll() 将整个集合作为一组包装;.wrapInner() 包装内容(不包装元素本身)。.clone() 创建深拷贝——传 true 同时克隆事件处理函数和数据(默认 false,所以拷贝不会触发处理函数)。.unwrap() 移除直接父元素。这些方法在不重写 HTML 字符串的情况下重构标记非常强大。
// wrap each element in a structure
$('p').wrap('<div class="wrapper"></div>')
// each <p> becomes <div class="wrapper"><p>...</p></div>
// wrap all matched elements together
$('p').wrapAll('<div class="all"></div>')
// wrap inner contents
$('p').wrapInner('<span></span>')
// <p><span>...</span></p>
// clone (with events)
const $copy = $('.box').clone(true) // true = clone events too
$('body').append($copy)
// unwrap (remove wrapper)
$('.box').unwrap()遍历与过滤
.each() 遍历——'this' 是原始 DOM 元素;return false 跳出,其他继续。.map() 转换为新的 jQuery 集合;调用 .get() 转为普通数组。.filter() 按选择器或回调缩小范围;.not() 是反向操作。.is() 返回布尔值(是否有任何元素匹配?)——用于条件判断。避免用 .eq(i) 的 for 循环;使用 .each() 或 .map() 写地道的 jQuery。
// .each: iterate (this = DOM element, i = index)
$('li').each(function (i, el) {
console.log(i, this, el) // el === this
if (i === 2) return false // break
// return true to continue
})
// .map: transform to new array
const ids = $('li').map(function (i, el) {
return $(el).data('id')
}).get() // .get() converts to plain array
// .filter: keep matching
$('li').filter('.active').addClass('on')
$('li').filter(function (i) { return i % 2 === 0 })
// .not: remove matching
$('li').not('.active').addClass('off')
// .is: returns boolean (does ANY match?)
if ($('li').is('.active')) { /* at least one is active */ }CSS 操作
获取与设置 CSS 属性
.css() 获取计算样式(始终是解析后的值,即使来自 CSS 规则)或设置内联样式。Getter 传入单个属性名(返回字符串)。Setter 传入 (prop, value) 或 prop:value 对象。属性名可以是 camelCase 或 kebab-case(后者需引号)。数值对大多属性默认为像素。避免用 .css() 做布局——使用类和样式表以保持可维护性。
// get computed style (first matched element)
$('.box').css('color') // 'rgb(255, 0, 0)'
$('.box').css('font-size') // '14px'
// set single property
$('.box').css('color', 'red')
// set multiple properties
$('.box').css({
color: 'red',
'background-color': '#eee', // kebab-case needs quotes
fontSize: '16px', // or use camelCase
marginTop: '10px'
})
// setter with callback (index, old value)
$('div').css('width', function (i, old) {
return (parseInt(old) + 50) + 'px'
})类操作
类方法是设置元素样式的首选方式——将表现留在 CSS 中,而非 JS。.addClass、.removeClass、.toggleClass 接受空格分隔的类列表。.toggleClass(name, bool) 根据布尔值添加/移除——用于状态驱动的 UI。.hasClass(name) 返回布尔值(只检查第一个元素)。性能上,类方法比 .css() 快得多,因为它们批量更改并让浏览器优化。
// add class
$('.box').addClass('active')
// remove class
$('.box').removeClass('active')
// toggle (add if absent, remove if present)
$('.box').toggleClass('active')
// toggle based on boolean
$('.box').toggleClass('active', isValid)
// add/remove multiple (space-separated)
$('.box').addClass('active highlighted urgent')
$('.box').removeClass('active highlighted')
// check if has class (boolean)
if ($('.box').hasClass('active')) { /* ... */ }
// callback form (index, hasClass)
$('div').toggleClass('even', function (i) {
return i % 2 === 0
})宽度与高度
.width()/.height() = 内容框(不含 padding/border)。.innerWidth()/.innerHeight() = 内容 + padding。.outerWidth()/.outerHeight() = 内容 + padding + border。.outerWidth(true) 包含 margin。Setter 接受数值(px)或字符串。对于 window/document,$(window) 是视口,$(document) 是整个页面。它们返回数值(不像 .css('width') 返回带单位的字符串),便于数学运算。
// content size (excludes padding, border, margin)
$('.box').width() // e.g., 200
$('.box').width(300) // set content width to 300px
$('.box').height()
// inner (content + padding)
$('.box').innerWidth()
$('.box').innerHeight()
// outer (content + padding + border)
$('.box').outerWidth()
$('.box').outerHeight()
// outer including margin
$('.box').outerWidth(true)
// window & document
$(window).width() // viewport width
$(document).height() // full document height位置与偏移
.offset() 返回 {top, left},相对于文档(用于绝对定位或拖放)。.position() 返回相对于 offset parent(最近的 position: relative/absolute/fixed 祖先)的坐标。.scrollTop()/.scrollLeft() 获取或设置滚动位置——$(window).scrollTop(0) 滚动到顶部。.offsetParent() 查找定位祖先。这些对于动画、拖放和无限滚动至关重要。
// .offset(): position relative to document
const off = $('.box').offset()
console.log(off.left, off.top) // e.g., 100, 50
$('.box').offset({ top: 200, left: 100 }) // set
// .position(): position relative to offset parent
const pos = $('.box').position()
console.log(pos.left, pos.top)
// .scrollTop() / .scrollLeft(): scroll position
$(window).scrollTop() // how far scrolled down
$(window).scrollTop(0) // scroll to top
$('.container').scrollTop(100)
// .offsetParent(): nearest positioned ancestor
$('.box').offsetParent()滚动与坐标
滚动处理常用于固定导航、无限滚动和滚动触发动画。$('html, body').animate({scrollTop: n}) 平滑滚动。为性能考虑,节流滚动处理函数(它们在滚动时触发多次)。isVisible 辅助函数检查元素是否在视口内——用于懒加载图片或触发动画。现代 IntersectionObserver 在可见性检测上更高效;新项目可考虑使用。
// scroll to top smoothly
$('html, body').animate({ scrollTop: 0 }, 500)
// scroll to an element
const top = $('#section').offset().top
$('html, body').animate({ scrollTop: top }, 500)
// detect scroll position
$(window).on('scroll', function () {
const scrolled = $(window).scrollTop()
if (scrolled > 100) {
$('.nav').addClass('fixed')
} else {
$('.nav').removeClass('fixed')
}
})
// check if element is in viewport
function isVisible($el) {
const t = $el.offset().top
const h = $el.outerHeight()
const wTop = $(window).scrollTop()
const wH = $(window).height()
return t >= wTop && (t + h) <= (wTop + wH)
}AJAX
$.ajax(完全控制)
$.ajax 是底层、完全控制的方法。method/http 动词、data(GET 时作为查询字符串,POST 时作为请求体)、dataType 提示预期响应(自动解析)。success/error/complete 是旧版回调钩子。beforeSend 让你修改 xhr(添加请求头)。timeout 在 N 毫秒后中止。现代代码推荐使用 fetch() 或 $.ajax 配合 .done()/.fail() promise,而非 success/error 回调。
$.ajax({
url: '/api/users',
method: 'GET', // or 'POST', 'PUT', 'DELETE'
data: { page: 1, limit: 10 }, // query string or body
dataType: 'json', // expected response type
contentType: 'application/json',
timeout: 5000, // ms
headers: { 'X-Token': 'abc' },
beforeSend: function (xhr) { /* ... */ },
success: function (data, status, xhr) {
console.log('got', data)
},
error: function (xhr, status, error) {
console.error('failed', status, error)
},
complete: function (xhr, status) {
// runs on both success and error
}
})简写:$.get 与 $.post
$.get 和 $.post 是简单 GET/POST 请求的简写。promise 式接口(.done/.fail/.always)比 success/error 回调更清晰——且支持链式多个 .done 处理函数。对于 JSON 请求体,必须设置 contentType: 'application/json' 并 JSON.stringify 数据(jQuery 不会自动将对象序列化为 JSON)。注意:jQuery 的 promise 是 Deferred,不是原生 Promise,但大多数情况下工作方式类似。
// GET request
$.get('/api/users', { page: 1 }, function (data) {
console.log(data)
}, 'json')
// promise-based (preferred)
$.get('/api/users', { page: 1 })
.done(function (data) { console.log(data) })
.fail(function (xhr, status, err) { console.error(err) })
// POST request
$.post('/api/users', { name: 'Alice' })
.done(function (data) { /* created */ })
// POST JSON
$.ajax({
url: '/api/users',
method: 'POST',
contentType: 'application/json',
data: JSON.stringify({ name: 'Alice' })
})加载 HTML 到元素
.load() 是专门的简写方法:从 URL 获取 HTML 并注入到匹配元素中。'url #fragment' 语法只加载响应的一部分(匹配 #fragment 的部分)。.load() 非常适合局部页面更新而无需完全刷新——在遗留 SPA 中常见。默认是 GET,传数据时为 POST。回调接收 (responseText, status, xhr)。现代应用推荐 fetch + 手动 DOM 更新。
<!-- load HTML from server into element -->
<div id="content">Loading...</div>
<script>
$('#content').load('/partials/header.html')
// load a fragment (selector after URL space)
$('#content').load('/page.html #main')
// with callback (response, status, xhr)
$('#content').load('/page.html', function (resp, status, xhr) {
if (status === 'error') {
$('#content').html('Failed to load')
}
})
// POST with data
$('#content').load('/api', { id: 5 })
</script>getJSON 与 getScript
$.getJSON 是 $.ajax 的简写,dataType 为 'json'——自动解析响应。'?callback=?' 技巧为跨域请求启用 JSONP(遗留方案——现在用 CORS 代替)。$.getScript 获取并执行 JavaScript 文件——用于按需懒加载插件或分析脚本。两者都返回 jqXHR(promise 式)对象。如今的跨域请求,确保服务器发送 CORS 头;JSONP 是 CORS 出现前的变通方案,有安全注意事项。
// fetch and parse JSON
$.getJSON('/api/users', { page: 1 })
.done(function (data) {
console.log(data.users)
})
.fail(function (xhr, status, err) {
console.error('JSON fetch failed:', err)
})
// fetch and execute a script
$.getScript('/js/analytics.js')
.done(function (script, status) {
console.log('analytics loaded')
// script is now available
})
.fail(function () {
console.error('script failed')
})
// JSONP (cross-domain, legacy)
$.getJSON('https://api.example.com/data?callback=?')
.done(function (data) { /* ... */ })全局 AJAX 事件与设置
全局 AJAX 事件(ajaxStart、ajaxStop、ajaxComplete、ajaxError、ajaxSuccess)对所有 AJAX 请求触发——非常适合全局加载器和错误处理。$.ajaxSetup 设置应用于所有后续 AJAX 调用的默认值(URL 基础、请求头、超时)。.serialize() 将表单转为查询字符串;.serializeArray() 返回 {name, value} 对象数组——用于构建 JSON 或自定义载荷。状态更改请求务必包含 CSRF 令牌。
// global ajax event handlers (bind on document)
$(document)
.ajaxStart(function () { $('#loader').show() })
.ajaxStop(function () { $('#loader').hide() })
.ajaxError(function (e, xhr, settings, error) {
console.error('AJAX error:', settings.url, error)
})
// default settings for all AJAX
$.ajaxSetup({
baseURL: '/api',
timeout: 10000,
headers: { 'X-CSRF-Token': $('meta[name=csrf]').attr('content') }
})
// now $.get('/users') -> GET /api/users with the headers
$.get('/users').done(/* ... */)
// serialize a form for AJAX
const data = $('form').serialize() // "name=Alice&age=30"
const arr = $('form').serializeArray() // [{name, value}, ...]效果与动画
基础显示/隐藏
.show()/.hide()/.toggle() 改变 display。不带参数时瞬间显示/隐藏。带持续时间时,动画 width/height/opacity。'slow' = 600ms,'fast' = 200ms,或数值(毫秒)。回调在动画完成时触发。jQuery 动画默认使用 requestAnimationFrame(无卡顿)。注意:.hide() 设置 display:none;.show() 恢复原始 display 值。如需更多控制,直接使用 .animate()。
// instantly show/hide/toggle
$('.box').show()
$('.box').hide()
$('.box').toggle()
// animated (duration in ms or 'slow'/'fast')
$('.box').show(400) // fade in over 400ms
$('.box').hide('slow') // ~600ms
$('.box').toggle(300)
// with callback (runs when animation completes)
$('.box').hide(300, function () {
console.log('hidden')
})
// easing (default 'swing', also 'linear' with plugin)
$('.box').hide({ duration: 300, easing: 'linear' })淡入淡出
淡入淡出动画只改变 opacity(display 保持不变)。.fadeTo() 动画到特定 opacity——用于使元素变暗而不完全隐藏。动画按元素排队:链式 .fadeOut().fadeIn() 顺序执行,而非同时。要并行运行,使用 .animate() 处理多个属性或 queue 选项。对于通知等 opacity 驱动的 UI,淡入淡出比 show/hide 更平滑。
// fade in / out / toggle (opacity only)
$('.box').fadeIn(400)
$('.box').fadeOut(400)
$('.box').fadeToggle(400)
// fade to a specific opacity (0.0 - 1.0)
$('.box').fadeTo(400, 0.5) // 50% opacity
// with callback
$('.box').fadeOut(300, function () {
// runs after fade out completes
$(this).remove()
})
// sequence: fade out, then fade in
$('.box')
.fadeOut(300)
.fadeIn(300) // queued automatically滑动
滑动动画改变 height(和 display)。.slideDown 显示元素(从 display:none 到自然高度),.slideUp 隐藏元素(折叠到 0 然后 display:none),.slideToggle 切换状态。非常适合手风琴、下拉菜单和可折叠面板。结合 siblings() 实现手风琴行为(打开一个时关闭其他)。滑动是最常见的 jQuery 动画——配合适当的 ARIA 时平滑且无障碍友好。
// slide down (reveal) / up (hide) / toggle
$('.panel').slideDown(400)
$('.panel').slideUp(400)
$('.panel').slideToggle(400)
// accordion pattern
$('.accordion .header').on('click', function () {
$(this).next('.content').slideToggle(300)
.siblings('.content').slideUp(300)
})
// callback after slide completes
$('.panel').slideUp(300, function () {
console.log('panel closed')
})自定义 .animate()
.animate() 在持续时间内补间数值 CSS 属性。使用相对值('+=50')进行增量更改。属性名必须是 camelCase(marginLeft,不是 margin-left)。颜色需要 jQuery Color 插件或 jQuery UI——核心 jQuery 只动画数值。options 形式提供更多控制:queue:false 并行运行而非顺序。缓动默认 'swing'(ease-out);'linear' 为匀速。一次调用可动画多个属性。
// animate numeric CSS properties
$('.box').animate({
width: '+=50', // relative: add 50px
height: '300px',
opacity: 0.5,
marginLeft: '20px', // camelCase for hyphenated props
fontSize: '20px'
}, 800, 'swing', function () {
console.log('animation done')
})
// options object form
$('.box').animate({
left: 500
}, {
duration: 1000,
easing: 'linear',
complete: function () { /* done */ },
queue: false // run immediately, not queued
})
// NOTE: colors can't be animated without jQuery UI / color plugin停止与链式
.stop() 停止动画;(clearQueue, jumpToEnd) 提供控制。不用 stop,快速悬停会堆叠动画(元素在鼠标离开后长时间继续动画)——.stop(true) 在 .animate() 之前可防止此问题。.finish() 立即将所有排队动画跳到最终状态。.delay(ms) 在动画队列中插入暂停——用于排序。始终将悬停动画与 .stop() 配对以避免卡顿堆积。
// .stop(): stop current animation
$('.box').stop() // stop, leave at current state
$('.box').stop(true) // clear queue too
$('.box').stop(true, true) // clear queue AND jump to end
// prevent animation buildup on rapid hover
$('.box').hover(function () {
$(this).stop(true).animate({ width: 300 }, 200)
}, function () {
$(this).stop(true).animate({ width: 100 }, 200)
})
// .finish(): jump all queued animations to end state
$('.box').finish()
// .delay(): pause between queued animations
$('.box')
.fadeOut(300)
.delay(500) // wait 500ms
.fadeIn(300)遍历
向上移动(父元素)
向上遍历:.parent()(直接父元素),.parents()(直到 document 的所有祖先),.parents(selector) 过滤它们,.closest(selector) 向上查找并返回第一个匹配(包括自身——最常用于查找封闭组件)。.parentsUntil(selector) 返回直到但不包括匹配的祖先。.closest() 是事件委托模式的主力——给定点击的元素,找到其封闭的卡片/行/组件。
// direct parent
$('#item').parent()
// all ancestors (up to <html> or selector)
$('#item').parents() // all ancestors
$('#item').parents('.container') // first .container ancestor
// closest ancestor (incl. self) matching selector
$('#item').closest('.box') // very common - find enclosing box
// parentsUntil: ancestors up to (not including) match
$('#item').parentsUntil('.wrapper')
// offsetParent: nearest positioned ancestor
$('#item').offsetParent()向下移动(子元素)
.children() 只返回直接子元素(下一层);.find() 遍历所有层级。.find() 最常用——给定容器,查找匹配的后代(如查找表单内所有表单字段)。当只需直接子元素时 .children() 更快。.contents() 包括文本节点和注释节点(用于处理文本或 iframe)。两者都可用选择器过滤。始终用 .find() 限定遍历范围,避免扫描整个文档。
// direct children (optionally filtered)
$('#list').children() // all direct children
$('#list').children('.item') // direct children with class .item
// all descendants (filtered)
$('#list').find('li') // all <li> anywhere inside
$('#list').find('.active') // all .active descendants
// contents: children including text/comment nodes
$('#box').contents()
// first/last child
$('#list').children().first()
$('#list').children().last()水平移动(兄弟元素)
兄弟遍历:.siblings()(所有)、.next()/.prev()(相邻)、.nextAll()/.prevAll()(一个方向的所有)、.nextUntil()/.prevUntil()(直到匹配)。可用选择器过滤。经典的活动标签模式($(this).addClass('active').siblings().removeClass('active'))使用 siblings 重置兄弟状态。兄弟共享同一父元素——如果元素嵌套方式不同,则不是兄弟。
// all siblings (excluding self)
$('#item').siblings()
$('#item').siblings('.active') // siblings with class .active
// next/prev: immediate sibling
$('#item').next()
$('#item').prev()
// nextAll/prevAll: all following/preceding siblings
$('#item').nextAll()
$('#item').prevAll('.item')
// nextUntil/prevUntil: siblings up to a match
$('#item').nextUntil('.stop')
// common: highlight active tab
$('.tab').on('click', function () {
$(this).addClass('active').siblings().removeClass('active')
})过滤选择集
过滤在不重新查询 DOM 的情况下缩小当前集合。.filter(selector) 保留匹配项;.not(selector) 是反向操作。.has(selector) 保留包含匹配后代的元素。.eq(n) 缩减到一个元素(负 n 从末尾计数)。.first()/.last() 是便捷方法。.slice(start, end) 取范围。在遍历后用这些方法细化选择,而非编写复杂选择器——通常更清晰且更快。
// .filter: keep matching
$('li').filter('.active')
$('li').filter(function (i) { return i % 2 === 0 }) // even index
// .not: remove matching (inverse of filter)
$('li').not('.active')
$('li').not(':first')
// .has: keep elements that contain a descendant
$('div').has('p') // divs that contain <p>
// .eq: reduce to one element by index
$('li').eq(2) // 3rd <li> (0-indexed)
$('li').eq(-1) // last <li>
// .first / .last
$('li').first()
$('li').last()
// .slice: range
$('li').slice(2, 5) // indices 2, 3, 4使用 .end() 与 .addBack() 链式
.end() 弹出遍历栈,返回上一个集合——对于编写遍历后继续操作原始元素的可读链至关重要。.addBack()(原 .andSelf)将当前集合与上一个集合合并,使原始元素参与后续操作。这些让你构建强大的单行链:查找子元素、修改它们、end() 回父元素、修改父元素。缩进链使遍历结构清晰。
// .end(): pop back to previous set after a traversal
$('ul') // [ul]
.find('li') // [li, li, li]
.addClass('item')
.end() // back to [ul]
.addClass('has-items')
// .addBack(): include previous set in current
$('ul')
.children('li') // [li, li]
.addClass('item')
.addBack() // [li, li, ul]
.addClass('related')
// without .addBack, the ul would not get .related
// .andSelf (old name) is deprecated, use .addBack工具函数
迭代:$.each 与 $.map
$.each 迭代数组(index, value)或对象(key, value)——注意参数顺序与 Array.forEach 相反。return false 跳出。$.map 将每项转换为 newArray(return null 跳过)。$.grep 过滤数组。这些是 jQuery 的 ES5 前迭代工具;现代代码常用原生 Array 方法(forEach、map、filter)。使用 $.each 的主要原因是迭代普通对象(现代 JS 中用 Object.entries)。
// $.each: iterate arrays or objects (returns the collection)
$.each([10, 20, 30], function (index, value) {
console.log(index, value) // 0 10, 1 20, 2 30
if (value === 20) return false // break
})
$.each({ a: 1, b: 2 }, function (key, value) {
console.log(key, value) // 'a' 1, 'b' 2
})
// $.map: transform to a new array
const doubled = $.map([1, 2, 3], function (value, index) {
return value * 2 // [2, 4, 6]
})
// return null/undefined to skip
const evens = $.map([1, 2, 3, 4], function (v) {
return v % 2 === 0 ? v : null // [2, 4]
})
// $.grep: filter an array
const evens = $.grep([1, 2, 3, 4], function (v) { return v % 2 === 0 })
// [2, 4]类型检查
$.type 返回小写类型字符串(比 typeof 更细粒度)。$.isArray、$.isFunction、$.isPlainObject、$.isEmptyObject、$.isWindow、$.isNumeric 是便捷布尔方法。$.isPlainObject 区分普通 {} 与 new Date 或 window 等实例。$.isNumeric 对有限数字和数字字符串返回 true(新版 jQuery 不含 '0x1F' 等十六进制)。现代 JS 中 Array.isArray 和 typeof 覆盖大部分需求,但 $.isPlainObject 对深度扩展安全仍很实用。
// $.type: detailed type
$.type([]) // 'array'
$.type({}) // 'object'
$.type('hi') // 'string'
$.type(null) // 'null'
$.type(undefined) // 'undefined'
$.type(/regex/) // 'regexp'
// specific checks (all return boolean)
$.isArray([]) // true
$.isFunction(fn => {}) // true
$.isEmptyObject({}) // true
$.isPlainObject({}) // true (created by {} or new Object)
$.isPlainObject([]) // false
$.isWindow(window) // true
$.isNumeric('42') // true (also '0x1F')
$.isNumeric('abc') // false
// $.isEmptyObject: no own enumerable props
$.isEmptyObject({ a: 1 }) // false对象与数组工具
$.extend 合并对象——后面的属性覆盖前面的。第一个参数传 true 进行深度(递归)合并;否则为浅合并。始终传 {} 作为第一个参数以避免修改源对象。$.merge 连接数组(修改第一个)。$.inArray 是 indexOf(未找到返回 -1)。$.makeArray 将类数组对象(NodeList、arguments)转为真正数组——现代代码用 Array.from 或展开 [...nl]。
// $.extend: merge objects (mutates first unless deep copy flag)
const defaults = { a: 1, b: 2 }
const options = { b: 3, c: 4 }
const merged = $.extend({}, defaults, options)
// { a: 1, b: 3, c: 4 } (later overrides earlier)
// deep merge (recurses into nested objects)
const deep = $.extend(true, {},
{ nested: { x: 1, y: 2 } },
{ nested: { y: 3, z: 4 } }
)
// { nested: { x: 1, y: 3, z: 4 } }
// $.merge: combine two arrays (mutates first)
const combined = $.merge([1, 2], [3, 4]) // [1, 2, 3, 4]
// $.inArray: find index (-1 if not found)
const idx = $.inArray(2, [1, 2, 3]) // 1
// $.makeArray: convert array-like to true array
const arr = $.makeArray(document.querySelectorAll('li'))字符串与数据工具
$.trim 是去空白(现代用 str.trim())。$.parseHTML 将 HTML 字符串转为 DOM 节点数组——比 $.fn.html 处理不可信输入更安全(避免脚本执行)。$.param 将对象序列化为查询字符串(parse 的逆操作)。$.now 是 Date.now()。$.unique 去重 DOM 元素数组(现在很少需要)。这些大多有原生现代等价物;最有用的剩余是 $.param(用于 AJAX 数据)和 $.parseHTML。
// $.trim: remove leading/trailing whitespace
$.trim(' hello ') // 'hello' (use str.trim() in modern code)
// $.parseHTML: parse string to DOM nodes
const nodes = $.parseHTML('<div><p>Hi</p></div>')
// [div] — array of DOM nodes
// $.parseJSON: parse JSON string (use JSON.parse instead)
const obj = $.parseJSON('{"a":1}')
// $.param: serialize object to query string
$.param({ name: 'Alice', age: 30 })
// 'name=Alice&age=30'
$.param({ tags: ['a', 'b'] })
// 'tags%5B%5D=a&tags%5B%5D=b'
// $.now: current timestamp (Date.now equivalent)
$.now() // 1700000000000
// $.unique: remove duplicates from DOM element array
$.unique($('.box').get())数据存储($.data)
$.data / .data() 在内部缓存中存储与元素关联的任意值(DOM 中不可见,比属性访问快)。.data() 还自动读取 data-* 属性(带类型转换:数字、布尔值、JSON)。重要:.data(name, value) 写入缓存,不写入 data-* 属性——用 .attr('data-name', val) 更新属性本身。.removeData 清除缓存条目。元素 .remove() 时缓存自动清理,防止泄漏。
// store data on an element (private, won't appear in DOM)
$.data(document.getElementById('box'), 'key', { count: 1 })
// read it back
const v = $.data(el, 'key') // { count: 1 }
//.data() method form (more common)
$('.box').data('key', { count: 1 })
$('.box').data('key') // { count: 1 }
// auto-parses data-* attributes
// <div class="box" data-id="42" data-name="alice"></div>
$('.box').data('id') // 42 (auto-cast to number)
$('.box').data('name') // 'alice'
// remove data
$('.box').removeData('key')
// note: .data() does NOT write back to data-* attrs
// use .attr('data-key', val) to update the attribute插件与扩展
编写插件
插件扩展 $.fn(jQuery 的原型),因此可在集合上调用。始终返回 this(或 this 上方法的返回值)以支持链式调用。插件内部,'this' 是 jQuery 集合;.each() 内部,'this' 是原始 DOM 元素。用 IIFE 包装以通过 jQuery 安全别名 $。$.extend 默认值模式是接受带合理默认值选项的标准方式。通过 npm 或独立脚本发布插件。
// basic plugin pattern
(function ($) {
$.fn.highlight = function (color) {
// 'this' is the jQuery collection
return this.css('background', color || 'yellow')
}
}(jQuery))
// usage
$('.box').highlight('red')
$('.box').highlight() // default 'yellow'
// options pattern
$.fn.tooltip = function (options) {
const settings = $.extend({
position: 'top',
delay: 200
}, options)
return this.each(function () {
// 'this' is a DOM element here
// ... tooltip logic ...
})
}带方法的插件
样板插件模式使用 methods 对象,按第一个参数分派:$('.c').counter({ start: 5 }) 初始化,$('.c').counter('increment') 调用方法。状态存储在 $.data 中。这种模式在 Vue/React 之前的时代很流行,用于滑块、模态框、日期选择器等组件。如今,框架(Vue/React)通常是状态 UI 的更好选择;但对于增强遗留 jQuery 站点,此模式仍然有用且广为理解。
// stateful plugin using data + methods
(function ($) {
$.fn.counter = function (method) {
const methods = {
init: function (options) {
return this.each(function () {
const $el = $(this)
$el.data('count', options?.start || 0)
$el.text($el.data('count'))
})
},
increment: function () {
return this.each(function () {
const $el = $(this)
const c = $el.data('count') + 1
$el.data('count', c).text(c)
})
},
reset: function () {
return this.each(function () {
$(this).data('count', 0).text(0)
})
}
}
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1))
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments)
} else {
$.error('Method ' + method + ' does not exist')
}
}
}(jQuery))使用流行插件
jQuery UI 是官方配套库,提供小部件(datepicker、accordion、autocomplete、dialog)和交互(draggable、droppable、sortable)。其他流行插件:DataTables(功能丰富的表格)、Select2(增强选择框)、Slick(轮播)、Magnific Popup(模态框)。在 jQuery 之后引入插件 CSS 和 JS。在 DOM ready 时初始化。许多插件支持方法 API 如 $('#x').plugin('method', args) 和通过 plugin('option', name, val) 更新选项。
<!-- jQuery UI (interactions, widgets, effects) -->
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
<script>
// datepicker
$('#date').datepicker({ dateFormat: 'yy-mm-dd' })
// draggable / droppable / sortable
$('.item').draggable()
$('.list').sortable()
// dialog
$('<div>Hello</div>').dialog({ modal: true })
</script>
<!-- Other common plugins -->
<!-- DataTables: tables with sorting, search, pagination -->
<!-- Select2: enhanced <select> with search -->
<!-- Slick: carousel/slider -->
<!-- Magnific Popup: lightbox/modal -->小部件工厂(jQuery UI)
jQuery UI 小部件工厂($.widget)是构建有状态、支持继承的插件的结构化方式。它提供 _create(构造函数)、_destroy(清理)、_setOption、_on(自动清理事件绑定)、_trigger(自定义事件)。选项通过方法 API 暴露:$el.widget('option', name, value)。自定义事件以 widgetname + eventname 形式触发。对于 jQuery 中的复杂状态 UI,小部件工厂是最健壮的模式——但新代码可考虑迁移到 Vue/React。
// create a reusable widget with state + inheritance
$.widget('my.counter', {
options: { start: 0 },
_create: function () {
this.count = this.options.start
this._refresh()
this._on(this.element, { click: 'increment' })
},
increment: function () {
this.count++
this._refresh()
this._trigger('changed', null, { count: this.count })
},
_refresh: function () {
this.element.text(this.count)
},
_destroy: function () {
this.element.text('')
}
})
// usage
$('#el').counter({ start: 5 }) // init
$('#el').counter('increment') // call method
$('#el').on('counterchanged', function (e, data) {
console.log('count is', data.count)
})
$('#el').counter('destroy') // cleanup插件最佳实践
插件最佳实践:(1) 始终返回 this 以支持链式;(2) 用 IIFE 包装确保 $ 安全;(3) 使用 .each() 使插件支持多元素选择;(4) 命名空间事件(click.myplugin)让用户能干净地解绑而不影响其他;(5) 用 $.extend 合并选项并暴露默认值供用户全局覆盖;(6) destroy 时清理(事件、数据、DOM 修改);(7) 命名空间化插件名避免冲突;(8) 暴露公共方法和默认值以提供灵活性。
// 1. always return this for chainability
$.fn.green = function () { return this.css('color', 'green') }
// 2. use IIFE for $ safety
(function ($) { /* plugin */ })(jQuery)
// 3. iterate with .each() for multi-element support
$.fn.x = function () { return this.each(function () { /* per element */ }) }
// 4. namespace events so users can unbind cleanly
this._on(this.element, { 'click.myplugin': 'handler' })
// 5. accept options via $.extend
const opts = $.extend({}, $.fn.x.defaults, options)
$.fn.x.defaults = { color: 'red' }
// 6. clean up on destroy (events, data, DOM)
// 7. namespace your plugin name to avoid collisions
// 8. provide public methods + defaults accessible for override属性与特性
属性 Getter 与 Setter
.attr() 读写 HTML 属性(标记中的值,始终为字符串)。.prop() 读写 DOM 属性(当前状态——如复选框的 checked 属性在用户交互后可能与 checked 属性不同)。布尔属性(checked、disabled、selected)用 .prop(),其他(href、src、alt、data-*)用 .attr()。.data() 读取 data-* 但将值存储在内部缓存中并保留类型——如需更新 DOM 属性本身,使用 .attr('data-*')。
// .attr: HTML attribute (what's in the markup)
$('img').attr('src') // get
$('img').attr('src', 'new.jpg') // set
$('img').attr({ // set multiple
src: 'new.jpg',
alt: 'Photo',
width: 200
})
// .removeAttr: remove attribute entirely
$('img').removeAttr('alt')
// .prop: DOM property (current state, may differ from attr)
$('input').prop('checked') // true/false (current state)
$('input').prop('checked', true) // check it
$('input').prop('disabled', true)
// data-* attributes
$('div').attr('data-id', 42) // sets the attribute (string)
$('div').data('id', 42) // stores in cache (typed)数据属性
.data(name) 读取 data-* 属性并自动类型转换(数字、布尔值、JSON)。通过 .data(name, value) 设置写入内部缓存,不写入属性——因此 .attr('data-name') 仍返回原值。要更新实际 DOM 属性(如为了 CSS 选择器或服务器端读取),使用 .attr('data-name', value)。混淆此区别会导致 bug。.removeData 清除缓存。驼峰命名:data-foo-bar 变为 .data('fooBar')。
<!-- HTML -->
<div class="box" data-id="42" data-name="alice" data-active="true" data-config='{"x":1}'></div>
<script>
// .data() reads data-* with auto type conversion
$('.box').data('id') // 42 (number, not string)
$('.box').data('name') // 'alice'
$('.box').data('active') // true (boolean)
$('.box').data('config') // { x: 1 } (parsed JSON)
// set data (stored in cache, NOT the attribute)
$('.box').data('id', 99)
$('.box').data('id') // 99
$('.box').attr('data-id') // still '42' (attribute unchanged)
// to update the attribute itself, use .attr
$('.box').attr('data-id', 99)
// remove data
$('.box').removeData('id')
</script>表单属性
表单状态使用 .prop()(布尔 DOM 属性)——.prop('checked') 反映当前状态,而 .attr('checked') 返回初始 HTML 属性(用户交互时不更新)。.val() 统一获取/设置表单值。单选按钮找到 :checked 的那个并读 .val()。多选时 .val() 返回数组。.is(':checked') 是简洁的布尔检查。这些区别对表单验证和序列化很重要。
// checkbox state
$('input[type=checkbox]').prop('checked') // true/false
$('input[type=checkbox]').prop('checked', true)
$('input[type=checkbox]').is(':checked') // boolean
// disabled / readonly
$('input').prop('disabled', true)
$('input').prop('readOnly', true) // note: camelCase
// select value
$('select').val() // selected value
$('select').val('option2') // set selected
$('select option:selected').text() // selected text
// multiple select
$('select[multiple]').val() // ['a', 'b'] array
// radio: value of checked
$('input[name=gender]:checked').val()
//.prop vs .attr for checkboxes:
// .prop('checked') = current state (changes when clicked)
// .attr('checked') = initial HTML value (doesn't change)类与属性
三种向 CSS 和 JS 暴露元素状态的方式:类(广为人知,.hasClass)、data-* 属性(可查询,携带值)、ARIA 属性(屏幕阅读器可访问)。视觉状态用类最简单。多值状态(data-state='loading|success|error')用 data-* 更清晰。无障碍用 aria-*(aria-pressed、aria-expanded、aria-busy)——它们常与视觉状态重叠,所以配对使用。现代框架使这更容易,但在 jQuery 中要有意识地选择。
// adding behavior tags via class
$('.btn').addClass('loading')
// CSS: .btn.loading { opacity: 0.5; pointer-events: none; }
// via data attribute (cleaner for state)
$('.btn').attr('data-state', 'loading')
// CSS: .btn[data-state="loading"] { ... }
// via aria (accessible)
$('.btn').attr('aria-busy', 'true')
// choosing:
// - class: many CSS hooks, well-known
// - data-*: cleaner for state values, queryable
// - aria-*: exposes state to screen readers
// toggling example
$('.btn').on('click', function () {
const $b = $(this)
$b.attr('aria-pressed', $b.attr('aria-pressed') !== 'true')
})值(表单字段)
.val() 是表单值的通用 getter/setter——文本输入、文本域、选择框,以及(单选/复选框的)选中项的值。.serialize() 生成 URL 编码字符串;.serializeArray() 给出 {name, value} 数组——用 .serializeArray().reduce((o, p) => (o[p.name] = p.value, o), {}) 转为普通对象。重置表单时,调用原始 DOM 元素的原生 .reset() 方法(jQuery 没有 .reset() 方法)。
// text input
$('input[type=text]').val() // get current value
$('input[type=text]').val('new value') // set
// textarea
$('textarea').val()
// select
$('select').val() // selected value
$('select').val('opt2') // set
$('select').val(['a', 'b']) // multi-select set
// checkbox (use prop for state)
$('input[type=checkbox]').prop('checked')
// radio
$('input[type=radio][name=g]:checked').val()
// serialize whole form
$('form').serialize() // 'name=Alice&age=30'
$('form').serializeArray() // [{name, value}]
// reset form
$('form')[0].reset()尺寸与位置
宽度与高度方法
宽/高方法返回数值(px),不像 .css('width') 返回带单位的字符串。.width() = 内容框(CSS width)。.innerWidth() = 内容 + padding。.outerWidth() = 内容 + padding + border。.outerWidth(true) 包含 margin。对于 window/document,$(window).width() 是视口(可见区域);$(document).height() 是整个页面高度(滚动时可能大于视口)。Setter 接受数值(px)或字符串('50%'、'10em')。
// content box (no padding, border, margin)
$('.box').width() // 200 (number, px)
$('.box').width(300) // set to 300px
// inner (content + padding)
$('.box').innerWidth()
$('.box').innerHeight()
// outer (content + padding + border)
$('.box').outerWidth()
$('.box').outerHeight()
// outer + margin
$('.box').outerWidth(true)
$('.box').outerHeight(true)
// window & document
$(window).width() // viewport width
$(window).height() // viewport height
$(document).height() // document height (may exceed viewport)position 与 offset
.offset() 是相对于文档的(绝对坐标——用于拖放、鼠标位置的工具提示)。.position() 是相对于 offset parent(最近的 position:relative/absolute/fixed 祖先)——用于在容器内重新定位。.offsetParent() 查找该祖先。.scrollTop()/.scrollLeft() 获取或设置滚动位置——$(window).scrollTop() 是页面滚动。offset(文档)与 position(父元素)的区别对正确的定位计算至关重要。
// .offset(): relative to document (top-left of page)
const o = $('.box').offset()
console.log(o.left, o.top) // e.g., 100, 200
$('.box').offset({ top: 0, left: 0 }) // move to top-left of page
// .position(): relative to offset parent
const p = $('.box').position()
console.log(p.left, p.top) // position within parent
// .scrollTop() / .scrollLeft()
$(window).scrollTop() // how far page is scrolled down
$(window).scrollTop(0) // scroll to top
$('.container').scrollTop(100) // scroll inner container
// .offsetParent(): nearest positioned ancestor
$('.box').offsetParent() // the element .position() is relative to响应式布局辅助
resize 在拖动期间触发多次——用 setTimeout 防抖以避免性能问题。添加 body 类(mobile/desktop)让 CSS 通过 body.mobile .nav { ... } 响应。现代响应式代码中,样式优先用 CSS 媒体查询,JS 中用 window.matchMedia()——性能更好且与 CSS 断点一致。jQuery 的 resize 方式适用于遗留代码,但 CSS + matchMedia 是现代标准。
// check viewport width on resize
$(window).on('resize', function () {
const w = $(window).width()
if (w < 768) {
$('body').addClass('mobile').removeClass('desktop')
} else {
$('body').addClass('desktop').removeClass('mobile')
}
}).resize() // trigger once to initialize
// debounced resize (avoid firing hundreds of times)
let timer
$(window).on('resize', function () {
clearTimeout(timer)
timer = setTimeout(function () {
console.log('resize settled')
}, 200)
})
// match media (preferred for responsive checks)
if (window.matchMedia('(max-width: 768px)').matches) {
// mobile
}元素可见性检测
:visible/:hidden 检查 display、visibility、opacity 和尺寸——jQuery 3+ 将 visibility:hidden 和 opacity:0 视为 hidden(旧版本视为 visible)。视口检测时,inViewport 辅助函数将元素边界与滚动位置比较。现代代码使用 IntersectionObserver——效率更高(浏览器做计算,仅在变化时回调)且不需要滚动监听器。jQuery 方式可行但在每次滚动事件时触发。
// :visible / :hidden pseudo-classes
$('.box').is(':visible') // boolean
$('.box:visible').doSomething()
// note: visibility:hidden is now :hidden in jQuery 3+
// (display:none, opacity:0, width:0/height:0 are also :hidden)
// is element in viewport?
function inViewport($el) {
const elTop = $el.offset().top
const elBottom = elTop + $el.outerHeight()
const docTop = $(window).scrollTop()
const docBottom = docTop + $(window).height()
return elBottom >= docTop && elTop <= docBottom
}
// scroll-triggered reveal
$(window).on('scroll', function () {
$('.reveal').each(function () {
if (inViewport($(this))) {
$(this).addClass('shown')
}
})
})滚动位置与动画
平滑滚动:在 html 和 body 上同时动画 scrollTop(跨浏览器)。减去头部高度以适应固定导航。无限滚动:当滚动位置 + 视口高度在文档高度 N 像素内时,加载更多内容。视差:根据滚动位置 translate 元素。始终节流滚动处理函数(它们每秒触发数十次)——使用 _.throttle、基于定时器的防抖或 requestAnimationFrame。现代替代:CSS scroll-behavior:smooth 用于滚动,IntersectionObserver 用于触发。
// current scroll position
const scrollTop = $(window).scrollTop()
// smooth scroll to top
$('html, body').animate({ scrollTop: 0 }, 500)
// smooth scroll to element
function scrollTo($el, duration) {
const top = $el.offset().top - 20 // 20px offset for fixed header
$('html, body').animate({ scrollTop: top }, duration || 500)
}
scrollTo($('#section2'))
// infinite scroll: load more when near bottom
$(window).on('scroll', function () {
if ($(window).scrollTop() + $(window).height() >= $(document).height() - 100) {
loadMore()
}
})
// parallax (simple)
$(window).on('scroll', function () {
const y = $(window).scrollTop()
$('.hero').css('transform', 'translateY(' + (y * 0.5) + 'px)')
})Deferred 与 Promise
创建 Deferred
$.Deferred 是 jQuery 的 promise 实现(早于原生 Promise)。创建一个,然后调用 .resolve(value) 或 .reject(reason) 来结算它。.promise() 返回只读视图(消费者无法 resolve)。旧版 API 使用 .done/.fail/.always 而非 .then/.catch。jQuery 3+ 的 Deferred 是 thenable,所以 await 和 Promise.resolve 可与之配合。新代码优先使用原生 Promise——Deferred 主要与 AJAX 和旧版 API 相关。
// create a Deferred (jQuery's promise source)
const deferred = $.Deferred()
// resolve or reject it later
setTimeout(function () {
if (Math.random() > 0.5) {
deferred.resolve('success data')
} else {
deferred.reject(new Error('failed'))
}
}, 1000)
// get the promise (read-only view)
const promise = deferred.promise()
// consume it
promise
.done(function (data) { console.log('done:', data) })
.fail(function (err) { console.log('fail:', err) })
.always(function () { console.log('settled') })when(并行异步)
$.when(deferreds...) 等待多个 Deferred/Promise 全部 resolve,然后用每个结果的数组调用 .done。任何一个 reject 则 .fail 立即触发。这是并行异步模式(类似 Promise.all)。回调为每个输入 deferred 接收一个数组(AJAX 时每个含 [data, status, xhr])。现代代码中,Promise.all + fetch 更清晰且直接返回已解析值。$.when 在 jQuery 重的代码库中仍有用。
// run multiple async ops in parallel, wait for all
$.when(
$.get('/api/users'),
$.get('/api/posts'),
$.get('/api/comments')
).done(function (usersResp, postsResp, commentsResp) {
// each arg is [data, status, xhr]
const users = usersResp[0]
const posts = postsResp[0]
console.log('all loaded:', users, posts)
}).fail(function (err) {
console.error('one failed:', err)
})
// modern equivalent
Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json())
]).then(([users, posts]) => {
console.log('all loaded:', users, posts)
})链式 .then()
jQuery 3+ 将 .then 与 Promises/A+ 规范对齐——它接受 (onFulfilled, onRejected) 并返回新 promise,就像原生 Promise.then。可以链式异步操作并转换值。从 .then 返回 promise 会等待它;返回值则向下游传递。3+ 也支持 .catch。jQuery 1.x/2.x 的 .then 行为非标准——使用 .pipe 代替。如可能,升级到 3+ 并使用 .then/.catch 获得更清晰的异步代码。
// jQuery 3+: .then follows Promises/A+ (like native then)
$.get('/api/users')
.then(function (users) {
// transform or chain another async
return $.get('/api/users/' + users[0].id)
})
.then(function (userDetail) {
console.log('detail:', userDetail)
return userDetail.name // pass value downstream
})
.then(function (name) {
console.log('name:', name)
})
.catch(function (err) {
console.error('any error in chain:', err)
})
// in jQuery 1.x/2.x, .then had different semantics
// use .done/.pipe for old code; .then for newAJAX Promise 接口
所有 $.ajax 调用(及 $.get/$.post)返回 jqXHR 对象,它是 promise 式的(支持 .done/.fail/.always)且(jQuery 3+)thenable(所以 await 和 Promise.resolve 可用)。可附加多个 .done 回调——它们都在成功时触发。.abort() 取消请求(触发 status 为 'abort' 的 .fail)。新代码中,await $.ajax(...) 或用 Promise.resolve() 包装以与原生 Promise 链和 async/await 干净集成。
// $.ajax returns a jqXHR (promise-like)
const req = $.ajax({ url: '/api/data', method: 'GET' })
// multiple callbacks on same request
req.done(function (data) { console.log('handler 1', data) })
req.done(function (data) { console.log('handler 2', data) })
req.fail(function (xhr, status, err) { console.error(err) })
req.always(function () { console.log('settled') })
// abort an in-flight request
req.abort()
// convert to native Promise (jQuery 3+)
Promise.resolve(req).then(function (data) {
console.log('via native promise:', data)
})
// use with async/await (jQuery 3+)
async function getData() {
try {
const data = await $.ajax({ url: '/api/data' })
console.log(data)
} catch (err) {
console.error(err)
}
}自定义 Promise 管道
将任何异步操作包装在 Deferred 中以获得 promise:创建、稍后 resolve/reject、返回 .promise()。jQuery 集合还暴露 .promise(),当元素上所有排队动画完成时 resolve——用于在一批动画后触发代码。这将 jQuery 的动画队列与基于 promise 的代码桥接,让你可以 await 动画或将其与 AJAX 或其他异步工作干净地链式。
// wrap a callback-based API in a promise
function delay(ms) {
const d = $.Deferred()
setTimeout(function () { d.resolve() }, ms)
return d.promise()
}
// use it
delay(500).then(function () { console.log('500ms passed') })
// animate then continue
$('.box').fadeOut(300).promise().done(function () {
console.log('fade done')
})
// queue of animations on multiple elements
$('.box').each(function (i) {
$(this).delay(i * 100).fadeIn().promise()
})
// when animations on a set are done
$('.box').fadeOut().promise().done(function () {
console.log('all boxes faded')
})表单
表单事件
submit 在表单提交时触发——AJAX 时始终 preventDefault。input 在每次按键(文本)或值改变时触发——适合实时验证或即搜即输。change 对文本输入在 blur 时触发,但对复选框、单选和选择框立即触发。focus/blur 在字段获焦时触发。对动态添加的字段在表单上使用事件委托。'change' 与 'input' 的区别很重要:'input' 是实时的,'change' 是用户完成编辑时。
// submit event
$('form').on('submit', function (e) {
e.preventDefault() // stop page reload
const data = $(this).serialize()
$.post('/api/save', data)
})
// input event (fires on every keystroke/change)
$('input').on('input', function () {
console.log('current value:', $(this).val())
})
// change event (fires on blur for text, immediately for checkbox/select)
$('select').on('change', function () {
console.log('selected:', $(this).val())
})
// focus / blur
$('input').on('focus', function () { $(this).addClass('focused') })
$('input').on('blur', function () { $(this).removeClass('focused') })
// delegated (works for dynamically added fields)
$('form').on('input', '.field', function () { /* ... */ })序列化表单数据
.serialize() 生成 URL 编码字符串,可直接用于 AJAX data。.serializeArray() 给出 {name, value} 数组——用 reduce 转为普通对象。两者都尊重表单当前状态(复选框仅在选中时包含;选择框使用选中的选项)。复选框值默认为 'on',除非设置 value='something'。禁用字段被排除。这是为 AJAX 提交收集表单数据而无需完全刷新的标准方式。
<!-- form -->
<form id="f">
<input name="name" value="Alice">
<input name="email" value="[email protected]">
<select name="role"><option value="admin" selected>Admin</option></select>
<input type="checkbox" name="agree" checked>
</form>
<script>
// serialize: URL-encoded string
$('#f').serialize()
// 'name=Alice&email=a%40b.com&role=admin&agree=on'
// serializeArray: array of {name, value}
$('#f').serializeArray()
// [{ name: 'name', value: 'Alice' }, ...]
// convert to plain object
const obj = {}
$('#f').serializeArray().forEach(p => { obj[p.name] = p.value })
// { name: 'Alice', email: '[email protected]', role: 'admin', agree: 'on' }
// or one-liner reduce
const obj2 = $('#f').serializeArray().reduce((o, p) =>
(o[p.name] = p.value, o), {})
</script>表单验证
手动验证:遍历必填字段,检查 .val(),切换 'invalid' 类以应用 CSS 样式。邮箱正则是基本合理性检查(完整 RFC 5322 正则太复杂)。复杂表单可使用 jQuery Validation Plugin,它提供声明式规则(class='required email')和一致的错误消息。始终客户端验证为 UX,服务端验证为安全——客户端验证可被绕过。HTML5 属性(required、type='email'、pattern)是现代基线。
// simple validation
function validate($form) {
let valid = true
$form.find('[data-required]').each(function () {
const $f = $(this)
if (!$f.val()) {
$f.addClass('invalid')
valid = false
} else {
$f.removeClass('invalid')
}
})
$form.find('[type=email]').each(function () {
if (!/^[^@]+@[^@]+\.[^@]+$/.test($(this).val())) {
$(this).addClass('invalid')
valid = false
}
})
return valid
}
$('form').on('submit', function (e) {
e.preventDefault()
if (validate($(this))) {
$.post('/api/save', $(this).serialize())
}
})动态表单字段
通过 $() 创建元素并 append 来动态添加/移除表单字段。使用数组式名称(tags[])让服务器接收数组。事件委托(on('click', '.remove', ...))处理动态添加的移除按钮点击。对于可重排字段(带上/下按钮或拖放),每次更改后重新编号名称让服务器看到干净序列。此模式常用于标签输入、动态行项目和配置表单。
// add a new field
$('.add-field').on('click', function () {
const $field = $('<input>', {
type: 'text',
name: 'tags[]',
class: 'tag-input',
placeholder: 'Tag'
})
$('#fields').append($field)
})
// remove a field
$('#fields').on('click', '.remove', function () {
$(this).closest('.field-row').remove()
})
// array field names (tags[]) — server receives an array
// PHP/Rails auto-parse 'name[]' into arrays
// re-number fields after add/remove
function renumber() {
$('#fields .field').each(function (i) {
$(this).find('input').attr('name', 'item_' + i)
})
}AJAX 表单提交
可复用的 AJAX 表单处理:序列化表单、POST 到 action URL、处理成功/错误。请求期间禁用提交按钮以防止重复提交。触发自定义事件(ajax:success、ajax:error)让其他代码反应。成功时重置表单。通过将字段名映射到错误消息来显示服务器验证错误。此模式通过添加 class='ajax' 将任何表单转为 AJAX 表单——渐进增强的常见做法。
// generic AJAX form submission
$('form.ajax').on('submit', function (e) {
e.preventDefault()
const $form = $(this)
const data = $form.serialize()
const url = $form.attr('action') || window.location.href
const method = $form.attr('method') || 'POST'
// show loading state
$form.find('button').prop('disabled', true).addClass('loading')
$.ajax({
url: url,
method: method,
data: data,
dataType: 'json'
})
.done(function (resp) {
$form.trigger('ajax:success', [resp])
$form[0].reset()
})
.fail(function (xhr) {
$form.trigger('ajax:error', [xhr])
const errors = xhr.responseJSON?.errors
if (errors) showErrors($form, errors)
})
.always(function () {
$form.find('button').prop('disabled', false).removeClass('loading')
})
})迭代与循环
$.each 与 .each()
$.each 是通用迭代器,可迭代数组(index, value)或普通对象(key, value)。.each() 是 jQuery 集合上的方法——'this' 是原始 DOM 元素。两者都接受 return false 跳出,其他继续。注意参数顺序是 (index, value)——与 Array.forEach 的 (value, index) 相反。现代代码优先使用原生 forEach/map/for...of;.each() 在处理 jQuery 集合且需要元素作为 'this' 时仍有用。
// $.each: generic iterator (arrays AND objects)
$.each([10, 20, 30], function (index, value) {
console.log(index, value) // 0 10, 1 20, 2 30
})
$.each({ a: 1, b: 2 }, function (key, value) {
console.log(key, value) // 'a' 1, 'b' 2
})
// .each(): method on jQuery collections only
$('li').each(function (index, element) {
// 'this' is the DOM element (= element)
// wrap with $(this) for jQuery methods
$(this).text(index + ': ' + $(this).text())
})
// breaking out
$('li').each(function (i) {
if (i === 3) return false // stop iteration
// return true (or nothing) to continue
})$.map 与 .map()
$.map 将数组/对象转换为新数组(return null 跳过,return 数组则展开)。注意参数顺序是 (value, index)——与 $.each 相反。.map() 在 jQuery 集合上返回新 jQuery 集合(调用 .get() 转为普通数组)。从 .map() 返回 null/undefined 排除该项。这些早于原生 Array.map;现代代码使用原生 map,但 jQuery 的 .map() 在从 DOM 集合提取值时仍然方便。
// $.map: transform an array or object
const doubled = $.map([1, 2, 3], function (value, index) {
return value * 2 // [2, 4, 6]
})
// returning null/undefined removes the item
const evens = $.map([1, 2, 3, 4], function (v) {
return v % 2 === 0 ? v : null // [2, 4]
})
// returning an array flattens it
const flat = $.map([1, 2], function (v) {
return [v, v * 10] // [1, 10, 2, 20]
})
// .map() on jQuery collections
const ids = $('li').map(function (index, el) {
return $(el).data('id') // jQuery collection of ids
}).get() // .get() converts to plain array [1, 2, 3]for/while 循环
大型集合的原始性能,使用经典 for 循环加 $items[i](原始 DOM 元素)而非 $items.eq(i)(jQuery 包装)——避免每次迭代创建新 jQuery 对象。.length 属性直接可用。for...of 可用于 jQuery 集合(它们可迭代)。但除非测量到性能问题,优先使用 .each() 以提高可读性——差异在数千元素以下很少明显。
// classic for loop with .eq(i)
const $items = $('li')
for (let i = 0; i < $items.length; i++) {
const $item = $items.eq(i)
$item.text('Item ' + i)
}
// or use the raw DOM elements (faster, no jQuery wrapping)
for (let i = 0; i < $items.length; i++) {
$items[i].textContent = 'Item ' + i // raw DOM
}
// while loop
let i = 0
while (i < $items.length) {
$($items[i]).addClass('item-' + i)
i++
}
// for...of (modern, on the collection)
for (const el of $items) {
$(el).addClass('processed')
}迭代中过滤
迭代前过滤使代码更清晰——$('li').filter('.active').each(...) 比 .each() 内的条件判断更地道。要收集子集,将匹配的原始 DOM 元素 push 到数组,最后用 $() 包装。对于值提取,.map().get() 是最干净的模式。避免在循环中构建 jQuery 集合(每个 $() 调用有开销)——收集原始元素,一次包装。
// filter then iterate
$('li').filter('.active').each(function () {
$(this).addClass('highlighted')
})
// iterate with conditional logic
$('li').each(function (i) {
if (i % 2 === 0) {
$(this).addClass('even')
} else {
$(this).addClass('odd')
}
})
// collect matching items
const active = []
$('li').each(function () {
if ($(this).hasClass('active')) {
active.push(this) // push raw DOM element
}
})
const $active = $(active) // wrap in jQuery
// or use .map().get()
const ids = $('li.active').map(function () {
return $(this).data('id')
}).get()嵌套与复杂迭代
嵌套 .each() 处理网格/矩阵。从数据构建 DOM 时,append 到文档片段然后一次插入(比每项 append 快得多——每次 append 触发回流)。$(document.createDocumentFragment()) 创建 jQuery 包装的片段。对于非常大的列表,考虑字符串拼接 + .html()(最快)或模板库。现代框架(Vue/React)声明式处理,但片段模式仍是有用的 jQuery 优化。
// nested iteration over rows and cells
$('table tr').each(function (rowIdx, row) {
$(row).find('td').each(function (colIdx, cell) {
$(cell).text('R' + rowIdx + 'C' + colIdx)
})
})
// iterate over a JSON response and build DOM
$.get('/api/users').done(function (users) {
const $list = $('#users')
$.each(users, function (i, user) {
$list.append(
$('<li>').text(user.name).data('id', user.id)
)
})
})
// build with a document fragment (faster)
const $frag = $(document.createDocumentFragment())
$.each(users, function (i, user) {
$frag.append('<li>' + user.name + '</li>')
})
$('#users').append($frag) // one DOM update数据存储与缓存
$.data 与 .data()
$.data(el, key, val) 是原始 DOM 元素上的底层函数;.data() 是 jQuery 集合上的便捷方法。两者都在内部缓存中存储值(不在 DOM 中),比属性访问快。.data() 还自动读取 data-* 属性并类型转换(数字、布尔值、JSON)。通过 .data(key, val) 设置不会更新 data-* 属性——用 .attr('data-key', val) 更新属性。用 $.removeData(el, key) 或 .removeData(key) 清除。