4.3 常用事件类型与触发时序 绑定与信号都练熟了,这一节盘点神经的敏感区分布:鼠标、键盘、表单三大族的常用事件,以及每族内部"谁先谁后"的触发次序。时序不是冷知识——悬停闪烁、输入丢失、校验时机不对这三类高频故障,根子全在选错了同类事件里的那一个。 本节摘要:鼠标族要点是 与 不冒泡、不串门, 与 冒泡、会跨进子元素;键盘族用 与 , 已废弃;输入族首选 实时感知、 等失焦确认;表单族拦提交用 、焦点族要冒泡就选 与 。 鼠标族:悬停为什么一闪一闪 早期页面里最经典的悬停故障:给一个带子元素的容器绑 做展开,鼠标在容器里挪动,面板展开又收起抖个不停。病因是 随着指针进入任何子元素都会再次触发——它在子元素边界上来回横跳。
绑定与信号都练熟了,这一节盘点神经的敏感区分布:鼠标、键盘、表单三大族的常用事件,以及每族内部"谁先谁后"的触发次序。时序不是冷知识——悬停闪烁、输入丢失、校验时机不对这三类高频故障,根子全在选错了同类事件里的那一个。
本节摘要:鼠标族要点是
mouseenter与mouseleave不冒泡、不串门,mouseover与mouseout冒泡、会跨进子元素;键盘族用keydown与keyup,keypress已废弃;输入族首选input实时感知、change等失焦确认;表单族拦提交用submit、焦点族要冒泡就选focusin与focusout。
早期页面里最经典的悬停故障:给一个带子元素的容器绑 mouseover 做展开,鼠标在容器里挪动,面板展开又收起抖个不停。病因是 mouseover 随着指针进入任何子元素都会再次触发——它在子元素边界上来回横跳。不冒泡的近亲 mouseenter 与 mouseleave 只在进入、离开容器边界时各触发一次,天生是悬停交互的正解:
// 抖动版:跨进子元素也触发 $('#panel').on('mouseover', function () { $(this).addClass('open'); }); $('#panel').on('mouseout', function () { $(this).removeClass('open'); }); // 稳定版:只在进出容器边界时触发 $('#panel').on('mouseenter', function () { $(this).addClass('open'); }); $('#panel').on('mouseleave', function () { $(this).removeClass('open'); });
成对关系记两条:mouseover 配 mouseout,mouseenter 配 mouseleave;要冒泡(比如委托场景监听一批条目的进出)选前者,只关心容器自身选后者。jQuery 还有一个 hover 快捷方式,把稳定版的两行合成一句,等价于依次绑定 mouseenter 与 mouseleave。
点击族的完整次序是:mousedown 按下、mouseup 抬起、click 在两者于同一元素上完成后触发,快速双击再追加 dblclick。想在"按住拖动"场景里做事情,监听前两个;只关心确认动作,监听 click。同理 contextmenu 在右键抬起时触发,做自定义右键菜单用得上。
键盘三兄弟里,keypress 已被标准废弃——它只对可打印字符可靠,控制键全丢。现代分工只剩两位:keydown 在按下瞬间触发(此时输入框的值还没变),keyup 在抬起时触发(值已更新)。
实时校验输入框该听谁?答案是既不是 keydown 也不是 keyup,而是输入事件 input:
$('#search-box').on('input', function () { var kw = this.value.trim(); console.log('实时关键词:', kw); // 粘贴、拖放、输入法统统覆盖 });
input 的覆盖面是它胜出的原因:键盘输入、右键粘贴、拖拽进文字、输入法确认,任何造成值变化的途径都会触发。keyup 监工的旧代码有一个著名漏网之鱼——纯鼠标粘贴不经过键盘,校验就不跑。change 则是另一个性格:等失焦时才确认一次,适合"填完这格再查"的场景,与 input 的实时监工恰好互补。
提交拦截的正门是 submit——绑在表单上,回调里 preventDefault 即可扣住数据:
$('#register').on('submit', function (event) { event.preventDefault(); console.log('校验通过后才放行,这里先扣住'); });
焦点族有一对与鼠标族同构的近亲:focus 与 blur 不冒泡,focusin 与 focusout 冒泡。给整个表单做"任一输入框聚焦就高亮"这类统一监听,委托给表单容器时必须选 focusin——focus 绑在容器上永远听不到子输入框的动静。下拉框、复选框的值变化由 change 报告,2.4 节的提交前体检正是靠它复检。
| 操作链 | 触发次序 |
|---|---|
| 单击 | mousedown → mouseup → click |
| 双击 | 单击链 → click → dblclick |
| 键入一个字符 | keydown → (值变化触发 input)→ keyup |
| 失焦且值有变 | input(如果变了)→ blur → change |
| 聚焦子输入框 | 容器上只听得见 focusin,听不见 focus |
| 提交表单 | 各控件 change 收尾 → submit |
表里最难记的是 change 的位置:它排在 blur 之后,因为要等"确认不再改了"才结算。排错时拿这张表对时间线,多数"回调顺序不对"的困惑当场可解。
三大族的事件名与原生完全一致,on('input', fn) 与 addEventListener('input', fn) 是同一件事的两种写法。差异有两处:其一,jQuery 的 hover、toggle 这类组合糖是方言,原生要自己写两行;其二,which 是 jQuery 的统一键码封装,原生用 event.key、event.code 与 event.button。事件类型本身没有任何翻译成本,这层通用性让"jQuery 学的事件知识"可以整体平移到原生世界。
mouseenter 与 mouseleave;mouseover 与 mouseout 会跨进子元素反复触发。keypress 已废弃;按键即时响应用 keydown,实时监值用 input,失焦确认用 change。input,这是它取代键盘事件做校验的理由。submit 并 preventDefault;委托场景的焦点监听选 focusin 与 focusout。change 在失焦之后才触发,把它排进时间线再看"回调顺序"问题,多数当场破案。三大敏感区盘点完毕,下一节把"绑得越高、管得越宽"推到极致:一根听诊器管全页的委托术。