- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
本手册针对 CKEditor 4 的 Table Resize(表格列宽拖拽调整)插件在窄容器编辑器横向滚动后,列边框上的缩放光标发生错位这一已知缺陷(issue #4889,随 4.20.0 版本修复),给出基于 Bender 手动测试(Manual Test)的完整复现步骤、预期/非预期行为判定标准,并结合插件源码与自动化测试,从"pillar(柱线)缓存失效"的角度剖析缺陷根因与修复机制。读完本文,你将掌握如何定位 Table Resize 的坐标计算核心、理解其滚动场景下的缓存失效问题,并学会用仓库内置的 manual test 与 bender 自动化用例双重验证此类交互缺陷。
一、手册定位:一份"bug 型"手动测试文档
本文所依据的文档是 scrollupdate.md,它是 CKEditor 4 测试体系中典型的manual test(手动测试)说明文档,与同目录下的 scrollupdate.html 页面配套使用。其文档头部的 Bender 指令完整描述了该用例的属性:
@bender-ui: collapsed @bender-tags: bug, 4.20.0, 4889 @bender-ckeditor-plugins: wysiwygarea, toolbar, table, tableresize, tabletools, floatingspace@bender-ui: collapsed:手动测试页面以折叠(collapsed)方式嵌入 Bender 手动测试运行器 UI 中,避免页面干扰操作;@bender-tags: bug, 4.20.0, 4889:明确这是一个bug 回归用例,对应 GitHub issue #4889,修复随4.20.0版本发布(见 CHANGES.md 中 "Fixed: Incorrect position of the Table Resize cursor after scrolling the editor horizontally");@bender-ckeditor-plugins:声明该用例需要加载wysiwygarea、toolbar、table、tableresize、tabletools、floatingspace等插件——注意tableresize插件本身在 plugin.js 中声明了requires: 'tabletools',因此tabletools是其依赖项。
这样的文档结构意味着:它不讲解功能如何使用,而是规定了一套可执行的缺陷复现流程。这是 CKEditor 4 质量保障体系中"回归测试必须可复现、可判定"原则的直接体现。
二、缺陷背景:Table Resize 的光标与 pillar 机制
要理解这个 bug,需要先了解 Table Resize 插件的工作方式。在 plugin.js 中,插件通过buildTableColumnPillars()为表格的每一列边框计算一个"pillar"(柱线,即列边框的可拖拽命中区域),其核心字段如下(plugin.js):
pillar = { table: table, index: pillarIndex, x: pillarLeft, // 柱线左边界,基于 getDocumentPosition() 计算 y: pillarPosition.y, width: pillarWidth, // 最小为 3px(Math.max(pillarRight - pillarLeft, 3)) height: pillarHeight, rtl: rtl };pillar 的x/y坐标全部来自CKEDITOR.dom.element.getDocumentPosition()——这是一个相对于文档的绝对坐标,而不是相对于视口的坐标。当鼠标移动时,mousemove处理器通过evt.getPageOffset()取得页面级坐标,再调用getPillarAtPosition()判断鼠标是否落在某个 pillar 范围内(plugin.js)。
为了提高性能,插件把每次计算出的 pillars 缓存到表格的自定义数据_cke_table_pillars上(plugin.js),并只在mouseout/mousedown时通过clearPillarsCache()清除缓存(plugin.js)。
滚动为何导致错位
缺陷的根源在于:pillar 坐标是相对文档的绝对坐标,而getPageOffset()取到的鼠标位置会随着编辑区域滚动而变化。
- 当编辑区域(inline/divarea 容器或 iframe 文档)发生横向滚动后,页面级鼠标坐标已经变化;
- 但缓存的 pillars 中记录的是滚动前计算的文档坐标,未随滚动失效;
- 于是
getPillarAtPosition()命中了一个坐标过期的 pillar,columnResizer.attachTo()据此把缩放条(resizer)定位到错误位置(plugin.js),表现为"缩放光标从列边框上偏移"。
值得注意的是,手动测试文档特别注明了一个细节:"The bug does not occur when dragging the scrollbar."(用鼠标滚轮或触控板手势滚动时出现,拖动滚动条时不出现)。这与浏览器事件模型一致——拖动滚动条不会触发容器/文档的scroll事件流(或鼠标位置计算路径不同),而滚轮/手势滚动才会让编辑器内表格的文档坐标与页面鼠标坐标产生错位。
三、复现步骤(原文档核心操作流程)
以下步骤完整继承自 scrollupdate.md,并结合 scrollupdate.html 的测试页面结构给出补充说明。测试页面包含三种编辑器形态,共 3 个待测实例:
| 编辑器 | 创建方式 | 容器特征 |
|---|---|---|
| Inline editor | CKEDITOR.inline( 'inline' ) | 250px 宽、overflow: auto的可编辑 div |
| div-based editor | CKEDITOR.replace( 'div', { extraPlugins: 'divarea', width: 250 } ) | 250px 宽、overflow: auto的 div 编辑区 |
| iframe-based editor | CKEDITOR.replace( 'iframe', { width: 250 } ) | 250px 宽的标准 iframe 编辑区 |
每个实例内部都是一张style="width:500px"、border="1"的 5 行 4 列表格——即表格宽度(500px)远超容器宽度(250px),必然产生横向滚动条,这是触发本 bug 的前提条件。此外 scrollupdate.html 中还有if ( bender.tools.env.mobile ) { bender.ignore(); },表示移动端环境自动跳过该用例。
操作步骤
- 将编辑器滚动到最右端,再稍微向左回滚一点。
- ⚠️注意:必须使用鼠标滚轮或触控板手势滚动;拖动滚动条无法复现该 bug(原因见上文第二节)。
- 尝试拖拽调整最后一列(将鼠标悬停到最右侧列边框上,出现
col-resize光标后按下拖拽)。 - 对页面上的全部 3 个编辑器(inline、div-based、iframe-based)重复上述过程。
预期结果(Expected)
缩放光标位于正确的位置(即恰好贴合最后一列的边框线),拖拽后列宽调整正常生效。
非预期结果(Unexpected)
缩放光标从列的边框上偏移——由于 pillar 缓存未随滚动失效,resizer 被定位到了滚动前的位置,鼠标指针看起来"悬空"在边框之外,无法准确拖拽最后一列。
四、修复原理:scroll 事件触发 pillar 缓存重置
针对 #4889,plugin.js 中新增了如下修复逻辑:
// Reset pillars position on scroll (#4889). editable.attachListener( listenerTarget, 'scroll', function() { var tables = editable.find( 'table' ).toArray(); CKEDITOR.tools.array.forEach( tables, CKEDITOR.tools.debounce( function( table ) { table.removeCustomData( '_cke_table_pillars' ); }, 200 ) ); } );关键设计有三点:
- 监听目标的选择:
listenerTarget在 init 时被确定为——inline 编辑器用editable本身,经典编辑器用editor.document(plugin.js)。这样无论滚动发生在可编辑容器还是 iframe 文档上,都能捕获到scroll事件。 - 批量失效:滚动发生时,遍历 editable 内的所有表格,逐一移除
_cke_table_pillars缓存数据。下次鼠标移动时,buildTableColumnPillars()会重新按最新文档坐标计算 pillars,光标位置随即恢复正确。 - 防抖(debounce):用
CKEDITOR.tools.debounce( ..., 200 )将清除动作延迟 200ms 合并执行,避免连续滚动造成频繁重算,兼顾正确性与性能。
与此同时,自动化测试 tableresize.js 中也新增了两个断言用例:
test pillars are resetted on scroll (inline):对 inline 编辑器触发editable.fire( 'scroll', {} ),等待 210ms(超过 200ms 防抖窗口)后断言table.getCustomData( '_cke_table_pillars' )为null;test pillars are resetted on scroll (classic):对经典编辑器触发editor.document.fire( 'scroll', {} ),同样断言缓存被清空。
这两个用例与手动测试文档形成了自动化 + 人工双重验证的闭环:自动化用例用 mock 事件验证缓存失效行为,手动用例验证真实浏览器滚动交互下的最终用户体验。测试还定义了inlineOverflow/classicOverflow两个带横向溢出场景的编辑器实例(tableresize.js),与 manual test 页面的窄容器设置相互印证。
五、如何运行该手动测试
本仓库使用 Bender.js 作为测试运行框架(其tests配置中Plugins分组以tests/为 basePath 覆盖plugins/**,见 bender.js),依赖由 package.json 中的benderjs系列包提供。
- 运行全部手动测试:
bender run manual(手动测试文档通过@bender-ui等标记被 Bender 识别为 manual test 分组); - 定位本用例:运行后选择
Plugins分组下的tableresize,即可在页面列表中找到scrollupdate用例,按第三节的步骤逐条验证。
由于仓库只读,本文仅介绍查看与运行方式;实际验证时,请在你的开发副本中安装依赖并启动 Bender 服务器,然后按上述流程在真实浏览器中操作。
六、小结:从一份 manual test 学到什么
通过这份 scrollupdate.md,可以提炼出三条可复用的工程经验:
- "坐标缓存类"交互缺陷的复现要点:凡是基于文档绝对坐标 + 缓存的交互(pillar、tooltip、浮动面板等),都必须验证滚动、缩放等位移场景;且复现方式(滚轮 vs 拖滚动条)可能影响 bug 是否出现,测试文档必须写清楚;
- 修复的标准姿势:监听
scroll事件 → 失效相关缓存 → 用 debounce 控制频率,是 CKEditor 4 中处理"滚动导致缓存坐标过期"类问题的通用模式(见 plugin.js); - 回归测试的双轨验证:manual test(真实交互体验)+ 自动化断言(缓存状态变化)组合,既保证"用户视角正确",又保证"实现细节正确",这在 tableresize.js 中体现得尤为清晰。
如果需要继续深入 Table Resize 插件,可阅读同目录下的其他手动测试,如 pillar.md(pillar 高度与整表等高)、mergedcells.md(合并单元格边界命中)、maximizeddivarea.md(最大化后缩放)等,它们共同覆盖了该插件的坐标计算与交互边界场景。
- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
相关推荐
CKEditor 4 Copy Formatting 与 Table Resize 光标冲突修复:从手动测试到源码原理
CKEditor 4 Copy Formatting 与 Table Resize 光标冲突修复:从手动测试到源码原理 Copy Formatting(复制格式
前端UI组件CKEditor 4 Table Resize 插件合并单元格(Rowspan)缩放修复解析与手动回归测试指南
CKEditor 4 Table Resize 插件合并单元格(Rowspan)缩放修复解析与手动回归测试指南 本篇技术指南以 CKEditor 4 仓库中的手
前端UI组件CKEditor 4 Table Resize 插件:IE 下 border-collapse 表格右边界无法调整(2823)的修复原理与手动验证
CKEditor 4 Table Resize 插件:IE 下 border collapse 表格右边界无法调整( 2823)的修复原理与手动验证 本文以 C
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考