Stimulus控制器实现可编辑区块工具箱显隐及点击交互问题
解决Contenteditable区块与工具箱的焦点冲突问题
需求概述
- 聚焦contenteditable区块时,显示工具箱(移除
d-none类) - 点击工具箱按钮时,为其添加Bootstrap的
active类 - 点击非工具箱/工具栏区域或完全失去焦点时,隐藏工具箱(添加
d-none类)
现有问题
使用Stimulus控制器实现时,focusout事件与工具箱点击存在冲突:点击工具箱按钮时,focusout会先触发并隐藏工具箱,导致按钮的点击交互无法正常完成。
问题原因
点击工具箱按钮时,焦点会从contenteditable区块转移到按钮上,此时contentBlockTarget的focusout事件会立即触发,先将工具箱隐藏,后续的点击事件因为元素已不可见而无法正常执行。
解决方案
1. 修改Stimulus控制器逻辑
调整focusout事件的判断逻辑,确认焦点是否转移到了工具箱或工具栏内部;同时修复工具箱点击事件的无效代码(原代码添加active类后立即移除,等于无操作)。
修改后的Stimulus代码:
connect() { // 聚焦编辑区块时显示工具箱 this.contentBlockTarget.addEventListener("focus", (event) => { this.toolboxTarget.classList.remove("d-none"); }); // 点击工具箱按钮时切换active类 this.toolboxTarget.addEventListener("click", (event) => { this.toolboxTarget.classList.toggle("active"); event.stopPropagation(); }); // 失去焦点时判断是否需要隐藏工具箱 this.contentBlockTarget.addEventListener("focusout", (event) => { const relatedTarget = event.relatedTarget; const toolbar = document.getElementById("toolbar1"); // 检查焦点是否落在工具箱或工具栏内部 const isFocusInToolArea = relatedTarget === this.toolboxTarget || (toolbar && toolbar.contains(relatedTarget)); if (!isFocusInToolArea) { this.toolboxTarget.classList.add("d-none"); this.toolboxTarget.classList.remove("active"); } }); // 监听全局点击,处理点击页面其他区域的情况 document.addEventListener("click", (event) => { const controllerRoot = this.element; const toolbar = document.getElementById("toolbar1"); // 检查点击是否在控制器范围、工具箱或工具栏内 const isClickInTargetArea = controllerRoot.contains(event.target) || event.target === this.toolboxTarget || (toolbar && toolbar.contains(event.target)); if (!isClickInTargetArea) { this.toolboxTarget.classList.add("d-none"); this.toolboxTarget.classList.remove("active"); } }); }
2. 优化HTML结构(可选)
确保工具箱和工具栏的DOM结构被包含在Stimulus控制器的根元素内,方便后续元素范围判断:
.border.border-dark.p-4 .position-relative.w-100{data: {controller: "content", content:{block:{value: true}}}} .position-absolute.bg-white{style: "right: 101%"} %button.d-none.btn.btn-outline-dark{type: "button", data: {content_target: "toolbox", bs:{toggle: "collapse", target: "#toolbar1"}}} %i.bi.bi-hammer #toolbar1.collapse.position-absolute.bottom-100.bg-white.btn-toolbar{role: "toolbar", aria:{label: "Toolbar with button groups"}} .btn-group.me-2{role: "group", aria:{label: "First group"}} %button.btn.btn-outline-dark.active{type: "button"} H2 %button.btn.btn-outline-dark{type: "button"} H3 %button.btn.btn-outline-dark{type: "button"} H4 %button.btn.btn-outline-dark{type: "button"} H5 %button.btn.btn-outline-dark{type: "button"} H6 %button.btn.btn-outline-dark{type: "button"} %i.bi.bi-paragraph %p{contenteditable: true, data: {content_target: "contentBlock"}} This is some text
关键说明
- 通过
event.relatedTarget在focusout事件中判断焦点去向,避免误隐藏工具箱 - 给工具箱点击事件添加
event.stopPropagation(),防止触发全局点击事件的误判 - 全局点击事件覆盖点击页面其他区域的场景,确保交互逻辑完整
内容的提问来源于stack exchange,提问作者Patrick Vellia
相关产品推荐
相关产品推荐

