☰
CKEditor 4 Copy Formatting 插件表格场景手动测试全解:从操作步骤到源码实现
2026/10/16 6:16:04 网站建设 项目流程
  • 前端
  • UI组件

【免费下载链接】ckeditor4

The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.

项目地址:https://gitcode.com/gh_mirrors/ck/ckeditor4
点击查看免费下载

导读

本文围绕 CKEditor 4 官方仓库中的手动测试用例 tests/plugins/copyformatting/manual/tables.md 展开,系统讲解 Copy Formatting(格式刷)插件在表格(table)这一特殊上下文中的行为验证方法,并结合 plugins/copyformatting/plugin.js 源码、tests/plugins/copyformatting/下的自动化测试以及 styles/copyformatting.css 样式文件,深入剖析"单元格样式隔离、表头样式、混合选区、表格结构不被破坏"背后的实现原理。读完本文,你将能够独立运行这套手动测试,理解表格上下文样式复制/粘贴的底层机制,并学会通过copyFormatting_allowedContexts、copyFormatting_allowRules等配置精确控制插件行为。

一、测试用例定位:tables.md 在仓库中的角色

tables.md是 Bender 测试框架下的一个**手动测试(manual test)**用例,与同目录的tables.html配套使用。其文件头部的元数据声明了运行环境:

@bender-ui: collapsed @bender-tags: bug, copyformatting, 4.6.0 @bender-ckeditor-plugins: copyformatting, toolbar, wysiwygarea, floatingspace, elementspath, table, tabletools, undo
  • @bender-ui: collapsed:手动测试页面以折叠的 UI 方式呈现,便于测试人员专注操作;
  • @bender-tags: bug, copyformatting, 4.6.0:该用例自 Copy Formatting 功能引入的版本(4.6.0)起即被用于回归验证;
  • @bender-ckeditor-plugins:声明了测试页面需要加载的插件集合,其中copyformatting、table、tabletools是表格场景的核心依赖,wysiwygarea、floatingspace、elementspath、toolbar负责编辑器框架与界面,undo用于验证撤销/重做集成。

整个tests/plugins/copyformatting/manual/目录还包含lists.md、keystroke.md、sticky.md、allowedcontexts.md、links.md、undointegration.md等二十余个用例,分别覆盖列表、快捷键、粘性模式、上下文限制、链接、撤销集成等维度;tables.md专门聚焦表格这一最容易出问题的结构。

二、测试准备:理解配套的 tables.html 页面

手动测试的"操作台"是 tests/plugins/copyformatting/manual/tables.html。页面创建了两个编辑器实例:

<script> CKEDITOR.replace( 'editor1', { allowedContent: true, extraPlugins: 'sourcearea' } ); CKEDITOR.inline( 'editor2', { allowedContent: true, extraPlugins: 'dialog,sourcedialog' } ); </script>
  • editor1是经典(classic)编辑器(CKEDITOR.replace,基于wysiwygareaiframe);
  • editor2是行内(inline)编辑器(CKEDITOR.inline)。

两个编辑器配置了allowedContent: true(关闭 ACF 内容过滤限制),确保测试时样式不会被过滤器拦截。页面内的表格内容经过精心构造,覆盖了大量样式组合,是验证插件行为的"富样式样本":

  1. 带表头的复杂表格:包含<thead>与<th scope="col">,表头单元格内嵌多层<span>,组合了字体族(Comic Sans MS)、字号(20px)、斜体、加粗、下划线、文字颜色(#DAA520)与背景色(#00FF00),用于验证"表头样式是否被正确复制/隔离";
  2. 富样式数据单元格:包含水平对齐(center)、垂直对齐(top)、word wrap(nowrap)、背景色(orange)、边框色(plum)等,用于验证"单元格级 CSS 属性是否只作用到单元格";
  3. 文字级样式样本:背景色 + 文字颜色 + 字体(Times New Roman)+ 字号(26px)嵌套组合,用于验证"文本内联样式与单元格样式是否被区分对待";
  4. 表格级属性:align="center"、border="5"、width="90%"、border-collapse: collapse、dir="ltr",以及另一张table-layout: fixed; width: 500px的固定宽度表格,用于验证"表格结构属性是否被误改"。

三、核心操作步骤(继承原文档 Procedure)

原文档给出的操作步骤非常简洁,但作为手动测试,其"玩转"(Play with tables)背后隐含着一套标准操作流。结合copyformatting插件的交互模型(工具栏按钮 + 鼠标点击 + 快捷键),完整步骤如下:

  1. 在editor1(经典编辑器)中,将光标放入任意带样式的文本/单元格;
  2. 点击工具栏中的Copy Formatting按钮(或按Ctrl + Shift + C)复制格式,此时按钮进入"按下(On)"状态,光标变为插件专用光标;
  3. 移动光标到目标位置(可以是另一个单元格、表头、表格外的段落),点击鼠标左键应用格式;也可以先框选一段文本再应用;
  4. 如需连续粘贴,双击Copy Formatting 按钮进入 sticky(粘性)模式,可反复点击多处;再次点击按钮或按Esc退出;
  5. 在editor2(行内编辑器)中重复以上操作,验证两种编辑器形态下行为一致;
  6. 结合elementspath(元素路径)面板观察当前光标所处的 DOM 层级,便于判断样式实际作用到了哪个元素。

四、Things to check:四个检查点逐条解读

原文档列出的四个检查点,正是表格场景最容易出 Bug 的四个维度,每一条背后都有明确的源码逻辑支撑。

检查点 1:混合上下文(选区跨段落与表格)下样式正确应用

Styles are applied correctly even in the mixed context (e.g. selection begins in a paragraph and ends in a table).

当选区起点在普通段落、终点落在表格内部时,插件需要把样式同时应用到两处。插件先通过_determineContext( range )判断选区所属上下文(见 plugin.js):

_determineContext: function( range ) { function detect( query ) { /* 用 walker 遍历 range 内节点,检测祖先 */ } if ( detect( { ul: 1, ol: 1 } ) ) { return 'list'; } else if ( detect( 'table' ) ) { return 'table'; } else { return 'text'; } }

随后在applyFormatting事件监听器中按上下文分发(plugin.js):

editor.copyFormatting.on( 'applyFormatting', function( evt ) { var context = plugin._determineContext( evt.data.range ); if ( context === 'list' && editor.copyFormatting._isContextAllowed( 'list' ) ) { plugin._applyStylesToListContext( evt.editor, evt.data.range, evt.data.styles ); } else if ( context === 'table' && editor.copyFormatting._isContextAllowed( 'table' ) ) { plugin._applyStylesToTableContext( evt.editor, evt.data.range, evt.data.styles ); } else if ( editor.copyFormatting._isContextAllowed( 'text' ) ) { plugin._applyStylesToTextContext( evt.editor, evt.data.range, evt.data.styles ); } }, null, null, 999 );

注意该监听器的优先级为 999,晚于默认优先级(10)的"先清除旧样式"监听器执行。_applyStylesToTableContext内部对不属于表格专属元素的样式(如b、span)会回退调用_applyStylesToTextContext,从而保证混合选区中段落部分也能获得样式。

自动化测试 tests/plugins/copyformatting/applyformat.js 的test applyFormat on mixed context with table styles用例使用选区<p>Apply for{mat</p><table><tr><td>Maybe h}ere</td></tr></table>精确验证了这一行为:断言表格单元格(td)内应用了 4 个样式、段落(p)内应用了 1 个样式,且td上text-decoration: underline生效。

检查点 2:单元格与表头样式只作用于单元格/表头

Styles from table cells (background color) and headings (background color and color) are applied only to table cells or headings. Mind that bold font in headings is a part of default browser's styles and won't be copied to the table cells.

这一条揭示了两个层面的行为:

(a)样式提取的"元素定位"。复制格式时,copyFormatting命令调用_extractStylesFromElement(plugin.js)从光标所在元素向上逐级提取样式,但遇到breakOnElements中的元素就停止向上:

breakOnElements: [ 'ul', 'ol', 'table' ],

也就是说,从单元格内提取样式时会一直走到<table>边界为止,不会把表格外的段落/文档级样式也一并抓进来。提取时通过_convertElementToStyleDef把元素转换为CKEDITOR.STYLE_INLINE类型的样式定义,并排除excludedAttributes中的无关属性:

excludedAttributes: [ 'id', 'style', 'href', 'data-cke-saved-href', 'dir' ],

(b)样式应用的"目标映射"。应用时,_applyStylesToTableContext(plugin.js)按来源元素的类型决定目标:

if ( indexOf( [ 'table', 'tr' ], style.element ) !== -1 ) { getNodeAndApplyCmd( range, style.element, function( currentNode ) { style.applyToObject( currentNode ); } ); } else if ( indexOf( [ 'td', 'th' ], style.element ) !== -1 ) { getNodeAndApplyCmd( range, { td: 1, th: 1 }, function( currentNode ) { applyToTableCell( currentNode, style ); } ); } else if ( indexOf( [ 'thead', 'tbody' ], style.element ) !== -1 ) { // ... 同样落到单元格 } else { CKEDITOR.plugins.copyformatting._applyStylesToTextContext( editor, range, [ style ] ); }

其中applyToTableCell有一个关键细节:如果目标单元格标签与样式来源标签不同(如td样式应用到th),会重建样式定义并把 element 改为目标单元格的标签再应用,这正是"表头背景色/文字色只作用于表头"的实现基础。自动化测试test applyFormat on table context with table styles (within thead)(applyformat.js)验证了 thead 内th上样式的正确应用。

关于"表头加粗"的提示:<th>的font-weight: bold属于浏览器默认样式,并非显式的内联样式,因此 Copy Formatting 复制的是内联样式属性,不会把浏览器默认加粗"复制"到普通单元格——这正是检查点要求测试人员留意的语义边界。

检查点 3:只有内联样式被应用到文本

Only inline styles are applied to the text.

Copy Formatting 提取的所有样式定义统一为type: CKEDITOR.STYLE_INLINE(见_convertElementToStyleDef的注释与实现)。应用时,属于块级边界的元素会被转成内联<span>再应用:

inlineBoundary: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div' ],
_filterStyles: function( styles ) { // 把 h1..h6/p/div 等块级元素名改为 'span' if ( CKEDITOR.tools.indexOf( CKEDITOR.plugins.copyformatting.inlineBoundary, styleDef.element ) !== -1 ) { styleDef.element = styles[ i ].element = 'span'; } // 过滤掉空的 span if ( styleDef.element === 'span' && isEmpty( styleDef.attributes ) && isEmpty( styleDef.styles ) ) { continue; } }

这也解释了 tables.html 样本中为何把"背景色 + 文字色 + 字体 + 字号"全部用<span>嵌套表达——它们是标准的内联样式载体,复制/应用后以span style="..."的形式落在目标文本上,而不会改变表格的块级结构。

检查点 4:应用样式后表格不被拆成两半

The table is not splitted into two after applying styles.

这是表格场景最经典的回归点。源码中有两处机制共同保障:

  • 应用阶段使用书签(bookmark)保护选区:_applyStylesToTableContext中对每个样式应用前后都执行bkm = range.createBookmark(); ... range.moveToBookmark( bkm );,并在注释中明确说明"防止表格的怪异行为(例如样式应用到所有单元格而不是仅选中的单元格)"。_applyStylesToListContext同样如此。
  • 清除旧样式阶段保留表格元素本身:preservedElements中包含了全部表格相关元素:
preservedElements: [ 'ul', 'ol', 'li', 'td', 'th', 'tr', 'thead', 'tbody', 'table' ],

在"先清除旧样式"的监听器(plugin.js)中,如果旧样式来源元素在preservedElements之列,则调用_removeStylesFromElementInRange只剥离属性、不删除元素(如<td style="...">变成<td>),从而保证表格结构完整;对于表格等元素还启用了stopOnFirst只处理最外层,避免连锁破坏。

五、配套自动化测试:把检查点固化为断言

手动测试之外,仓库还为表格场景提供了多组自动化测试,可作为 CI 回归防线:

  • tests/plugins/copyformatting/applyformat.js:覆盖"表格上下文应用表格样式""thead 内 th 样式应用""段落与表格混合选区"等场景,直接调用CKEDITOR.plugins.copyformatting._applyFormat并断言最终 DOM 状态;
  • tests/plugins/copyformatting/applyformatcontext.js:验证copyFormatting_allowedContexts配置对text/list/table三种上下文的开关行为。例如配置[ 'text' ]时,表格内应用样式的结果为空([]),而配置[ 'text', 'table' ]时表格上下文正常生效,并且测试还覆盖了传入非法值(如100)时的安全兜底;
  • tests/plugins/copyformatting/undointegration.js 与 tests/plugins/copyformatting/manual/undointegration.md:验证频繁移动选区后 Undo/Redo 按钮状态不被破坏,保证格式刷操作可被撤销;
  • tests/plugins/copyformatting/_helpers/tools.js:提供testApplyingFormat、fixHtml等公共断言辅助函数,其中fixHtml还处理了 Safari/IE8 选区符号差异。

测试通过 Bender 框架运行(配置见 bender.js 与 bender-runner.config.json,其中定义了 Bender 服务端口 9001、runner 端口 9002 及各平台浏览器列表)。手动测试用例则由manualBrowsers机制承载,测试人员启动 Bender 服务后即可在浏览器中逐条执行。

六、可配置项:按需裁剪表格场景行为

tables.md场景中表格上下文默认完全可用,是因为插件默认配置copyFormatting_allowedContexts = true(见 plugin.js)。如需定制,可在编辑器配置中覆盖以下选项:

配置项默认值作用示例
copyFormatting_allowedContextstrue允许的上下文,可选'text'、'list'、'table',设为数组即只允许列出的上下文config.copyFormatting_allowedContexts = [ 'text', 'table' ];
copyFormatting_allowRules'b s u i em strong span p div td th ol ul li(*)[*]{*}'允许提取样式的元素规则(ACF 语法),设为true关闭过滤config.copyFormatting_allowRules = 'span(*)[*]{*}';
copyFormatting_disallowRules'*data-cke-widget*,data-widget*,data-cke-realelement'明确禁止提取样式的元素(如 widget)config.copyFormatting_disallowRules = 'span(important)';
copyFormatting_keystrokeCopyCtrl + Shift + C复制格式快捷键,设为false关闭config.copyFormatting_keystrokeCopy = CKEDITOR.CTRL + CKEDITOR.SHIFT + 66;
copyFormatting_keystrokePasteCtrl + Shift + V应用格式快捷键config.copyFormatting_keystrokePaste = false;
copyFormatting_outerCursortrue激活格式刷时是否在整个页面显示"禁用"光标(提示当前悬停位置不可应用格式)config.copyFormatting_outerCursor = false;

其中copyFormatting_allowRules/copyFormatting_disallowRules通过State构造器构建的CKEDITOR.filter实例生效(plugin.js),并在extractFormatting事件中逐一校验(editor.copyFormatting.filter.check( new CKEDITOR.style( style ), true, true ),见 plugin.js)——不符合规则的样式不会进入复制结果。

七、运行与体验建议

要亲身体验这套表格测试,可按以下方式在仓库内运行:

  1. 安装依赖:npm install(仓库 package.json 的 devDependencies 中包含benderjs系列工具);
  2. 启动 Bender 服务(端口配置见 bender-runner.config.json);
  3. 在浏览器中打开手动测试入口,定位到tests/plugins/copyformatting/manual/tables.html页面;
  4. 按照第三节的操作步骤在两个编辑器中反复复制/粘贴表格样式,逐条核对第四节四个检查点。

操作时注意观察两类细节:一是elementspath 面板,它实时显示光标所在元素链(如body > table > tbody > tr > td),帮助你确认样式落点;二是光标形态变化——激活格式刷后,styles/copyformatting.css 会为.cke_copyformatting_active应用专用光标cursor.svg,并用!important覆盖 Table Resize 插件的内联光标样式(cke_copyformatting_tableresize_cursor),这是插件在表格区域交互时特意处理过的细节。同时插件为无碍访问提供了屏幕阅读器通知(_putScreenReaderMessage),在复制/应用/取消时播报对应文案(中文文案见 plugins/copyformatting/lang/zh-cn.js,如"格式已复制""格式已应用")。

总结

tables.md看似只是一份简短的手动测试清单,但它浓缩了 Copy Formatting 插件在表格上下文中的全部关键行为契约:混合选区、单元格/表头样式隔离、纯内联样式、结构完整性。配合 plugins/copyformatting/plugin.js 中_determineContext、_applyStylesToTableContext、breakOnElements、preservedElements等实现细节与 applyformat.js、applyformatcontext.js 的自动化断言,开发者既能用手动流程复现问题,也能从源码层面理解每个检查点"为什么能通过",从而在集成 Copy Formatting 时准确预测其在表格场景下的行为,并借助copyFormatting_allowedContexts等配置按需裁剪功能范围。

  • 前端
  • UI组件

【免费下载链接】ckeditor4

The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.

项目地址:https://gitcode.com/gh_mirrors/ck/ckeditor4
点击查看免费下载
上一篇:RevokeMsgPatcher完整指南:PC版微信/QQ/TIM一键防撤回补丁
下一篇:NCM转MP3教程:ncmdump三步无损解开网易云加密音乐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询