页面不是死物:按钮等你去点,输入框等你来敲。这个“等”,就是事件。 前面每一式的演示都在用事件,这一式,我们把它说透。

本式要点
  • 会用 on('事件名', fn) 绑定事件,认识常用事件名
  • 明白回调里的 this 与 $(this),会用 e.preventDefault() 与 e.stopPropagation()
  • 掌握事件委托,让“新添加的元素”一出生就能响应;会用 off() 解绑
  • 会用 one() 绑定“只响一次”的事件,用 trigger() / triggerHandler() 主动触发

on:事件的统一入口

jQuery 里绑定事件只有一个入口:on('事件名', 处理函数)。那些“快捷方法”—— .click(fn)、.keyup(fn)——都只是 on 的马甲,写哪个都行, 本教程统一用 on。

常用事件先混个脸熟,用到时回来翻:

事件触发时机典型场景
click单击按钮、菜单
dblclick双击少见,快捷操作
mouseenter鼠标移入元素(不冒泡)悬停高亮
mouseleave鼠标移出元素悬停恢复
keydown按下按键(长按会连续触发)快捷键
keyup松开按键输入统计、即时回显
change值改变(勾选框立即、输入框失焦时)表单联动
submit表单提交(回车或点提交按钮)表单校验
focus获得焦点提示、清空默认值

回调里的 this

事件回调执行时,this 指向“触发事件的元素本身”(原生 DOM 元素)。 用 $(this) 把它包成 jQuery 对象,就能施展全部招式。

妙处在于:绑定的时候你并不知道用户会点哪一个——this 永远指向“当事人”, 一个处理函数就能服务整个集合。下一个演示会反复用到它。

事件委托:让新元素一出生就会响应

直接绑定有一个隐藏的坑:$('li').on('click', …) 的监听器是“钉”在当时存在的那些 li 身上的。之后新加进来的 li,身上没有监听器,点了毫无反应。

解法是事件委托:把监听器钉在恒久存在的父容器上,再传一个子元素选择器。 事件从被点的元素一路冒泡到父容器,父容器按选择器过滤——是“我的辖区”才动手。 新元素天然在父容器的辖区里,所以一出生就会响应。

例 1 · 动态添加的条目,只有委托那边会响应

直接绑定:$('#jq05-list-a li').on('click', …)

  • 旧招式 · 第一式
  • 旧招式 · 第二式

事件委托:$('#jq05-list-b').on('click', 'li', …)

  • 旧招式 · 第一式
  • 旧招式 · 第二式

左边的新条目点了没反应,右边的一点就亮——这就是委托的威力。 江湖规矩:凡是会动态添加的元素,一律用事件委托。

事件对象:preventDefault

回调的第一个参数是事件对象(惯例叫 e),它携带着这次事件的详细信息, 也提供两个关键招式。e.preventDefault() 阻止浏览器的默认行为——表单不刷新、链接不跳转。 表单校验是它的头号用武之地:

例 2 · 表单 submit 拦截 + 提示(preventDefault)

按下回车或点“报名”,页面不会刷新。

按下回车或点“报名”,页面纹丝不动,只有提示在变——默认提交被 preventDefault() 拦下了。 真实项目里会在这里先校验,一切妥当后再提交。

冒泡与 stopPropagation

事件默认冒泡:内层元素触发后,会一层层向上汇报,祖先的监听器都会听到。 多数时候这是好事(委托就靠它),但有时不想让外层听到,就用 e.stopPropagation() 在半路截住。

例 3 · 冒泡演示:勾选后事件到内层为止
外层(监听着 click)

点按钮试试。

不勾选时点按钮:内层先说“内层听到了”,外层随后补充“外层也听到了”; 勾选后,事件走到内层为止。冒泡是委托的基石,也是“误伤”的来源——哪层要哪层不要,你说了算。

off:解绑

有绑就要有解。off() 与 on() 的写法一一对应:

常见用法是“只许点一次”——处理函数里先 off 再干活。

只响一次与主动出击:one / trigger

“只许点一次”有更省事的写法:one('事件名', fn)——它是 on 的孪生兄弟, 区别只在响应过一次就自动解绑。领券、签到、首次提示,都适合用它。

反过来,能不能用代码“替用户点一下”?可以。两个方法一对难兄难弟:

写法行为
$el.trigger('focus')模拟真实事件:冒泡、触发默认行为(如让输入框获得焦点),返回 jQuery 对象
$el.triggerHandler('focus')只把绑定的处理函数跑一遍:不冒泡、不触发默认行为,返回处理函数的返回值
例 4 · one 领一次、trigger 遥控一次

入场券: 还没领过。


点两个按钮,观察输入框与这行字的区别。
练功 · 第五式
  1. 在例 1 右侧列表下方加一个“解绑”按钮:点击后用 off 解除委托,再点“两边各添一招”验证新条目不再响应。
  2. 给一个输入框同时绑定 keyup 与 focus:获得焦点时提示“可以输入了”,松开按键时显示“已输入 N 字”。
  3. 思考题:事件委托为什么能让“新添加的元素”自动响应?它和直接绑定在“监听器放在哪”上有什么区别?
参考答案 1

解绑委托必须带上与绑定时相同的选择器——绑定是 on('click', 'li', …), 解绑就是 off('click', 'li')。之后旧条目与新条目都归于平静。

参考答案 2

同一个元素可以绑定多个事件,互不干扰;$(this) 让处理函数不必知道输入框的名字。

参考答案 3

直接绑定把监听器逐个挂在 li 身上——新元素出生时没人给它挂监听器,自然没反应; 委托只挂一个监听器在恒存在的父容器上,靠事件冒泡接收所有后代的事件, 再用选择器过滤——新元素天然在父容器辖区里,所以自动响应。 代价是每个后代事件都要过一遍过滤,元素极多且事件极高频时不如直接绑定精准,但常规场景毫无感觉。

本式小结

  • on() 是事件统一入口,快捷方法都是它的马甲;
  • 回调里的 this 是当事人,$(this) 让它进入 jQuery 招式体系;
  • preventDefault() 拦默认行为,stopPropagation() 截冒泡;
  • 动态元素靠事件委托,off() 让绑定有始有终;
  • one() 是“只响一次”的 on();trigger() 连默认行为一起触发,triggerHandler() 只跑处理函数。
下一式,修炼显隐与动画——hide、fadeIn、slideUp 轮番登场,页面开始有了呼吸。