Skip to content

jQuery 速查表

快速、小巧且功能丰富的 JavaScript 库。

01

入门基础

引入 jQuery

通过 CDN 引入 jQuery 可以实现快速、缓存的加载。压缩版(.min.js)用于生产环境;未压缩版用于调试。务必提供本地回退方案,以防 CDN 不可访问。使用 npm 时,可以将 jQuery 作为模块导入——对于 Webpack/Vite 等打包工具很有用。jQuery 3.7.x 是当前版本线;不再支持旧版 IE。

jquery
<!-- 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)之前触发。

jquery
// 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 回调以在局部使用别名。

jquery
// $ 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),链会中断,因为它们返回值而非集合。

jquery
// 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 和遗留环境中必不可少。

jquery
// 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)
02

选择器

基础选择器

jQuery 使用 CSS 选择器(大部分与 querySelectorAll 兼容)。三大基础:标签名、#id、.class。逗号组合选择器(并集)。#id 最快(使用 getElementById)。通配符 * 很慢——避免在大文档上使用。选择器结果始终是 jQuery 集合(类数组),即使匹配 0 或 1 个元素。

jquery
// 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(...) 限定范围以获得更好的性能。

jquery
// 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-* 属性。

jquery
// 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 对于读取表单状态至关重要。它们是动态的——每次访问都会重新求值。

jquery
// :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() 方法。

jquery
// 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')
03

事件

使用 .on() 绑定事件

.on() 是现代的、多合一事件绑定方法(取代了 .bind/.delegate/.live)。传入事件名和处理函数。处理函数内的 'this' 是 DOM 元素(非 jQuery 包装——需要时用 $(this) 包装)。使用事件命名空间(click.myapp)可以解绑特定处理函数而不影响其他。多个事件可共享一个处理函数(空格分隔)或有独立处理函数(对象形式)。

jquery
// 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 访问父元素。

jquery
// 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() 的可选第二个参数向处理函数传递数据。

jquery
$('.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(),它更明确且支持委托。

jquery
// 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) 订阅。

jquery
// 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')
04

DOM 操作

获取与设置内容

.html() 处理 HTML 标记(类似 innerHTML)——标签会被解析。.text() 用于纯文本——标签被转义并按字面显示(对用户输入更安全,防 XSS)。.val() 用于表单控件(input、select、textarea)。三者在不传参时为 getter(返回第一个元素的值),传参时为 setter(应用到所有匹配元素)。Setter 接受回调 (index, oldValue) => newValue。

jquery
// .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)反转主体——便于在新元素上链式调用。新插入的元素如果通过委托绑定则继承事件处理函数,但在插入前直接绑定的则不会。

jquery
// 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(''),那会导致子元素的数据/事件泄漏。

jquery
// 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 字符串的情况下重构标记非常强大。

jquery
// 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。

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 */ }
05

CSS 操作

获取与设置 CSS 属性

.css() 获取计算样式(始终是解析后的值,即使来自 CSS 规则)或设置内联样式。Getter 传入单个属性名(返回字符串)。Setter 传入 (prop, value) 或 prop:value 对象。属性名可以是 camelCase 或 kebab-case(后者需引号)。数值对大多属性默认为像素。避免用 .css() 做布局——使用类和样式表以保持可维护性。

jquery
// 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() 快得多,因为它们批量更改并让浏览器优化。

jquery
// 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') 返回带单位的字符串),便于数学运算。

jquery
// 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() 查找定位祖先。这些对于动画、拖放和无限滚动至关重要。

jquery
// .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 在可见性检测上更高效;新项目可考虑使用。

jquery
// 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)
}
06

AJAX

$.ajax(完全控制)

$.ajax 是底层、完全控制的方法。method/http 动词、data(GET 时作为查询字符串,POST 时作为请求体)、dataType 提示预期响应(自动解析)。success/error/complete 是旧版回调钩子。beforeSend 让你修改 xhr(添加请求头)。timeout 在 N 毫秒后中止。现代代码推荐使用 fetch() 或 $.ajax 配合 .done()/.fail() promise,而非 success/error 回调。

jquery
$.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,但大多数情况下工作方式类似。

jquery
// 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 更新。

jquery
<!-- 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 出现前的变通方案,有安全注意事项。

jquery
// 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 令牌。

jquery
// 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}, ...]
07

效果与动画

基础显示/隐藏

.show()/.hide()/.toggle() 改变 display。不带参数时瞬间显示/隐藏。带持续时间时,动画 width/height/opacity。'slow' = 600ms,'fast' = 200ms,或数值(毫秒)。回调在动画完成时触发。jQuery 动画默认使用 requestAnimationFrame(无卡顿)。注意:.hide() 设置 display:none;.show() 恢复原始 display 值。如需更多控制,直接使用 .animate()。

