CKEditor 4 全选删除后元素残留问题深度解析:bogus `<br>` 填充机制与 Enter 模式兼容性
前端UI组件【免费下载链接】ckeditor4The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.项目地址https://gitcode.com/gh_mirrors/ck/ckeditor4点击查看免费下载本文围绕 CKEditor 4 仓库中的手动测试用例 notwantedelementsonselectallandbackspace.md 展开深入剖析全选CtrlA后按 Backspace / Delete 删除全部内容这一经典场景下的 DOM 残留问题为什么small等行内元素不应残留、为什么删除后必须补一个 bogusbr填充节点以及不同 Enter 模式P / DIV / BR与无块内联编辑器下各自应有的行为。读完本文你将掌握该测试用例的完整操作与预期结果、底层按键处理源码core/editable.js的调用链、bogus 填充节点needsBrFiller的判定逻辑以及如何用 Bender 手动测试框架复现与验证该缺陷修复。用例背景一个典型的全选删除回归缺陷在富文本编辑器中全选 → 删除是最基础也最容易出 bug 的用户操作。编辑器需要保证删除后内容区只保留符合当前 Enter 模式的块元素p、div或br而不得残留之前被选中的行内元素如small、b、i等同时为了让光标能正常显示与移动空块内还必须存在一个填充节点bogus filler通常是一个br。该手动测试用例正是为了验证 CKEditor 4.5.0 中修复的这样一个 bug在h1 p结构下全选并删除后psmallSource/small/p中的small不应该出现在结果中而一个 bogusbr应当被补上。相关修复记录可见于 CHANGES.md如 #13142[Edge] Fixed:CtrlAand thenBackspaceresult in an emptydivelement说明该问题在 Edge 浏览器上曾导致删除后残留空div属于跨浏览器的回归类缺陷。用例文档逐句解读文档本体位于 notwantedelementsonselectallandbackspace.md全文仅 11 行由 Bender 手动测试元数据与操作步骤构成bender-tags: 4.5.0, bug bender-ui: collapsed bender-ckeditor-plugins: format, stylescombo, toolbar, wysiwygarea, undobender-tags标注该用例从 4.5.0 版本开始引入属于 bug 回归类测试bugbender-ui: collapsedUI 折叠模式测试页面以最小化界面呈现聚焦编辑区本身bender-ckeditor-plugins声明运行所需插件format、stylescombo、toolbar、wysiwygarea、undo——其中format负责块级格式如h1stylescombo提供样式下拉能产生small这类行内样式undo用于快照回退wysiwygarea提供经典 iframe 编辑区。操作步骤共三步聚焦编辑器Focus editor使用快捷键全选全部文本Select whole text using shortcut即 CtrlA按下Backspace或Delete。预期结果原文为There should be nosmallelement in result and bogusbrshould be present. Please note: There might be either anh1element or apin an editable.即结果中不得出现small元素且应当存在 bogusbr同时允许编辑区内最终保留h1或p之一不同浏览器对删除后保留哪个块元素的行为有差异属于允许范围。测试载体 HTML 的构造与文档配套的 notwantedelementsonselectallandbackspace.html 给出了具体的复现内容div ideditor h1Apollo 11/h1psmallSource/small/p /div p stylefont-family: monospace;strongResult:/strongspan idresult/span/p script ( function() { var editor CKEDITOR.replace( editor ), result CKEDITOR.document.getById( result ); editor.on( change, function() { setTimeout( function() { result.setText( CKEDITOR.instances.editor.editable().getHtml() ); }, 10 ); } ); }() ); /script该页面包含两个关键设计复现结构h1块级标题psmallSource/small/p段落内包裹行内小号文字正是formatstylescombo两个插件能生成的典型内容。全选删除后若实现有缺陷small可能残留在空块中实时回显监听编辑器change事件延迟 10ms 后将editable().getHtml()写入页面上的#result区域方便测试人员肉眼核对删除后的 DOM 结果。扩展用例Edge 变体与四种 Enter 模式针对同源问题仓库还提供了更全面的变体用例 notwantedelementsonselectallandbackspaceedge.md标记为4.5.4, bug, trac13142专门覆盖 Edge 浏览器对应 CHANGES.md 中的 #13142 修复。操作矩阵该用例要求对每种编辑器实例逐一执行先创建三类内容之一空段落、单段落单行文本、多段落文本用 CtrlA 全选分别按下Backspace、Delete、字母数字键、Enter键、或直接开始输入文字反复执行上述组合。预期结果编辑区内只应包含各自 Enter 模式所特有的元素——即 Enter 模式为 P 时只留p为 DIV 时只留div为 BR 时以br分行。文档还给出了三条注意事项p元素以绿色边框显示、div元素以红色边框显示便于肉眼区分样式见 _assets/styles.css在非无块内联编辑器中Edge 下可能出现div闪烁、IE 下可能出现p闪烁已知缺陷全选后按Enter会额外产生多余段落这是文档明确承认的既有 bug测试时需区分对待。四个编辑实例的配置配套页面 notwantedelementsonselectallandbackspaceedge.html 一次构建了四种形态的编辑器script CKEDITOR.disableAutoInline true; /scripteditor0h1 contenteditabletrue无块内联编辑器blockless inline editor通过CKEDITOR.inline( editor0, { plugins: toolbar,basicstyles,floatingspace,undo,format,stylescombo,enterkey } )初始化editor1enterMode CKEDITOR.ENTER_P经典p模式textarea 初始化editor2enterMode CKEDITOR.ENTER_DIVdiv模式editor3enterMode CKEDITOR.ENTER_BRbr模式。公共配置cfg为var cfg { contentsCss: %TEST_DIR%/_assets/styles.css, extraPlugins: wysiwygarea,sourcearea,htmlwriter,entities,toolbar,elementspath,undo,clipboard,format,basicstyles,enterkey,div };其中%TEST_DIR%是 Bender 测试框架注入的占位符指向测试用例所在目录enterkey插件负责 Enter 键的块拆分逻辑div插件是ENTER_DIV模式所依赖的插件。源码级原理Backspace / Delete 按键如何处理回到核心实现 core/editable.js编辑区挂载了一整套按键拦截逻辑。backspaceOrDelete将键码 8Backspace与 46Delete统一处理core/editable.jsvar backspaceOrDelete { 8: 1, 46: 1 };随后通过attachListener( editor, key, ... )拦截按键core/editable.js核心流程为只读保护编辑器处于readOnly时直接放行返回且会主动把 Backspace 键标记为阻塞键以防浏览器历史导航core/editable.js空选区保护若sel.getRanges().length 0无有效范围直接返回避免读取空 range 的路径对应 #13096、#457全选对象删除当选区完全覆盖表格或列表时走整块删除分支isListOrTableInSelection/getSelectedTableList删除前先editor.fire( saveSnapshot )建立撤销快照删除后再次saveSnapshot形成可回退的 undo 区间折叠选区特例range.collapsed时处理 Backspace/Delete 位于表格边界等特殊场景例如光标位于空块且紧邻table时删除该空块并把光标移动到表格单元格起始/末尾对应 #6217返回!isHandled若 CKEditor 已处理该按键则返回false阻止浏览器默认行为否则放行交给浏览器。这套逻辑保证了全选 Backspace/Delete场景下表格、列表等结构性内容能够被完整移除而非只删除文本节点。删除后如何补回 bogusbr测试预期中的bogusbrshould be present由填充节点机制保障。定义在 core/env.jsenv.needsBrFiller env.gecko || env.webkit || ( env.ie version 10 );即 Firefox、WebKit/Blink 系浏览器以及 IE11 需要br填充这些浏览器中空块若不包含内容则无法显示光标其余老 IE 则改用nbsp;填充。生成侧CKEDITOR.dom.element#appendBogus( force )core/dom/element.js会先检查最后一个有效子节点是否已是br否则创建并追加一个brFirefox 下附带type_moz属性判定侧CKEDITOR.dom.walker.bogus()core/dom/walker.js用于判断节点是否为填充节点——needsBrFiller为真时认br否则认末尾为nbsp;的文本节点。针对全选删除后编辑区被清空的场景core/editable.js 还有一段浏览器特判IE11 的无块内联编辑器editor.blockless CKEDITOR.env.ie CKEDITOR.env.needsBrFiller在 keyup 时若检测到 Backspace/Delete 后编辑区已无有效内容会调用this.appendBogus()补br并把光标设置到该填充节点之前因为 IE 倾向于把光标放到填充节点之后。另外针对 WebKit/Blink 在 Backspace/Delete 合并块时失控的问题core/editable.js 注册了优先级 100 的key监听器在删除后调用mergeBlocksCollapsedSelection/mergeBlocksNonCollapsedSelection主动合并块、滚动光标到新位置并保存快照——这正是删除后编辑区只剩一个p或h1这一预期结果背后的实现支撑。Enter 模式与无块编辑器的约束为什么预期结果会说可能是h1也可能是p这取决于编辑器的 Enter 模式配置。在 core/editor.js 中config.enterMode与config.shiftEnterMode会被校验后存入editor.enterMode与editor.activeEnterMode而validateEnterMode有一条硬性规则core/editor.jsfunction validateEnterMode( editor, enterMode ) { return editor.blockless ? CKEDITOR.ENTER_BR : enterMode; }无块blockless内联编辑器强制使用ENTER_BR模式。所谓无块是指内联编辑元素本身不是块级容器core/editor.jseditor.blockless editor.elementMode CKEDITOR.ELEMENT_MODE_INLINE ? !( editor.element.is( textarea ) || CKEDITOR.dtd[ editor.element.getName() ].p ) : false;即内联模式下若宿主元素如h1的 DTD 中不含p则视为无块编辑器。这解释了 edge 变体用例中editor0为什么是h1——在无块编辑器里删除全部内容后不能生成p或div只能以br承载光标。自动化测试如何验证同一行为手动测试之外仓库还提供了自动化按键测试 keystrokes.js 覆盖同一类删除行为。它以bender.editor配置autoParagraph: false, allowedContent: true起步通过assertKeystroke( key, keyModifiers, input, output, msg )断言用bot.setHtmlWithSelection( input )注入带选区标记的 HTML[、]表示选区^表示光标触发editable().fire( keydown, ... )模拟按键并通过监听key事件判断该按键是否被 CKEditor 处理用bender.tools.getHtmlWithSelection( ed )比对最终 DOM 与选区位置。典型的断言包括全选表格单元格内容后按 Delete 应只剩光标table...td[foo]/td.../table→^、全选列表内容后删除、以及无选区时按 Del/Backspace 不应抛错#13096。这些断言本质上与手动用例验证的是同一条删除路径。配套工具 tests/_benderjs/ckeditor/static/tools.js 中还定义了填充节点的专有标记表示可能有 bogusbr、!表示必须有 bogusbr、表示可能为br或nbsp;取决于needsBrFiller。自动化测试正是借助这些标记在跨浏览器环境下对删除后应存在 bogusbr这一预期做正则化断言。如何运行与排查两个手动用例都位于 tests/core/editable/manual 目录由 Bender 测试框架驱动。项目根目录的 bender.js 定义了 Bender 配置browsers、plugins、tests 分组等bender-*注释即被框架解析的用例元数据。运行方式安装依赖执行npm installbenderjs系列位于 package.json 的 devDependencies启动 Bender 服务并运行手动测试npx bender runbender.js中配置的startBrowser指定默认浏览器在浏览器中打开对应的手动测试页面按文档步骤操作并核对#result回显区。排查全选删除后元素残留问题时可依次核对检查项依据删除后是否存在small等行内元素若存在说明删除路径未正确移除行内包装节点重点复查 core/editable.js 的全选对象删除分支空块内是否补了 bogusbr核对env.needsBrFillercore/env.js与appendBoguscore/dom/element.js是否生效删除后保留的是p、div还是br核对当前实例的enterModecore/editor.js无块编辑器恒为ENTER_BREdge/WebKit 下是否残留空div对应 #13142 修复验证 WebKit 块合并逻辑core/editable.js小结notwantedelementsonselectallandbackspace系列手动用例虽只有寥寥数行却是 CKEditor 4 编辑核心core/editable.js中全选删除路径的真实缩影它把不得残留行内元素必须补 bogusbr结果元素应符合 Enter 模式这三条硬性约束固化为可复现的验收步骤。结合 edge 变体对四种 Enter 模式的矩阵化覆盖以及 keystrokes.js 的自动化断言读者可以完整理解从用户按键到 DOM 收敛的整条链路——这也是排查任何删除后内容异常类问题的起点。赞分享前端UI组件【免费下载链接】ckeditor4The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.项目地址https://gitcode.com/gh_mirrors/ck/ckeditor4点击查看免费下载相关推荐卸载后数据残留Sandboxie Plus默认沙箱删除机制深度解析卸载后数据残留Sandboxie Plus默认沙箱删除机制深度解析 在网络安全日益重要的今天沙箱技术已成为保护系统免受恶意软件侵害的关键工具。Sandbox应用安全虚拟化桌面应用Wayback Machine 网页存档工具终极指南从消失的网页到永久的数字记忆Wayback Machine 网页存档工具终极指南从消失的网页到永久的数字记忆 您是否曾经遇到过这样的情况昨天还能访问的重要资料页面今天却变成了404错前端浏览器控制上一篇Shardeum Network Account Compare多环境网络账户配置对比 CLI 工具实战指南下一篇Data Engineering Zoomcamp在 Linux 上安装 Spark 4.x 与 PySpark 的完整实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

