云计算百科
云计算领域专业知识百科平台

JQuery学习文档(四)

第四部分:事件机制与动画系统

目录

第四部分:事件机制与动画系统

4.1. 事件处理深度解析

4.1.1. 事件绑定演进史:废弃的 .bind()/.live()/.delegate() 与现代标准 .on()

4.1.2. 统一接口:.on() 与 .off() 的全面解析与签名重载

场景一:直接绑定点击事件

场景二:事件委托 – 处理动态生成的列表项

场景三:多事件绑定与数据传递

4.1.3. 一次性绑定:.one() 的内存管理优势

4.2. 事件委托原理

4.2.1. 冒泡机制与事件委托的优势(动态绑定与性能优化)

4.2.2. .on(events, selector, handler) 签名深度剖析

4.3. 事件对象与交互

4.3.1. 事件对象修正:event.originalEvent 与原生事件对象

4.3.2. 冒泡与默认行为:stopPropagation() vs preventDefault()

4.3.3. 模拟触发:.trigger() (触发事件冒泡) vs .triggerHandler() (不冒泡)

4.4. 高级特性

4.4.1. 事件命名空间:.on('click.myPlugin') 的批量解绑与模块化管理

绑定两组不同来源的点击事件

仅解绑UI相关的事件,不影响统计功能

触发特定命名空间的事件

4.4.2. 自定义事件:发布/订阅模式的 jQuery 实现

订阅者:监听自定义的 'dataUpdated' 事件

发布者:在业务逻辑完成后触发事件

4.5. 动画与特效系统

4.5.1. 预置动画:.show(), .hide(), .toggle() 与滑动淡入淡出

带参数的动画调用

带回调函数

避免动画队列堆积的写法

4.5.2. 自定义动画 .animate():参数详解与队列机制

4.5.3. 队列控制:.stop(), .finish(), .queue(), .dequeue() 详解

常见问题:快速移入移出,菜单不停闪烁

解决方案:stop([clearQueue], [jumpToEnd])

4.6. 现代视角下的动画

4.6.1. 性能对比:jQuery 动画 vs CSS3 Transition/Animation vs Web Animations API

传统 jQuery 写法

现代 CSS 方案

JS 仅负责切换类名

现代原生 API 写法

4.6.2. 无障碍访问 (a11y):prefers-reduced-motion 媒体查询的兼容处理

4.6.3. 扩展缓动:jQuery Easing Plugin 与自定义缓动函数

4.1. 事件处理深度解析

4.1.1. 事件绑定演进史:废弃的 .bind()/.live()/.delegate() 与现代标准 .on()

jQuery 的事件绑定 API 经历了从分散到统一的演进过程。

方法

绑定对象

动态元素支持

废弃原因/现状

.bind()

直接绑定到选定元素

不支持

性能差,每次绑定需遍历所有 DOM 元素。

.live()

绑定到 document

支持

已废弃。事件冒泡到文档根节点才触发,性能低下且易冲突。

.delegate()

绑定到指定祖先元素

支持

 .on() 取代,语法不够直观。

.on()

绑定到选定元素或祖先

支持

现行标准。通过参数重载实现了上述所有方法的功能。

早期版本的 .bind()、.live() 和 .delegate() 各自针对不同场景,但在 1.7 版本引入 .on() 后,后者成为了唯一推荐的事件绑定方法,整合了前者的所有功能并修正了潜在的内存泄漏风险。

旧时代写法 – 仅作了解

$('.box').bind('click', function() { /* … */ }); /* 直接绑定,新增.box无效 */

$('.box').live('click', function() { /* … */ }); /* 委托给document,性能黑洞 */

现代标准写法

$('.box').on('click', function() { /* … */ }); /* 替代.bind() */

$('#container').on('click', '.box', function() { /* … */ }); /* 替代.delegate(),支持动态元素 */

这就像是交通工具的进化:

  • 早期的 .bind() 是私家车,只能载特定的人(已存在的元素);
  • .live() 是公交车,虽然谁都能载但路线太长效率低;
  • 现代的 .on() 则是智能网约车系统,既能精准接送(直接绑定),也能通过设置集合点高效拼车(事件委托),一套系统解决所有出行需求。