jquery
// 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 更平滑。

jquery
// 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 时平滑且无障碍友好。

jquery
// 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' 为匀速。一次调用可动画多个属性。

jquery
// 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() 配对以避免卡顿堆积。

jquery
// .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)
08

遍历

向上移动(父元素)

向上遍历:.parent()(直接父元素),.parents()(直到 document 的所有祖先),.parents(selector) 过滤它们,.closest(selector) 向上查找并返回第一个匹配(包括自身——最常用于查找封闭组件)。.parentsUntil(selector) 返回直到但不包括匹配的祖先。.closest() 是事件委托模式的主力——给定点击的元素,找到其封闭的卡片/行/组件。

jquery
// 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() 限定遍历范围,避免扫描整个文档。

jquery
// 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 重置兄弟状态。兄弟共享同一父元素——如果元素嵌套方式不同,则不是兄弟。

jquery
// 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) 取范围。在遍历后用这些方法细化选择,而非编写复杂选择器——通常更清晰且更快。

jquery
// .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() 回父元素、修改父元素。缩进链使遍历结构清晰。

jquery
// .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
09

工具函数

迭代:$.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)。

jquery
// $.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 对深度扩展安全仍很实用。

jquery
// $.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]。

jquery
// $.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。

jquery
// $.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() 时缓存自动清理,防止泄漏。

jquery
// 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
10

插件与扩展

编写插件

插件扩展 $.fn(jQuery 的原型),因此可在集合上调用。始终返回 this(或 this 上方法的返回值)以支持链式调用。插件内部,'this' 是 jQuery 集合;.each() 内部,'this' 是原始 DOM 元素。用 IIFE 包装以通过 jQuery 安全别名 $。$.extend 默认值模式是接受带合理默认值选项的标准方式。通过 npm 或独立脚本发布插件。

jquery
// 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 站点,此模式仍然有用且广为理解。

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
<!-- 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。

jquery
// 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) 暴露公共方法和默认值以提供灵活性。

jquery
// 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
11

属性与特性

属性 Getter 与 Setter

.attr() 读写 HTML 属性(标记中的值,始终为字符串)。.prop() 读写 DOM 属性(当前状态——如复选框的 checked 属性在用户交互后可能与 checked 属性不同)。布尔属性(checked、disabled、selected)用 .prop(),其他(href、src、alt、data-*)用 .attr()。.data() 读取 data-* 但将值存储在内部缓存中并保留类型——如需更新 DOM 属性本身,使用 .attr('data-*')。

jquery
// .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')。

jquery
<!-- 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') 是简洁的布尔检查。这些区别对表单验证和序列化很重要。

jquery
// 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 中要有意识地选择。

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() 方法)。

jquery
// 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()
12

尺寸与位置

宽度与高度方法

宽/高方法返回数值(px),不像 .css('width') 返回带单位的字符串。.width() = 内容框(CSS width)。.innerWidth() = 内容 + padding。.outerWidth() = 内容 + padding + border。.outerWidth(true) 包含 margin。对于 window/document,$(window).width() 是视口(可见区域);$(document).height() 是整个页面高度(滚动时可能大于视口)。Setter 接受数值(px)或字符串('50%'、'10em')。

jquery
// 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(父元素)的区别对正确的定位计算至关重要。

jquery
// .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 是现代标准。

jquery
// 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 方式可行但在每次滚动事件时触发。

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 用于触发。

jquery
// 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)')
})
13

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 相关。

jquery
// 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 重的代码库中仍有用。

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
// 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 new

AJAX 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 干净集成。

jquery
// $.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 或其他异步工作干净地链式。

jquery
// 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')
})
14

表单

表单事件

submit 在表单提交时触发——AJAX 时始终 preventDefault。input 在每次按键(文本)或值改变时触发——适合实时验证或即搜即输。change 对文本输入在 blur 时触发,但对复选框、单选和选择框立即触发。focus/blur 在字段获焦时触发。对动态添加的字段在表单上使用事件委托。'change' 与 'input' 的区别很重要:'input' 是实时的,'change' 是用户完成编辑时。

jquery
// 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 提交收集表单数据而无需完全刷新的标准方式。

jquery
<!-- 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)是现代基线。

jquery
// 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', ...))处理动态添加的移除按钮点击。对于可重排字段(带上/下按钮或拖放),每次更改后重新编号名称让服务器看到干净序列。此模式常用于标签输入、动态行项目和配置表单。

jquery
// 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 表单——渐进增强的常见做法。

jquery
// 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')
  })
})
15

迭代与循环

$.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' 时仍有用。

jquery
// $.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 集合提取值时仍然方便。

jquery
// $.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() 以提高可读性——差异在数千元素以下很少明显。