HoloCubic 固件中的 LVGL 消息框实战:lv_msgbox 创建、事件处理与模态对话框实现

HoloCubic 固件中的 LVGL 消息框实战:lv_msgbox 创建、事件处理与模态对话框实现

嵌入式物联网智能硬件嵌入式GUI硬件开发 【免费下载链接】HoloCubic 带网络功能的伪全息透明显示桌面站 项目地址: https://gitcode.com/gh_mirrors/ho/HoloCubic 点击查看 免费下载 本文以 HoloCubic 开源仓库中 LVGL 示例文档 lv_ex_msgbox/index.rst 为核心&…

2026/10/15 21:13:20 阅读更多 →
DA14585 SDK 6.0.22.1401嵌入式开发全链路指南

DA14585 SDK 6.0.22.1401嵌入式开发全链路指南

简介:本资源为Dialog Semiconductor DA14585系列蓝牙低功耗SoC专用SDK开发包(v6.0.22.1401),面向嵌入式IoT开发者、可穿戴设备工程师及BLE固件初学者,用于快速构建低功耗蓝牙应用、协议栈移植与硬件驱动开发。压缩包共…

2026/10/15 21:13:20 阅读更多 →
Profibus DPV1原始报文解析范例与调试实战

Profibus DPV1原始报文解析范例与调试实战

