Web Component中Tooltip的显示/隐藏、外部点击隐藏及单显实现
实现带交互逻辑的Tooltip Web Component
需求说明
- 点击按钮切换Tooltip显示/隐藏(原逻辑已实现,优化了DOM选择可靠性)
- 点击按钮或Tooltip以外的区域,自动隐藏所有Tooltip
- 同一时间仅显示一个Tooltip,点击新按钮时自动关闭之前的
完整代码实现
JavaScript 代码
class BuildABox extends HTMLElement { constructor() { super(); // 记录当前正在显示的Tooltip this.activeTooltip = null; // 绑定this指向,确保事件处理函数里能正确访问组件实例 this.toggleTooltip = this.toggleTooltip.bind(this); this.handleOutsideClick = this.handleOutsideClick.bind(this); // 给所有带data-tooltip属性的按钮绑定点击事件 this.querySelectorAll('[data-tooltip]').forEach(button => { button.addEventListener('click', this.toggleTooltip); }); // 监听全局点击事件,处理外部点击隐藏逻辑 document.addEventListener('click', this.handleOutsideClick); } toggleTooltip(e) { e.preventDefault(); // 从当前点击的按钮里找到对应的Tooltip(比原有的lastChild.previousSibling更可靠) const tooltip = e.currentTarget.querySelector('.tooltip'); if (!tooltip) return; // 如果当前Tooltip本来就是显示状态,直接隐藏 if (this.activeTooltip === tooltip) { tooltip.classList.add('tw-invisible'); this.activeTooltip = null; return; } // 先把之前显示的Tooltip关掉 if (this.activeTooltip) { this.activeTooltip.classList.add('tw-invisible'); } // 显示当前的Tooltip,更新激活状态记录 tooltip.classList.remove('tw-invisible'); this.activeTooltip = tooltip; } handleOutsideClick(e) { // 判断点击的地方是不是按钮或者Tooltip内部 const isClickOnButton = e.target.closest('[data-tooltip]'); const isClickOnTooltip = e.target.closest('.tooltip'); // 如果都不是,且有激活的Tooltip,就隐藏它 if (!isClickOnButton && !isClickOnTooltip && this.activeTooltip) { this.activeTooltip.classList.add('tw-invisible'); this.activeTooltip = null; } } // 组件从DOM移除时,清理事件监听,避免内存泄漏 disconnectedCallback() { document.removeEventListener('click', this.handleOutsideClick); this.querySelectorAll('[data-tooltip]').forEach(button => { button.removeEventListener('click', this.toggleTooltip); }); } } customElements.define('build-a-box', BuildABox);
HTML 代码(优化重复ID问题)
注意:如果页面有多个Tooltip按钮,不能重复用id="tooltip",建议改成类名
.tooltip,否则会导致DOM查询出错。修改后的HTML示例:
<button id="info-btn" aria-label="Information" type="button" data-info="{{ block.settings.product_description }}"> <div data-tooltip="tooltip" class="tw-w-12"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-12 h-12" > <path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 01.67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 11-.671-1.34l.041-.022zM12 9a.75.75 0 100-1.5.75.75 0 000 1.5z" clip-rule="evenodd" /> </svg> {% comment %} 这是要显示/隐藏的Tooltip {% endcomment %} <div class="tw-invisible z-50 tw-absolute tw-top-24 tw-max-w-[300px] tw-bg-blue-400 tw-text-white tw-border-graphite tw-border-2 tw-overflow-auto tw-rounded-2xl tw-p-4 tw-mt-1 tooltip"> <p>{{ block.settings.product_description }}</p> </div> </div> </button>
核心逻辑解释
- 单一Tooltip控制:用
this.activeTooltip变量存当前显示的Tooltip,每次点击新按钮时,先关闭之前的,再打开当前的,确保同一时间只有一个Tooltip显示。 - 外部点击隐藏:监听整个文档的点击事件,用
closest()方法判断点击位置是否在按钮或Tooltip范围内,不在就隐藏激活的Tooltip。 - 可靠的DOM选择:替换原有的
lastChild.previousSibling为querySelector('.tooltip'),避免DOM结构里的空格、换行符导致的元素选择错误。 - 内存泄漏防护:在组件被移除时,移除所有绑定的事件监听,防止无用事件占用资源。
内容的提问来源于stack exchange,提问作者Mike Haslam
相关产品推荐
相关产品推荐