4.1.2. 统一接口:.on() 与 .off() 的全面解析与签名重载

.on() 是 jQuery 事件体系的核心,通过不同的参数签名实现了直接绑定与委托绑定两种模式。

签名形式

语法

核心用途

基本绑定

.on(events, handler)

将事件监听器直接附加到选定元素。

委托绑定

.on(events, selector, handler)

利用冒泡机制,在父元素监听子元素事件,支持动态内容。

多事件绑定

.on(events-map)

通过对象字面量一次性绑定多个不同事件。

数据传递

.on(events, [data], handler)

在绑定事件时预置数据,供回调函数使用。

理解其重载形式是掌握 jQuery 事件处理的关键。

场景一:直接绑定点击事件

$('#loginBtn').on('click', function(e) {

    /* 这里的this指向#loginBtn本身 */

    console.log('登录触发');

});

场景二:事件委托 – 处理动态生成的列表项

$('#list').on('click', 'li.item', function(e) {

    /* 这里的this指向实际点击的li.item,即使它是后来添加的 */

    $(this).toggleClass('active');

});

场景三:多事件绑定与数据传递

$('.panel').on({

    mouseenter: function() { $(this).addClass('hover'); },

    mouseleave: function() { $(this).removeClass('hover'); }

}, { status: 'active' }); /* 传入的数据会出现在e.data中 */

.on() 就像是前端世界的“万能插座”。

无论你是要插两孔(简单绑定)还是三孔(委托绑定),亦或是需要同时供电多个设备(多事件绑定),只需要这一个接口就能满足所有需求,完全取代了过去五花八门的转接头。

4.1.3. 一次性绑定:.one() 的内存管理优势

在特定场景下,事件只需触发一次,例如表单的首次提交或引导页的首次点击。

特性

.one()

.on() + 手动解绑

代码简洁度

高,一行代码搞定

低,需在回调中写解绑逻辑

内存安全性

自动释放,无残留风险

容易忘记解绑导致内存占用

执行效率

触发后立即销毁监听器

逻辑稍显冗余

.one() 方法专门用于绑定这种一次性事件,触发后立即解绑,避免了手动管理解绑逻辑的繁琐,同时有效防止内存泄漏。

/*新手引导提示仅显示一次 */

$('#guide-btn').one('click', function() {

    alert('欢迎来到系统!此提示将不再显示。');

    /* 此处无需调用off(),监听器已自动销毁 */

});

/* 等效的传统写法(对比) */

$('#guide-btn-old').on('click', function handler(e) {

    alert('欢迎!');

    $(this).off('click', handler); /* 必须手动解绑,且需保存函数引用 */

});

这就像是食品包装上的“一次性封条”。

撕开后就失效了,你不需要费心去把封条粘回去或者写个备忘录提醒自己“这个已经开过了”,系统自动帮你处理了后续的清理工作。

4.2. 事件委托原理

4.2.1. 冒泡机制与事件委托的优势(动态绑定与性能优化)

事件委托的核心依赖于 DOM 事件流的冒泡阶段。

当一个元素的事件被触发时,事件会逐级向上传递至根节点。

通过在父级节点监听事件,可以统一管理子元素的事件响应,这种机制在处理大量子元素或动态内容时尤为高效。

模拟表格删除功能 – 假设有1000行数据

/* 错误做法:给每个删除按钮绑定事件,内存消耗巨大 */

// $('tr .delete-btn').on('click', …); /* 创建1000个监听器 */

/* 正确做法:事件委托 */

$('table tbody').on('click', '.delete-btn', function(e) {

    /* 只创建1个监听器,监听整个tbody */

    /* 无论新增多少行,只要class匹配就能响应 */

    $(this).closest('tr').remove();

});

想象一下公司前台收快递的场景。

如果每个员工都要自己下楼拿快递(直接绑定),效率极低且楼道拥堵。

事件委托就是让前台(父元素)统一签收所有快递,再根据快递单上的名字(selector)分发给对应的员工。

这样无论公司新来了多少人,前台都能处理,且只占用一个接待位。

对比维度

直接绑定

事件委托

内存消耗

高(N个元素=N个监听器)

低(1个父级=1个监听器)

动态元素

无法响应新增元素

自动支持新增子元素

代码维护

需在元素增删时重新绑定

无需额外维护

适用场景

静态页面、少量元素

列表、表格、动态内容区

4.2.2. .on(events, selector, handler) 签名深度剖析

实现事件委托的关键在于 .on() 的第二个参数 selector。

当该参数存在时,jQuery 会在事件触发时检查事件源是否匹配该选择器,从而决定是否执行回调函数。

参数位置

参数名

作用说明

第一个

events

事件类型字符串,如 ‘click’ ‘mouseenter mouseleave’

第二个

selector

过滤选择器。只有事件源匹配该选择器才触发,省略则为直接绑定。

第三个

handler

事件处理函数,this 指向匹配 selector 的元素,而非父元素。

复杂场景:嵌套结构中的精确委托

$('#article').on('click', 'a.external', function(e) {

    e.preventDefault(); /* 阻止默认跳转 */

    /* 检查是否同时按下了Ctrl键 */

    if (e.ctrlKey) {

        window.open($(this).attr('href'), '_blank');

    } else {

        location.href = $(this).attr('href');

    }

});

/* 只有class包含'external'的a标签才会触发,其他点击被忽略 */

这就像是给门卫下达的指令:

  • “凡是穿着红马甲(selector)的人进门都要登记”,而不是“所有人进门都登记”。
  • 门卫(父元素)站在门口,只对符合特征的人进行拦截处理,其他人自由通行。

4.3. 事件对象与交互

4.3.1. 事件对象修正:event.originalEvent 与原生事件对象

jQuery 为了解决跨浏览器兼容性问题,对原生 DOM 事件对象进行了包装。

这种包装提供了统一的属性和方法(如 e.target、e.preventDefault()),但在处理鼠标滚轮、触摸事件等特殊场景时,仍需访问原生对象。

$('.scroll-container').on('wheel', function(e) {

    /* e 是 jQuery 修正后的事件对象 */

    console.log(e.type); /* 输出: wheel */

    /* 访问原生事件对象,获取滚轮方向等原生属性 */

    var originalEvent = e.originalEvent;

    /* 判断滚动方向(原生属性,jQuery未封装) */

    if (originalEvent.deltaY > 0) {

        console.log('向下滚动');

    }

});

jQuery 的事件对象就像是一份“翻译过的外文文档”。

它把不同浏览器(不同方言)的内容统一翻译成了标准语言,方便阅读。

但有些专业术语(原生特有属性)翻译版里没有,这时候就需要查阅“原件”(originalEvent)。

4.3.2. 冒泡与默认行为:stopPropagation() vs preventDefault()

在事件处理中,阻止事件传播和阻止默认行为是两个完全不同的概念,初学者极易混淆。

jQuery 对这两个方法做了标准化封装,确保在所有浏览器中行为一致。

方法

行为描述

典型应用场景

影响范围

stopPropagation()

阻止事件向上冒泡

点击弹窗内部时不关闭弹窗

仅影响事件流传播,不影响默认行为

preventDefault()

阻止元素的默认动作

阻止表单提交、阻止链接跳转

仅影响默认行为,不影响事件传播

return false

同时阻止两者

需要同时终止所有行为的场景

jQuery 特有,相当于调用了上述两个方法

场景:点击链接弹出提示,但不跳转,也不触发父级的点击事件

$('#link-container a').on('click', function(e) {

    e.preventDefault(); /* 阻止跳转,链接URL不会打开 */

    alert('链接被点击了');

    /* e.stopPropagation(); 如果加上这句,父级div的点击事件将不会触发 */

});

$('#link-container').on('click', function() {

    console.log('容器被点击了'); /* 依然会触发,因为未阻止冒泡 */

});

想象你在公司发通知(事件触发)。

  • stopPropagation() 是让秘书拦住通知,不让老板(父元素)知道这件事;
  • preventDefault() 是取消了通知里要求的行动(比如“全员开会”),大家照常工作。
  • return false 则是把通知撕了,老板也不通知,会也不开。

4.3.3. 模拟触发:.trigger() (触发事件冒泡) vs .triggerHandler() (不冒泡)

有时需要在代码中主动触发事件,而非等待用户操作。.

trigger() 和 .triggerHandler() 都能触发事件,但在行为控制和返回值上存在关键差异。

场景:页面加载后自动聚焦输入框

$('#search-input').on('focus', function() {

    $(this).addClass('highlight');

    console.log('聚焦事件触发');

});

使用 trigger()

$('#search-input').trigger('focus');

/* 结果:输入框获得焦点,类名改变,控制台输出。事件冒泡 */

使用 triggerHandler()

var result = $('#search-input').triggerHandler('focus');

/* 结果:类名改变,控制台输出。但输入框并未真正获得焦点(无光标),事件不冒泡 */

/* 返回值:最后一个处理函数的返回值,而非jQuery对象,无法链式调用 */

这就像是消防演习。

特性

.trigger()

.triggerHandler()

浏览器默认行为

执行(如聚焦输入框、提交表单)

不执行

事件冒泡

会冒泡到父元素

不会冒泡

返回值

返回 jQuery 对象,支持链式调用

返回事件处理函数的返回值

适用场景

模拟真实用户交互

纯逻辑调用,测试或执行回调

  • .trigger() 是真的拉响了警报器,声音传遍整栋楼(冒泡),大家都要跑出去(默认行为)。
  • .triggerHandler() 则是演习指挥部内部电话通知“演习开始”,只有相关责任人知道,警报器不响,大家也不用跑动,纯粹是流程逻辑的演练。

4.4. 高级特性

4.4.1. 事件命名空间:.on('click.myPlugin') 的批量解绑与模块化管理

当开发复杂的组件或插件时,可能会绑定大量相同类型的事件。

为了防止解绑时误删其他代码绑定的事件,jQuery 引入了事件命名空间机制,允许通过后缀对事件进行分组管理。

绑定两组不同来源的点击事件

$('#panel').on('click.stats', function() { console.log('统计点击'); });

$('#panel').on('click.ui', function() { console.log('UI交互'); });

$('#panel').on('click.ui.drag', function() { console.log('拖拽逻辑'); });

/* 此时点击面板,控制台输出三条记录 */

仅解绑UI相关的事件,不影响统计功能

$('#panel').off('click.ui');

/* 输出:统计点击。'UI交互'和'拖拽逻辑'均被移除 */

触发特定命名空间的事件

$('#panel').trigger('click.stats'); /* 仅触发统计逻辑 */

这就像是在文件柜里放文件。

普通的绑定是随手一扔,找起来很麻烦。

命名空间就像是给文件贴上了彩色标签(.ui、.stats)。

当你需要清理“UI相关”的所有文件时,只要找到所有蓝色标签的文件扔掉即可,完全不用担心误删了红色的“统计”文件。

操作

语法示例

作用范围

绑定命名空间

.on('click.ns', fn)

属于 ‘ns’ 分组的点击事件

解绑命名空间

.off('.ns')

解绑该分组下所有类型的事件

触发命名空间

.trigger('click.ns')

仅触发该分组的处理函数

4.4.2. 自定义事件:发布/订阅模式的 jQuery 实现

除了浏览器内置的点击、输入等事件,jQuery 还支持完全自定义的事件。

结合 .on()(订阅)和 .trigger()(发布),可以实现模块间的解耦通信,这就是经典的“发布/订阅”模式。

订阅者:监听自定义的 'dataUpdated' 事件

$(document).on('dataUpdated', function(e, newData) {

    console.log('收到数据更新:', newData);

    /* 更新界面逻辑… */

});

发布者:在业务逻辑完成后触发事件

function fetchUserData() {

    $.get('/api/user', function(res) {

        /* 发布事件,传递数据参数 */

        $(document).trigger('dataUpdated', res);

    });

}

这种模式就像是广播电台。

  • 组件 A(电台)只负责广播“这里有新闻了”,它不需要知道谁在听,也不需要知道听众是谁。
  • 组件 B(收音机)只要调到这个频道(监听事件)就能收到消息。
  • 两者互不依赖,电台坏了不影响收音机存在,收音机关了也不影响电台广播。

4.5. 动画与特效系统

4.5.1. 预置动画:.show(), .hide(), .toggle() 与滑动淡入淡出

jQuery 内置了一套简洁的动画 API,无需编写复杂的 CSS 或 JS 逻辑即可实现常见的显示/隐藏效果。

方法

效果描述

等效 CSS 变化

.show() / .hide()

尺寸与透明度同时变化

width, height, opacity

.fadeIn() / .fadeOut()

仅透明度变化

opacity

.slideDown() / .slideUp()

垂直方向展开/收起

height

.fadeToggle() / .slideToggle()

状态切换

对应属性

这些方法在底层实际上是修改了元素的 width、height、opacity 等属性。

带参数的动画调用

$('.box').hide(1000); /* 1秒内隐藏 */

带回调函数

$('.box').show('fast', function() {

    /* 动画完成后执行,this指向动画元素 */

    console.log('显示完成');

});

避免动画队列堆积的写法

$('.nav').stop().slideDown(); /* 先停止当前动画,再开启新动画 */

这些预置动画就像是快餐店的套餐。

虽然不如自己点菜(自定义动画)那么灵活,但胜在标准、快速、口味有保证。

对于简单的交互反馈,直接拿来用最方便。

4.5.2. 自定义动画 .animate():参数详解与队列机制

当预置动画无法满足需求时,.animate() 提供了高度可定制的动画能力。

它允许将任意 CSS 数值属性从当前值过渡到目标值。

参数项

说明

示例值

properties

包含 CSS 属性终值的对象

{ left: '+=50', opacity: 0.5 }

duration

动画持续时间

400 (默认), ‘fast’, ‘slow’

easing

缓动函数

‘swing’ (默认), ‘linear’

complete

完成后的回调函数

function() { … }

/* 将元素向右移动 200px,同时改变透明度 */

$('.mover').animate({

    left: '+=200px', /* 支持相对值写法 */

    opacity: 0.5

}, {

    duration: 800,

    easing: 'swing', /* 非线性变速,更自然 */

    complete: function() {

        console.log('移动完成');

    },

    /* 每一步动画都会触发,适合做同步效果 */

    progress: function(animation, progress) {

        console.log('进度:' + Math.round(progress * 100) + '%');

    }

});

这就像是编剧在写分镜头脚本。

你指定了“背景(CSS属性)”要从明亮变暗淡,“演员(元素)”要从舞台左边走到右边,时间控制在多少秒。

至于中间每一帧怎么演,jQuery(作为导演)会自动计算好插值,把连续的动作流畅地呈现出来。

4.5.3. 队列控制:.stop(), .finish(), .queue(), .dequeue() 详解

jQuery 的动画默认是按顺序执行的,形成了一个“动画队列”。

如果用户快速连续触发动画(如鼠标快速移入移出),队列会堆积,导致元素“抽搐”或重复播放。

队列控制方法用于管理这一行为。

常见问题:快速移入移出,菜单不停闪烁

$('.menu').hover(

    function() { $(this).animate({ height: '200px' }, 500); },

    function() { $(this).animate({ height: '30px' }, 500); }

);

解决方案:stop([clearQueue], [jumpToEnd])
  • clearQueue: 是否清空后续队列;
  • jumpToEnd: 是否跳到当前动画终态

$('.menu').hover(

    function() { $(this).stop(true).animate({ height: '200px' }); }, /* 停止并清空,重开动画 */

    function() { $(this).stop(true).animate({ height: '30px' }); }

);

/* finish():停止当前动画,清空队列,并让所有动画属性跳到终态 */

$('#btn').on('click', function() {

    $('.box').finish(); /* 瞬间完成所有排队的动画 */

});

方法

行为

形象比喻

.stop(false)

暂停当前动画,队列中下一个动画开始执行

唱片切歌,播放下一首

.stop(true)

暂停当前动画,清空后续队列

唱片机直接关机

.finish()

瞬间完成所有队列中的动画,跳到终态

唱片快进到底

.queue()

查看或操作队列函数

查看播放列表

动画队列就像是银行柜台的排队叫号系统。

如果不加干预,每个人(动画)都会按顺序办完业务。

.stop() 就是告诉柜员“我不办了”,.finish() 则是让柜员“把剩下的人全部快速处理完”。

合理使用 .stop(true) 是解决动画卡顿的关键。

4.6. 现代视角下的动画

4.6.1. 性能对比:jQuery 动画 vs CSS3 Transition/Animation vs Web Animations API

随着浏览器技术的发展,jQuery 的动画机制因直接操作 DOM 属性且依赖 CPU 运算,在性能上已落后于现代方案。

现代前端更推荐使用 GPU 加速的 CSS 动画或原生 JS API。

方案

执行线程

性能表现

适用场景

jQuery .animate()

主线程

低(频繁重绘,易掉帧)

兼容老旧浏览器、简单非关键动画

CSS3 Transition

合成线程

高(GPU加速)

状态切换、简单过渡效果

CSS3 Animation

合成线程

复杂关键帧动画、循环动画

Web Animations API

主线程/合成线程

/

需要JS精细控制、高性能需求

传统 jQuery 写法

$('.box').animate({ left: '100px' }, 500);

现代 CSS 方案

.box { transition: transform 0.5s ease; }

.box.active { transform: translateX(100px); }

JS 仅负责切换类名

$('.box').addClass('active');

现代原生 API 写法

const box = document.querySelector('.box');

box.animate([

    { transform: 'translateX(0)' },

    { transform: 'translateX(100px)' }

], {

    duration: 500,

    easing: 'ease'

});

  • jQuery 动画就像是老式拖拉机,虽然能跑,但在高速公路(现代浏览器)上显得力不从心且噪音大。
  • CSS 动画则是高铁,利用专用轨道(GPU)飞速疾驰。
  • Web Animations API 则是跑车,既有速度又能灵活控制方向盘。

4.6.2. 无障碍访问 (a11y):prefers-reduced-motion 媒体查询的兼容处理

无障碍设计已成为现代 Web 开发的标配。

部分用户(如前庭功能障碍患者)会因为动画感到眩晕。

优秀的工程实践应当检测用户的系统偏好,并据此禁用或简化动画。

/* jQuery 动画的兼容处理 */

const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

$('.panel').on('click', function() {

    if (prefersReducedMotion) {

        /* 无障碍模式:直接切换,无动画 */

        $(this).toggle();

    } else {

        /* 标准模式:滑入滑出动画 */

        $(this).slideToggle();

    }

});

/* CSS 中的处理方案 */

/*

@media (prefers-reduced-motion: reduce) {

  *, *::before, *::after {

    animation-duration: 0.01ms !important;

    transition-duration: 0.01ms !important;

  }

}

*/

这就像餐厅为不同口味的顾客提供不同菜单。

普通顾客享受全套精美摆盘(动画),而对某些调料过敏(对动画敏感)的顾客,餐厅应提供简餐版(直接切换),确保所有人都能安全、舒适地享用服务。

4.6.3. 扩展缓动:jQuery Easing Plugin 与自定义缓动函数

jQuery 默认仅提供 swing 和 linear 两种缓动效果。

缓动类型

效果描述

体验感受

linear

匀速运动

机械、生硬

swing

自然、流畅

easeInOutBack

超出目标回弹

活泼、有张力

easeOutBounce

落地弹跳

有趣、物理感

通过引入 jQuery Easing Plugin 或自定义 jQuery.easing 对象,可以极大地丰富动画的运动轨迹,使交互更具弹性与活力。

/* 自定义缓动函数:简单的弹性效果 */

jQuery.easing['myElastic'] = function(x, t, b, c, d) {

    /* x: 0-1的进度值 */

    /* 复杂的数学公式计算弹性位置 */

    return Math.pow(2, -10 * x) * Math.sin((x – 0.1) * 5 * Math.PI) + 1;

};

/* 应用自定义缓动 */

$('.ball').animate({ top: '400px' }, {

    duration: 1000,

    easing: 'myElastic'

});

缓动函数就像是给动画加了“性格”。

linear 是机器人走路,swing 是普通人散步,而自定义缓动则是舞蹈家在舞台上跳跃。

优秀的交互设计往往就赢在这些微妙的“微交互”细节上。

赞(0)
未经允许不得转载:网硕互联帮助中心 » JQuery学习文档(四)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!