简介:这份资料面向工业自动化现场调试与通信协议开发人员,聚焦Profibus DPV1原始报文与解析报文的对照学习,帮助读者理解主站与从站之间的交互细节。包内共1个docx文档,压缩包约33KB,以文字与报文表格形式呈现&#xf…

2026/10/15 21:12:16 阅读更多 →

最新新闻

Unity Profiler性能分析实战:从真机采集到GC优化完整指南

Unity Profiler性能分析实战:从真机采集到GC优化完整指南

1. 为什么每个Unity开发者都该把Profiler当日常工具做Unity这几年,我见过太多项目在编辑器里跑得飞起,一打包到真机就卡成幻灯片。帧率从90掉到22,发热烫手,玩家骂声一片,团队却找不到瓶颈在哪。这种场景几乎每个中大型…

2026/10/15 22:09:39 阅读更多 →
SolidWorks干涉检查假干涉排查:隐藏实体与压缩的区别及选项设置

SolidWorks干涉检查假干涉排查:隐藏实体与压缩的区别及选项设置

1. 先别急着翻设计树:干涉检查报出的"假干涉"从哪来做整机装配干涉检查的时候,我猜你肯定遇过这种场面:明明把弹簧、密封圈、线束、装饰罩这些零件都隐藏了,图纸看着清爽得很,结果一点"干涉检查"&…