jquery
// 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 集合(每个 $() 调用有开销)——收集原始元素,一次包装。

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 优化。

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
16

数据存储与缓存

$.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) 清除。

jquery
// $.data: low-level, on raw DOM element
const el = document.getElementById('box')
$.data(el, 'count', 1)
$.data(el, 'count')       // 1
$.data(el, 'count', 2)
$.data(el, 'count')       // 2
$.removeData(el, 'count')

// .data(): method on jQuery collection (more common)
$('.box').data('count', 1)
$('.box').data('count')   // 1
$('.box').removeData('count')

// auto-reads data-* attributes (with type conversion)
// <div data-id="42" data-active="true"></div>
$('.box').data('id')      // 42 (number)
$('.box').data('active')  // true (boolean)

存储每元素状态

每元素状态是 .data() 的杀手级功能——存储与每个元素关联的配置、计数器、标志或对象而不污染 DOM。不带参数调用 .data() 返回所有存储数据为对象。注意存储对象并修改它会修改存储的引用(无需重新设置)。内部缓存在通过 .remove() 移除元素时自动清理,防止内存泄漏——但通过原始 DOM 方法(parentNode.removeChild)移除则不会。

jquery
// store per-element state on plugin init
$('.counter').each(function () {
  $(this).data('count', 0)
})

// increment on click
$('.counter').on('click', function () {
  const $el = $(this)
  const c = $el.data('count') + 1
  $el.data('count', c)
  $el.text('Count: ' + c)
})

// retrieve all data
const all = $('.counter').data()  // { count: 5, ... }

// store objects
$('.box').data('config', { color: 'red', size: 10 })
$('.box').data('config').color = 'blue'  // mutates the stored object
$('.box').data('config').color  // 'blue'

缓存选择集

缓存 jQuery 选择避免重复查询 DOM——在循环或频繁处理函数中是显著性能提升。约定:缓存的 jQuery 对象加 $ 前缀($boxes、$nav)以区分原始 DOM 元素。注意:DOM 变化时(添加/移除匹配元素)缓存的选择会过时。动态内容插入后重新查询。对于事件处理函数,在处理函数外部(同一作用域)缓存避免每次事件重新查询。

jquery
// BAD: re-querying the DOM on every use
function update() {
  $('.box').text('updated')     // queries every call
  $('.box').addClass('done')    // queries again
}

// GOOD: cache the selection
const $boxes = $('.box')        // query once
function update() {
  $boxes.text('updated')
  $boxes.addClass('done')
}

// re-query when content changes
$('#container').load('/partial', function () {
  // $boxes is stale — re-query
  const $newBoxes = $('.box')
})

// convention: prefix cached jQuery objects with $
const $nav = $('#nav')
const $items = $('.item')

HTML5 data-* 属性

data-* 属性让你在 HTML 中声明式嵌入配置——非常适合渐进增强(服务器渲染配置,JS 读取)。.data() 读取时类型转换(数字、布尔值、JSON 对象);.attr() 读取原始字符串。多词名称在 HTML 中用 kebab-case(data-foo-bar),JS 中用 camelCase(.data('fooBar'))。JSON 值在 HTML 中用单引号包裹(JSON 使用双引号)。通过 .attr() 更新 data-* 持久化到 DOM;.data() 只写入缓存。

jquery
<!-- declarative config in HTML -->
<div class="widget"
     data-id="42"
     data-mode="advanced"
     data-config='{"timeout":5000,"retries":3}'
     data-enabled="true">
  Widget
</div>

<script>
// .data() reads with type conversion
const $w = $('.widget')
$w.data('id')        // 42 (number)
$w.data('mode')      // 'advanced'
$w.data('config')    // { timeout: 5000, retries: 3 } (parsed JSON)
$w.data('enabled')   // true (boolean)

// .attr() reads raw strings
$w.attr('data-id')   // '42' (string)
$w.attr('data-config') // '{"timeout":5000,...}' (string)

// camelCase mapping: data-foo-bar -> .data('fooBar')
$('[data-foo-bar]').data('fooBar')
</script>

内存与清理

使用 .remove() 删除元素——它自动清理关联的数据和事件。.detach() 从 DOM 移除但保留数据/事件(用于重新插入)。.empty() 移除子元素并清理其数据/事件。绝不要用 .html('') 清除子元素——它不清理子元素的数据/事件,导致内存泄漏。对于插件,暴露 destroy 方法解绑事件并移除数据。jQuery 的自动清理是使用 .remove() 而非原始 DOM 移除的主要原因。

jquery
// .remove() cleans up data and events
$('.box').remove()  // safe — data and events gone

// .detach() preserves data and events for re-insertion
const $box = $('.box').detach()
$('#elsewhere').append($box)  // events still fire

// .empty() removes children AND their data/events
$('.container').empty()  // children's data/events cleaned

// .html('') does NOT clean up children's data/events (leak!)
// use .empty() instead

// manual cleanup before removal
$('.box').off().removeData().remove()

// custom cleanup in a plugin
$.fn.myPlugin = function () {
  this.on('click.myplugin', handler)
  this.data('myplugin', { /* state */ })

  // expose destroy for manual cleanup
  // ... or use .on('remove') event (non-standard)
17

自定义动画

.animate() 基础

.animate() 在持续时间内补间数值 CSS 属性。使用相对值('+=50'、'-=20')进行增量更改。属性名必须是 camelCase(marginLeft,不是 margin-left)。特殊值 'toggle'、'show'、'hide' 的工作方式类似对应方法。数值默认为像素。颜色需要 jQuery Color 插件——核心 jQuery 只动画数值。回调在动画完成时触发一次(每个元素,所以集合可能多次触发)。

jquery
// animate numeric CSS properties
$('.box').animate({
  width: '500px',
  height: 200,         // number = px
  opacity: 0.5,
  marginLeft: '+=50',  // relative
  fontSize: '20px'     // camelCase for hyphenated props
}, 1000)  // duration in ms

// with callback
$('.box').animate({ left: 300 }, 1000, function () {
  console.log('done')
})

// 'slow' = 600, 'fast' = 200
$('.box').animate({ height: 'toggle' }, 'slow')

// special values: 'toggle', 'show', 'hide'
$('.box').animate({ width: 'toggle' })

缓动与选项

缓动控制速度曲线:'swing'(默认,慢开始/结束,快中间)和 'linear'(匀速)。jQuery UI 或缓动插件添加 30+ 种(easeInOutCubic、easeOutBounce)。options 形式提供精细控制:step 每动画帧调用(用于同步其他元素),queue:false 并行运行(默认 true 顺序排队),specialEasing 设置每属性缓动。step 接收 (now, fx),fx 含 fx.prop(属性名)和 fx.start/fx.end。

jquery
// easing: 'swing' (default, ease-out) or 'linear'
$('.box').animate({ left: 300 }, 1000, 'linear')

// options object form
$('.box').animate({ left: 300 }, {
  duration: 1000,
  easing: 'swing',
  complete: function () { console.log('done') },
  step: function (now, fx) {
    console.log(fx.prop, now)  // called each frame
  },
  queue: true,         // default true (sequential)
  specialEasing: {
    width: 'linear',   // per-property easing
    height: 'swing'
  }
})

// jQuery UI adds more easings: easeInOutCubic, etc.
// or include jquery.easing plugin

动画队列

每个元素有 'fx' 队列——同一元素上的动画默认顺序执行。用 queue:false 并行运行。.delay(ms) 插入暂停。.queue(fn) 在队列中插入自定义函数——调用 next()(或 dequeue())继续。这让你在序列中混合动画与任意代码(AJAX、回调)。.clearQueue() 清空队列不影响当前动画。.dequeue() 前进到下一个排队项。

jquery
// animations queue per element (default)
$('.box')
  .animate({ width: 500 }, 500)  // runs first
  .animate({ height: 300 }, 500) // runs after
  .animate({ opacity: 0.5 }, 500) // runs after that

// total: 1500ms (sequential)

// run in parallel: queue: false
$('.box').animate({ width: 500 }, { duration: 500, queue: false })
$('.box').animate({ height: 300 }, { duration: 500, queue: false })
// both run simultaneously

// .delay(): pause in the queue
$('.box')
  .animate({ width: 500 }, 500)
  .delay(300)              // wait 300ms
  .animate({ height: 300 }, 500)

// .queue(): insert custom function into the queue
$('.box').animate({ width: 500 }, 500).queue(function (next) {
  console.log('between animations')
  next()  // must call to continue
})

颜色动画

核心 jQuery 无法动画颜色(它们是非数值的)。jQuery Color 插件(小而独立)或 jQuery UI 添加颜色动画支持——backgroundColor、color、borderColor 等。jQuery UI 还启用基于类的动画:.addClass('foo', 500) 在 500ms 内过渡到新类样式。现代站点优先使用 CSS 过渡(transition: background 0.3s)——它们硬件加速且无需 JS。jQuery 颜色动画仅用于遗留支持。

jquery
<!-- jQuery core does NOT animate colors -->
<!-- include jQuery Color plugin or jQuery UI -->
<script src="https://code.jquery.com/color/jquery.color-2.2.0.min.js"></script>

<script>
// now color animations work
$('.box').animate({
  backgroundColor: '#ff0000',
  color: '#ffffff',
  borderColor: '#000000'
}, 1000)

// jQuery UI also enables:
// - class animations: .toggleClass('active', 500)
// - more easing functions
// - color transitions on hover
$('.box').hover(function () {
  $(this).animate({ backgroundColor: '#eee' }, 300)
}, function () {
  $(this).animate({ backgroundColor: '#fff' }, 300)
})
</script>

停止与清除

.stop() 停止当前动画。带 (clearQueue=true) 还清空队列。带 (jumpToEnd=true) 将当前动画跳到最终状态。.finish() 立即将所有排队动画跳到最终状态。:animated 选择器匹配动画中的元素。始终将悬停驱动的动画与 .stop(true) 配对以防止堆积——没有它,鼠标快速进出时动画会排队并在鼠标离开后很久才播放完毕,导致卡顿行为。

jquery
// .stop([clearQueue], [jumpToEnd])
$('.box').stop()              // stop current, stay at current state
$('.box').stop(true)          // also clear the queue
$('.box').stop(true, true)    // clear queue AND jump current to end
$('.box').stop(false, true)   // jump current to end, keep queue

// .finish(): jump ALL queued animations to end state
$('.box').finish()

// prevent hover animation buildup
$('.box').hover(function () {
  $(this).stop(true).animate({ width: 300 }, 200)
}, function () {
  $(this).stop(true).animate({ width: 100 }, 200)
})

// check if currently animating
if (!$(':animated').length) { /* nothing animating */ }
if ($('.box').is(':animated')) { /* still animating */ }
18

事件委托

为什么使用委托?

事件委托在父元素上绑定一个处理函数;子元素上的点击冒泡上来并触发它。优点:(1) 一个处理函数代替 N 个(内存),(2) 自动支持动态添加的元素,(3) 更快设置(一次绑定 vs N 次)。.on() 的第二个参数('li')是选择器过滤器——处理函数内的 'this' 是匹配的子元素,不是父元素。对任何子元素共享行为的列表、表格或容器使用委托。唯一代价:每事件轻微开销(选择器检查),可忽略。

jquery
// ANTI-PATTERN: bind to each <li> directly
$('li').on('click', function () {
  $(this).toggleClass('done')
})
// - 1 handler PER <li> (100 items = 100 handlers)
// - NEW <li> added later DON'T get the handler
// - high memory, slow setup

// PATTERN: delegate to parent
$('ul').on('click', 'li', function (e) {
  $(this).toggleClass('done')
})
// - 1 handler total
// - works for future <li>
// - low memory, fast setup
// - 'this' is the <li> (not the <ul>)

委托实践

此待办列表模式展示委托的威力:#tasks 上两个委托处理函数处理所有当前和未来项的删除按钮和项切换。添加新 <li> 无需重新绑定。删除按钮上的 e.stopPropagation() 阻止 li 点击也触发(删除时会切换 .done)。.closest('li') 从点击的按钮找到封闭的 <li>。此模式是框架出现前 jQuery SPA 的基础——对类小部件 UI 仍有用。

jquery
<!-- dynamic list -->
<ul id="tasks">
  <li>Task 1 <button class="delete">×</button></li>
</ul>

<script>
// ONE delegated handler handles all current AND future items
$('#tasks').on('click', '.delete', function (e) {
  e.stopPropagation()
  $(this).closest('li').remove()
})

$('#tasks').on('click', 'li', function () {
  $(this).toggleClass('done')
})

// adding new items works without re-binding
$('.add').on('click', function () {
  $('#tasks').append('<li>New task <button class="delete">×</button></li>')
})
</script>

委托与直接绑定

直接绑定将处理函数附加到匹配元素;委托绑定附加到父元素并按选择器过滤。处理函数内的关键区别:直接绑定时 e.currentTarget === this === 元素。委托绑定时 e.currentTarget 是父元素(绑定处理函数处),this 是匹配子元素(选择器匹配),e.target 是实际点击元素(可能是匹配子元素的后代)。用 e.delegateTarget 在委托处理函数中访问父元素。

jquery
// DIRECT: bind directly to element
// - element must exist at bind time
// - handler attached to element itself
// - e.target === e.currentTarget === this
$('.btn').on('click', function (e) {
  console.log(e.currentTarget === this)  // true
})

// DELEGATED: bind to parent, filter by selector
// - works for future children
// - handler attached to parent
// - e.currentTarget = parent (the bound element)
// - this = child (matched selector)
// - e.target = actual clicked element (may be deeper child)
$('ul').on('click', 'li', function (e) {
  console.log(e.currentTarget)  // the <ul>
  console.log(this)              // the <li>
  console.log(e.target)          // clicked element (maybe <span> in li)
})

解绑委托处理函数

命名空间(click.task)是解绑特定处理函数而不影响其他的最干净方式。.off('click', 'li') 移除匹配该选择器的委托处理函数。.off('click') 移除所有 click 处理函数(直接和委托)。.off() 移除一切——谨慎使用。要移除特定处理函数,传入原始函数引用。对于插件,始终命名空间事件(click.myplugin)以便用户能在不了解内部的情况下干净解绑。

jquery
// delegate with namespace
$('ul').on('click.task', 'li', handler)

// remove just this delegated handler (by namespace)
$('ul').off('click.task')

// remove all delegated click handlers
$('ul').off('click')

// remove all delegated handlers with a specific selector
$('ul').off('click', 'li')

// remove ALL handlers (including delegated)
$('ul').off()

// remove with reference to the original handler
function handler() { /* ... */ }
$('ul').on('click', 'li', handler)
$('ul').off('click', 'li', handler)  // exact match

委托陷阱

将委托限定到最近的稳定祖先——不要对一切委托到 document/body(每个事件都冒泡并检查选择器,很慢)。避免过于宽泛的选择器('div')。注意子元素中的 stopPropagation:它阻止祖先上的委托处理函数触发。如果子元素停止传播,父元素的委托处理函数不会运行。权衡:委托灵活但每事件有微小开销(选择器匹配)——通常可忽略,但在超大页面上有影响。

jquery
// PITFALL 1: delegated handler on a parent that's too high
$(document).on('click', '.btn', handler)
// works, but EVERY click bubbles to document and is checked
// (slow on large pages — scope to nearest stable parent)

// BETTER: scope to the nearest container that won't be replaced
$('#sidebar').on('click', '.btn', handler)

// PITFALL 2: selector that matches too broadly
$('body').on('click', 'div', handler)  // fires on every div click

// PITFALL 3: stopping propagation in a child breaks delegation
$('.btn').on('click', function (e) {
  e.stopPropagation()  // parent's delegated handler won't fire!
})

// PITFALL 4: delegated handlers don't fire if a parent
// stops propagation before reaching the delegate target
19

最佳实践

性能技巧

关键性能技巧:(1) 缓存选择;(2) 用 .find() 将查询限定到容器;(3) 优先用 #id 选择器(内部用 getElementById);(4) 避免 *;(5) 委托事件;(6) 通过片段批量 DOM 更新——每次 append 触发回流,所以构建片段一次 append;(7) 重度操作前 detach(对分离元素操作不触发回流)。1000+ 项列表,字符串拼接 + .html() 最快,但片段更清晰。

jquery
// 1. cache selections
const $items = $('.items')  // query once

// 2. scope queries to a container
$('.item', $container)      // search within container
$container.find('.item')    // equivalent, slightly faster

// 3. use ID selectors when possible (fastest)
$('#header')                // getElementById

// 4. avoid universal selector
$('*').doSomething()        // SLOW

// 5. delegate events instead of binding many
$('ul').on('click', 'li', fn)  // 1 handler

// 6. batch DOM updates (use document fragment)
const $frag = $(document.createDocumentFragment())
for (let i = 0; i < 1000; i++) {
  $frag.append('<li>Item ' + i + '</li>')
}
$('ul').append($frag)  // one reflow, not 1000

// 7. detach before heavy manipulation
const $list = $('ul').detach()
// ... modify $list ...
$('#container').append($list)

DOM 插入性能

DOM 插入是最大性能瓶颈。每次 .append() 触发回流(浏览器重新计算布局)。批量插入:(1) 构建 HTML 字符串并用 .html() 设置(简单内容最快);(2) 使用文档片段并一次 append(复杂元素更清晰);(3) 分离容器、修改、重新附加(避免修改期间实时 DOM 回流)。1000+ 项时,字符串方式明显快于逐项 .append()。

jquery
// SLOW: appending in a loop (reflow per iteration)
for (let i = 0; i < 1000; i++) {
  $('ul').append('<li>Item ' + i + '</li>')
}

// FASTER: build string, append once
let html = ''
for (let i = 0; i < 1000; i++) {
  html += '<li>Item ' + i + '</li>'
}
$('ul').html(html)

// FASTEST: detached node + fragment
const $list = $('ul').detach()
const $frag = $(document.createDocumentFragment())
for (let i = 0; i < 1000; i++) {
  $frag.append($('<li>').text('Item ' + i))
}
$list.append($frag).appendTo('#container')

// even faster: array.join
const html = new Array(1000).fill(0).map((_, i) =>
  '<li>Item ' + i + '</li>'
).join('')
$('ul').html(html)

现代 jQuery 模式

现代 jQuery(3.x)模式:使用 .on()/.off()(旧方法 .bind/.live/.delegate 已移除或弃用)。使用 promise 接口(.done/.fail)而非 success/error 回调。在 IIFE 或 ES 模块中限定代码作用域——不要污染全局。脚本在 body 末尾时跳过 $(document).ready。优先用 CSS 过渡/动画而非 .animate()(硬件加速,更平滑)。使用 'use strict'。新项目评估框架(Vue/React)是否比 jQuery 更合适。

jquery
// use .on() / .off() (not bind/unbind, live, delegate)
$('.btn').on('click', fn)

// use promises (.done/.fail) instead of success/error
$.get('/api').done(fn).fail(errFn)

// use IIFE or modules to scope code
(function ($) {
  'use strict'
  // private code
  $('.btn').on('click', fn)
}(jQuery))

// or ES modules
import $ from 'jquery'
$('.btn').on('click', fn)

// don't pollute global scope
// don't use $(document).ready if script is at end of body

// prefer CSS for animations (hardware-accelerated)
// .css({ transition: 'all 0.3s', transform: 'translateX(100px)' })

无障碍

无障碍要点:添加 ARIA 角色和状态(role='dialog'、aria-modal、aria-expanded、aria-labelledby)。使自定义小部件键盘可访问——处理按钮的 Enter/Space、菜单的箭头键、Esc 关闭模态框。管理焦点:打开模态框时将焦点移入,关闭时返回触发器。使用语义元素(<button>、<a>、<nav>)而非带 onclick 的 div。可见性:优先用 CSS 类(屏幕阅读器可能忽略)而非 display:none(始终忽略)隐藏。

jquery
// add ARIA attributes for screen readers
$('.modal').attr({
  role: 'dialog',
  'aria-modal': true,
  'aria-labelledby': 'modal-title'
})

// toggle aria-expanded for collapsibles
$('.accordion .header').on('click', function () {
  const $h = $(this)
  const expanded = $h.attr('aria-expanded') !== 'true'
  $h.attr('aria-expanded', expanded)
  $h.next('.content').slideToggle()
})

// keyboard navigation
$('.menu-item').on('keydown', function (e) {
  if (e.which === 13 || e.which === 32) {  // Enter or Space
    $(this).trigger('click')
  }
})

// focus management for modals
$('.modal').on('shown', function () {
  $(this).find('input:first').focus()
})

// use button elements for actions (not <div onclick>)
$('<button>').text('Save').on('click', save).appendTo('body')

安全:XSS 与 HTML 注入

绝不要通过 .html() 注入不可信字符串——它执行嵌入的 <script> 标签和事件处理函数(XSS)。纯文本用 .text()(自动转义)。必须包含 HTML 时,先净化(手动转义或 DOMPurify 库)。最安全模式:用 $() 构建元素并用 .text() 处理动态内容——$('<li>').text(userInput) 自动转义。特别注意 .append('<div>' + data + '</div>') 模式——如果 data 用户可控则是 XSS 向量。

jquery
// DANGEROUS: injecting untrusted HTML
const userInput = '<script>alert("xss")</script>'
$('.box').html(userInput)        // EXECUTES the script!

// SAFE: escape with .text()
$('.box').text(userInput)        // displays as text, no execution

// SAFE: sanitize before injecting
function escapeHtml(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
}
$('.box').html(escapeHtml(userInput))

// SAFE: build elements with .text() for content
const $li = $('<li>').text(userInput)  // text is auto-escaped
$('ul').append($li)

// AVOID .html() with user input. Use .text() or DOMPurify.
// $('div').html('<p>' + data + '</p>')  // BAD if data is user input
20

常见模式

模态对话框

模态模式:创建遮罩 + 模态框、append 到 body(逃出父级堆叠上下文)、淡入。点击遮罩(非模态框)关闭——用 e.target === this 检测。Esc 键关闭。关闭时始终清理(移除 DOM、解绑处理函数)。生产环境考虑使用库(Magnific Popup、Bootstrap Modal)处理无障碍(焦点陷阱、ARIA、返回焦点到触发器)、动画和 iframe/ajax 模态。上述模式是最简的手写版本。

jquery
// simple modal pattern
function openModal(html) {
  const $overlay = $('<div class="modal-overlay"></div>')
  const $modal = $('<div class="modal"></div>').html(html)
  $overlay.append($modal).appendTo('body').hide().fadeIn(200)

  $overlay.on('click', function (e) {
    if (e.target === this) closeModal()  // click outside to close
  })

  $(document).on('keydown.modal', function (e) {
    if (e.which === 27) closeModal()  // Esc to close
  })
}

function closeModal() {
  $('.modal-overlay').fadeOut(200, function () { $(this).remove() })
  $(document).off('keydown.modal')
}

// usage
$('.open').on('click', function () {
  openModal('<p>Hello from modal!</p>')
})

标签页

经典标签模式:点击标签链接激活该标签。链接的 href(#tab1)映射到内容的 id。链接和内容上的 active 类控制可见性(CSS: .tab-content:not(.active) { display: none })。处理函数使用事件委托(处理动态添加的标签)。preventDefault 阻止 URL hash 改变(或省略 preventDefault 启用基于 hash 的标签深链接)。为无障碍添加 role='tab'、aria-selected 和键盘箭头导航。

jquery
<!-- HTML -->
<div class="tabs">
  <ul class="tab-nav">
    <li><a href="#tab1" class="active">Tab 1</a></li>
    <li><a href="#tab2">Tab 2</a></li>
    <li><a href="#tab3">Tab 3</a></li>
  </ul>
  <div id="tab1" class="tab-content active">Content 1</div>
  <div id="tab2" class="tab-content">Content 2</div>
  <div id="tab3" class="tab-content">Content 3</div>
</div>

<script>
$('.tab-nav').on('click', 'a', function (e) {
  e.preventDefault()
  const $a = $(this)
  $a.addClass('active').parent().siblings().find('a').removeClass('active')
  $($a.attr('href')).addClass('active').siblings().removeClass('active')
})
</script>

手风琴

手风琴模式:点击头部切换其内容。单开变体在打开一个时关闭其他(多开则移除 .not($h) 块)。使用 aria-expanded 向屏幕阅读器暴露状态——CSS 可基于它样式化(.acc-header[aria-expanded="true"] { ... })。slideToggle 动画内容。头部使用 <button>(默认键盘可访问)。多项时,父元素上的事件委托避免逐个头部绑定。

jquery
<!-- HTML -->
<div class="accordion">
  <div class="acc-item">
    <button class="acc-header" aria-expanded="false">Section 1</button>
    <div class="acc-content">Content 1</div>
  </div>
  <!-- more items -->
</div>

<script>
$('.accordion').on('click', '.acc-header', function () {
  const $h = $(this)
  const $c = $h.next('.acc-content')

  // close others (single-open accordion)
  $h.closest('.accordion')
    .find('.acc-header').not($h)
      .attr('aria-expanded', false)
      .next('.acc-content').slideUp()

  // toggle this one
  const isOpen = $h.attr('aria-expanded') === 'true'
  $h.attr('aria-expanded', !isOpen)
  $c.slideToggle()
})
</script>

无限滚动

无限滚动:检测用户滚动接近底部时,获取更多项并 append。防止并发加载(loading 标志)。服务器返回无更多项时,解绑滚动处理函数(不再尝试)。通过片段 append 以提高性能。节流滚动处理函数(每秒触发多次)——用防抖/_.throttle 或检查 loading 标志(如此处)跳过请求进行中。现代替代:底部哨兵元素上的 IntersectionObserver。

jquery
let loading = false
let page = 1

function loadMore() {
  if (loading) return
  loading = true
  $('.loader').show()

  $.get('/api/items', { page: page })
    .done(function (items) {
      if (items.length === 0) {
        // no more items — unbind the scroll handler
        $(window).off('scroll.inf')
        return
      }
      const $frag = $(document.createDocumentFragment())
      items.forEach(item => {
        $frag.append($('<li>').text(item.name))
      })
      $('ul.items').append($frag)
      page++
    })
    .always(function () {
      loading = false
      $('.loader').hide()
    })
}

// trigger when near bottom
$(window).on('scroll.inf', function () {
  if ($(window).scrollTop() + $(window).height() > $(document).height() - 200) {
    loadMore()
  }
})

实时搜索(防抖)

实时搜索:用户输入时查询服务器并显示结果。防抖必不可少——没有它每次按键都发请求(快速输入 = 数十个请求)。防抖在最后一次按键后等待 N 毫秒再触发。300ms 是良好默认值。始终处理空查询(清空结果)。生产环境还应在开始新请求时取消进行中的请求(对上一个 jqXHR 使用 .abort())以避免乱序响应。现代替代:fetch 配合 AbortController。

jquery
// debounce: wait until user pauses typing
function debounce(fn, wait) {
  let timer
  return function () {
    clearTimeout(timer)
    const args = arguments
    const ctx = this
    timer = setTimeout(() => fn.apply(ctx, args), wait)
  }
}

// live search
const $input = $('#search')
const $results = $('#results')

$input.on('input', debounce(function () {
  const q = $(this).val()
  if (!q) { $results.empty(); return }

  $.get('/api/search', { q: q })
    .done(function (items) {
      $results.empty()
      items.forEach(item => {
        $results.append($('<li>').text(item.name))
      })
    })
}, 300))  // wait 300ms after last keystroke

// without debounce, a search fires on every keystroke (too many requests)

这篇内容对您有帮助吗?

学习路径

从零开始学习

通过结构化课程从头学习这个语言。