- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
本篇文章基于现代 JavaScript 教程(The Modern JavaScript Tutorial)中文仓库中的「工具提示行为」任务(任务原文)展开,带你完整实现一个基于data-tooltip特性的声明式工具提示(tooltip)模块。你将掌握事件委托(Event Delegation)、mouseover/mouseout事件配对、getBoundingClientRect()坐标定位与窗口边缘越界处理等核心技能,最终写出一个「不熟悉 JavaScript 的人也能直接给任意元素加注释」的通用行为模块。
任务背景:事件委托与"行为"模式
在动手写代码之前,先明确这个任务在教程体系中的位置。它位于「事件委托」章节的练习题部分,对应章节正文(article.md)系统讲解了事件委托的思想:当我们有许多以类似方式处理的元素时,不必为每个元素分配处理程序,而是把单个处理程序放在它们的共同祖先上,在程序中通过event.target判断事件实际发生在哪里。
该章节还提出了著名的**"行为"(behavior)模式**,其结构分为两部分:
- 把自定义特性(attribute)添加到描述行为的元素上;
- 用文档范围级(document-level)的处理程序追踪事件,如果事件发生在具有特定特性的元素上,就执行对应行为。
本章节已给出的行为示例包括data-action(菜单按钮分发)、data-counter(点击自增)和data-toggle-id(点击切换显隐)。本任务要实现的data-tooltip正是这一模式在「鼠标悬停」场景下的典型应用:HTML 作者只需要在元素上写一个特性,悬停提示功能即自动生效,无需编写任何针对单个元素的脚本。
任务需求拆解
任务原文(task.md)给出的需求非常明确,我们逐条落实:
- 当鼠标位于带有
data-tooltip特性的元素上方时,显示工具提示;鼠标移开时隐藏; - 工具提示的内容就是
data-tooltip特性的值,且可以是任意 HTML(例如"HTML<br>tooltip"); - 元素与工具提示之间的距离固定为
5px; - 工具提示应尽量相对于元素水平居中;
- 工具提示不能与窗口边缘交叉:默认显示在元素上方;但如果元素位于页面顶部、上方没有空间,则改到元素下方显示;
- 一次只能显示一个工具提示;
- 必须使用事件委托:只在
document上设置mouseover和mouseout两个处理程序,统一管理所有带data-tooltip的元素; - 本任务假设带
data-tooltip的元素内部只有文本,尚无嵌套标签(嵌套场景是后续进阶任务的内容)。
任务给出的示例 HTML 如下:
<button>.tooltip { position: fixed; padding: 10px 20px; border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; font: italic 14px/1.3 sans-serif; color: #333; background: #fff; box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); }关键点是position: fixed。工具提示是动态创建并直接append到document.body的独立元素,使用fixed定位使其坐标直接相对视口(viewport)计算,恰好与后续getBoundingClientRect()返回的视口坐标系一致,并且在页面滚动时仍能跟随固定位置。解决方案的body特意设置了height: 2000px使页面可滚动,并在页面中加入提示文案Scroll the page to make buttons appear on the top, check if the tooltips show up correctly.,就是为了验证滚动后工具提示的定位依然正确。
第二步:mouseover 处理程序——创建并定位工具提示
let tooltipElem; document.onmouseover = function(event) { let target = event.target; // 如果元素带有>document.onmouseout = function(e) { if (tooltipElem) { tooltipElem.remove(); tooltipElem = null; } };移出带注释元素时,移除当前存在的工具提示节点并将tooltipElem置空。由于模块级变量tooltipElem始终只保存一个工具提示引用,「一次只能显示一个工具提示」的需求天然得到满足——后一次悬停必然先经历上一次的mouseout清理,或直接覆盖旧引用。
一个值得注意的改进:用 addEventListener 替换 onmouseover
解决方案示例直接使用了document.onmouseover赋值。但教程在「行为:计数器」一节(article.md)给出了明确的警告:
当我们将事件处理程序分配给
document对象时,我们应该始终使用addEventListener,而不是document.on<event>,因为后者会引起冲突:新的处理程序会覆盖旧的处理程序。在实际项目中,在document上有许多由代码的不同部分设置的处理程序,这是很正常的。
因此在真实项目中,推荐将解决方案改写为:
document.addEventListener('mouseover', function(event) { let target = event.target; let tooltipHtml = target.dataset.tooltip; if (!tooltipHtml) return; tooltipElem = document.createElement('div'); tooltipElem.className = 'tooltip'; tooltipElem.innerHTML = tooltipHtml; document.body.append(tooltipElem); // ...定位逻辑与上节完全相同... }); document.addEventListener('mouseout', function(event) { if (tooltipElem) { tooltipElem.remove(); tooltipElem = null; } });这样,即使页面上其他脚本也在document上注册了事件处理程序,本模块也不会覆盖它们,多个行为(behavior)可以和平共存——这正是「行为」模式能成为可复用模块的关键保障。
完整可运行示例
将以下内容保存为 HTML 文件即可直接运行验证(该结构即仓库中 solution.view/index.html 的完整形态):
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <style> body { height: 2000px; /* 让页面可滚动,验证滚动后工具提示定位 */ } .tooltip { position: fixed; padding: 10px 20px; border: 1px solid #b3c9ce; border-radius: 4px; text-align: center; font: italic 14px/1.3 sans-serif; color: #333; background: #fff; box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); } </style> </head> <body> <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p> <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p> <button>赞- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
相关推荐
使用事件委托实现嵌套>使用事件委托实现嵌套 data tooltip 提示框:Modern JavaScript Tutorial 实战解析 本指南基于 Modern JavaScr
文档/教程前端Modern JavaScript Tutorial:基于事件委托实现>Modern JavaScript Tutorial:基于事件委托实现 data tooltip 行为式 Tooltip(行为模式实战) 导读 本篇文章围绕 J
文档/教程前端事件委托实战:用 30 行代码实现可排序表格(zh.javascript.info 教程任务解析)
事件委托实战:用 30 行代码实现可排序表格(zh.javascript.info 教程任务解析) 导读 本文基于《现代 JavaScript 教程》(zh.j
文档教程前端