2026/10/15 22:09:39 阅读更多 →
collect.js 严格比较设计指南:全方法默认 `===` 的实现原理与 Laravel 差异对照

collect.js 严格比较设计指南:全方法默认 `===` 的实现原理与 Laravel 差异对照

开发工具 【免费下载链接】collect.js 💎 Convenient and dependency free wrapper for working with arrays and objects 项目地址: https://gitcode.com/gh_mirrors/co/collect.js 点击查看 免费下载 导读 本文围绕 collect.js 的**严格比较&#x…

2026/10/15 22:09:39 阅读更多 →
6D位姿估计综述:RGB-D、点云、RGB三条路线选型与复现避坑指南

6D位姿估计综述:RGB-D、点云、RGB三条路线选型与复现避坑指南

简介:这份资源是面向机器人视觉与三维感知方向学习者、研究者的6D位姿估计综述文献总结,以思维导图形式梳理了Vision-based Robotic Grasping综述的核心脉络,帮助读者快速建立从物体定位、位姿估计到抓取估计的整体认知框架。内容覆盖基于RGB…

2026/10/15 22:09:39 阅读更多 →
memtester内存压力测试实战:从原理到脚本化压测

memtester内存压力测试实战:从原理到脚本化压测

简介:这是一份内存压力测试工具 memtester 4.1.2 的完整源码包,面向 Linux/Unix 系统管理员、运维工程师与底层软硬件开发者,用于检测内存条位翻转、数据丢失、地址错误等不稳定因素,也可作为服务器上线前压力验证和故障排查的有力…

2026/10/15 22:09:39 阅读更多 →
MySQL时间类型选型:DATETIME与TIMESTAMP底层差异与避坑指南

MySQL时间类型选型:DATETIME与TIMESTAMP底层差异与避坑指南

MySQL 的时间类型,尤其是在 DATETIME 和 TIMESTAMP 之间反复纠结的情况,我见过太多了。很多人直到线上出现“时间差8小时”或者“2038年数据写不进去”才发现自己对这两个类型的理解还是靠猜。今天把这俩家伙的底层逻辑、行为差异、性能取舍和选型思路一…

2026/10/15 22:08:39 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →