news 2026/10/17 2:00:05

CKEditor 4 内容过滤如何驱动表格单元格属性对话框:从 allowedContent 手动测试到源码实现剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CKEditor 4 内容过滤如何驱动表格单元格属性对话框:从 allowedContent 手动测试到源码实现剖析
  • 前端
  • 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/tabletools/manual/allowedcontent.md展开,深入剖析其背后的核心机制:allowedContent(内容过滤规则)如何实时控制"单元格属性(Cell Properties)"对话框与右键上下文菜单的可用选项。读完本文,你将掌握 CKEditor 4 内容过滤系统(core/filter.js)与表格工具插件(plugins/tabletools/plugin.js)的联动原理,学会通过配置allowedContent精确裁剪单元格属性编辑能力,并理解为何该手动测试需要反复销毁、重建编辑器。

一、测试场景原文全貌

关联文档tests/plugins/tabletools/manual/allowedcontent.md定义了一个清晰的手动验收场景,其核心内容如下:

测试场景(Test scenario):

  1. 使用复选框(checkboxes)选择允许的单元格属性(allowed cell properties)。
  2. 在编辑器上使用上下文菜单(context menu)打开单元格属性(cell properties)。

期望结果(Expected):

  • 单元格属性对话框中的选项与所勾选的复选框一一匹配。
  • 如果未勾选任何属性,则"单元格属性(Cell Properties)"不会出现在上下文菜单中。

意外情况(Unexpected):

  • 对话框选项与勾选的复选框不一致。

注意事项(Note):

  • 勾选/取消勾选复选框会销毁并创建新的编辑器,因此页面可能出现可见的闪烁(flickering)。

这段描述虽然简短,却浓缩了两个关键技术断言:对话框选项由allowedContent动态裁剪,以及上下文菜单项由过滤器门控。下面逐一拆解。

二、配套页面:复选框如何驱动 allowedContent

该手动测试的实体页面为 tests/plugins/tabletools/manual/allowedcontent.html,页面通过脚本动态生成复选框,并建立了一张"界面文案 → 过滤规则"的映射表:

复选框文案对应过滤规则规则归属
Width(宽度)widthtd的styles
Height(高度)heighttd的styles
Word Wrap(自动换行)white-spacetd的styles
Horizontal Align(水平对齐)text-aligntd的styles
Vertical Align(垂直对齐)vertical-aligntd的styles
Cell Type(单元格类型)th元素本身
Rows Span(行跨度)rowspantd的attributes
Cols Span(列跨度)colspantd的attributes
Background Color(背景颜色)background-colortd的styles
Border Color(边框颜色)border-colortd的styles
Color Dialog(颜色选择对话框)colordialog额外插件(extra plugin)

页面初始化一个基础allowedContent对象:

allowedContent = { p: true, div: true, table: true, thead: true, tbody: true, tr: true, td: { styles: [], attributes: [] } }

td的styles与attributes数组初始为空,之后由复选框的change(IE8 下退化为click)事件驱动增删:

case 'th': if ( state ) { allowedContent.th = true; } else { delete allowedContent.th; } case 'rowspan': case 'colspan': addRemoveRule( rule, state, attributes ); break; default: addRemoveRule( rule, state, styles );

addRemoveRule在数组中增删对应规则项。勾选变化后,页面执行editor.destroy( true )并调用createEditor()用新的allowedContent重建编辑器:

function createEditor() { var config = { allowedContent: allowedContent, plugins: 'wysiwygarea,toolbar,table,tabletools,sourcearea', extraPlugins: [] }; if ( CKEDITOR.document.findOne( 'input[name="colordialog"]' ).$.checked ) { config.extraPlugins.push( 'colordialog' ); } return CKEDITOR.replace( 'editor1', config ); }

值得注意的是,编辑器固定加载table与tabletools插件,并视复选框状态动态追加colordialog插件——这直接呼应了对话框中"背景/边框颜色"字段在安装颜色选择插件后才会多出取色按钮的行为。

三、源码级原理:对话框字段按 requiredContent 逐项过滤

3.1 命令级 allowedContent 白名单

cellProperties命令在 plugins/tabletools/plugin.js 中注册,声明了两层过滤规则:

addCmd( 'cellProperties', new CKEDITOR.dialogCommand( 'cellProperties', createDef( { allowedContent: 'td th{width,height,border-color,background-color,white-space,vertical-align,text-align}[colspan,rowspan]', requiredContent: requiredContent, ... } ) ) );

其中requiredContent数组明确定义了单元格属性能力所需的全部规则:

var requiredContent = [ 'td{width}', 'td{height}', 'td{border-color}', 'td{background-color}', 'td{white-space}', 'td{vertical-align}', 'td{text-align}', 'td[colspan]', 'td[rowspan]', 'th' ];

同时命令还注册了三组contentTransformations,负责兼容旧式属性向新式样式的迁移:

  • background简写样式 →background-color;
  • 旧valign属性 →vertical-align样式;
  • 行级height样式 → 逐个单元格写入cke-row-height自定义属性再回写(对应 #16818 的修复)。

3.2 对话框字段的逐项门控

对话框定义位于 plugins/tabletools/dialogs/tableCell.js,其中每个 UI 字段都声明了自己的requiredContent:

对话框字段对应requiredContent
width / height(尺寸)td{width}/td{height}
wordWrap(自动换行)td{white-space}
hAlign(水平对齐)td{text-align}
vAlign(垂直对齐)td{vertical-align}
cellType(单元格类型)th[scope]
rowSpan / colSpan(跨行/跨列)td[rowspan]/td[colspan]
bgColor(背景颜色)td{background-color}
borderColor(边框颜色)td{border-color}

对话框构建时先对所有字段做一次过滤(tableCell.js):

items = CKEDITOR.tools.array.filter( items, function( item ) { var requiredContent = item.requiredContent, ret; delete item.requiredContent; ret = editor.filter.check( requiredContent ); if ( ret && !item.isSpacer ) { itemsCount++; } return ret; } );

未通过的字段直接从定义中剔除;再依据itemsCount > 5决定对话框采用单列还是双列布局(minWidth随之在 205 与 410 之间切换)。

除此之外,plugins/dialog/plugin.js 中uiElement构造器还有第二道防线:

if ( elementDefinition.requiredContent && !dialog.getParentEditor().filter.check( elementDefinition.requiredContent ) ) { styles.display = 'none'; this.notAllowed = true; }

即运行时仍以filter.check()复核,不满足的字段直接隐藏并标记notAllowed。这正是自动化测试中item.notAllowed断言(见下文)所针对的机制。

3.3 底层判定:filter.check 与结果缓存

上述所有门控最终都收敛到 core/filter.js 的filter.check()方法。该方法将测试规则字符串(如td{width})构造成"模拟元素"后走一遍完整过滤流水线,若元素被标记移除则返回false。其关键实现细节是结果缓存:

if ( typeof test == 'string' ) this._.cachedChecks[ cacheKey ] = result;

一旦某条规则的判定结果被缓存,后续check直接命中缓存。这解释了手动测试页面源码中的一句注释:

// We need to recreate editor, because filter is caching checks. editor.destroy( true );

——正因为过滤器缓存了判定结果,修改allowedContent后必须重建编辑器(或通过filter的重新加载机制)才能让新规则生效。这也是"勾选/取消复选框会销毁并重建编辑器、从而产生闪烁"这一注意项的根本原因。

四、源码级原理:上下文菜单项由 filter.check 门控

手动测试的第二个断言是:"如果未勾选任何属性,则单元格属性不会出现在上下文菜单中"。其实现位于 plugins/tabletools/plugin.js 的菜单项组装逻辑:

getItems: function() { var selection = editor.getSelection(), cells = getSelectedCells( selection ), items = { /* 插入/删除/合并/拆分等条目固定出现 */ }; if ( editor.filter.check( requiredContent ) ) { items.tablecell_properties = cells.length > 0 ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED; } return items; }

当编辑器当前过滤器不允许requiredContent数组中的任何一条规则(例如allowedContent被配置为仅table;tbody;thead;tr;td;,未放行任何td样式/属性)时,tablecell_properties条目就不会进入菜单对象,于是右键菜单中找不到"单元格属性"入口;反之,若放行了部分规则,该条目出现,但具体字段仍由对话框级过滤进一步裁剪。

这一"上下文菜单出现与否"的断言在自动化测试 tests/plugins/tabletools/allowedcontent.js 中有对应验证:

assert editor.name === 'editor' ? 'isFalse' : 'isTrue' ;

五、自动化测试:逐项组合验证字段显隐

仓库在 tests/plugins/tabletools/allowedcontent.js 中提供了一套完备的自动化测试,与手动测试场景互为印证。测试定义了一张filterMap,将对话框字段映射到"元素 → styles/attributes → 规则名":

var filterMap = { width: [ 'td', 'styles', 'width' ], height: [ 'td', 'styles', 'height' ], wordWrap: [ 'td', 'styles', 'white-space' ], hAlign: [ 'td', 'styles', 'text-align' ], vAlign: [ 'td', 'styles', 'vertical-align' ], cellType: [ 'th', 'attributes', 'scope' ], rowSpan: [ 'td', 'attributes', 'rowspan' ], colSpan: [ 'td', 'attributes', 'colspan' ], bgColor: [ 'td', 'styles', 'background-color' ], borderColor: [ 'td', 'styles', 'border-color' ] };

基准编辑器配置为allowedContent: 'table;tbody;thead;tr;td;',然后为每个字段组合动态生成一个带extraAllowedContent的编辑器实例(editor_<index>_allowed:<key1,key2,...>),逐一断言:字段被允许时对话框中出现对应选项,未被允许时对应字段缺失或notAllowed。

另一份测试 tests/plugins/tabletools/cellproperties.js 从反方向验证了同一机制:

'test allowedContent rule': function() { // allowedContent: '*[lang,dir]; table tbody thead tr th td tfoot' // 断言 dialog.getContentElement( 'info', 'width' ) 为 undefined(字段被剔除) }, 'test disallowedContent rule': function() { // 默认编辑器配置下 // 断言 width / height / heightType 均 isVisible() }

六、实战配置:用 allowedContent 裁剪单元格属性能力

理解了过滤机制后,即可在实际项目中按需裁剪。以下配置组合可供直接参考:

1. 仅允许基础表格结构,不允许任何单元格属性(对应手动测试"全不勾选"态):

CKEDITOR.replace( 'editor1', { allowedContent: 'table;tbody;thead;tr;td;' } );

效果:右键菜单不出现"单元格属性";即使通过 API 强行打开cellProperties对话框,所有字段也均为空/隐藏。

2. 只允许尺寸与合并属性,禁止颜色与对齐设置:

CKEDITOR.replace( 'editor1', { allowedContent: { table: true, tbody: true, thead: true, tr: true, td: { styles: [ 'width', 'height' ], attributes: [ 'colspan', 'rowspan' ] } } } );

效果:对话框中仅保留宽度/高度/行跨度/列跨度字段,其余字段被过滤;菜单中的"单元格属性"条目仍会出现。

3. 通过extraAllowedContent渐进式放行(与自动化测试同思路):

CKEDITOR.replace( 'editor1', { allowedContent: 'table;tbody;thead;tr;td;', extraAllowedContent: { td: { styles: [ 'background-color' ], attributes: [ 'rowspan' ] }, th: true } } );

效果:仅背景颜色、行跨度、th元素被放行,对应字段随之出现。注意extraAllowedContent的规则会与默认/自定义allowedContent合并后参与filter.check()判定。

4. 颜色选择器联动:当配置中未启用colordialog插件时,背景/边框颜色字段退化为纯文本输入(tableCell.js 依据editor.plugins.colordialog决定是否追加取色按钮);启用后才会在字段旁出现取色按钮。

七、总结与注意事项

回顾整个机制链路:allowedContent(配置层)→CKEDITOR.filter.check()(判定层,含结果缓存)→ 对话框字段级requiredContent过滤与上下文菜单项门控(表现层)。手动测试场景正是用复选框直观演示了这条链路,而自动化测试则从代码层面锁定了每一处行为。

实际使用中需特别注意两点:

  1. 过滤器缓存:filter.check()会缓存字符串规则的判定结果,动态修改allowedContent后必须重建编辑器(或显式重新加载过滤器)才能生效——这正是手动测试"编辑器被销毁重建、页面闪烁"的来源,属预期行为而非缺陷;
  2. 上下文菜单与对话框的过滤粒度不同:菜单只看requiredContent数组整体是否"至少有一条被放行",对话框则逐字段过滤,因此可能出现"菜单有入口、但对话框内字段大幅缩减"的中间状态,配置时需结合两者综合考虑。

进一步阅读:命令与菜单定义见 plugins/tabletools/plugin.js,对话框实现见 plugins/tabletools/dialogs/tableCell.js,过滤判定核心见 core/filter.js,自动化验证见 tests/plugins/tabletools/allowedcontent.js 与 tests/plugins/tabletools/cellproperties.js。

  • 前端
  • UI组件

【免费下载链接】ckeditor4

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

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

相关推荐

上一篇:MediaPipe 经典解决方案模型全景解析:模型清单、变体选型与仓库落地指南
下一篇:async-stripe 支付集成实战:PaymentIntent 从创建到确认的完整教程

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 1:59:03

基于MP2642GR-Z与TM4C129XKCZAD的两串电池被动均衡BMS方案设计

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

作者头像 李华
网站建设 2026/10/17 1:58:04

GEE模块化开发:用exports和require打造可复用脚本库

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

作者头像 李华
网站建设 2026/10/17 1:56:36

SSM整合jar包全解析:依赖清单、版本选型与Maven配置避坑

简介&#xff1a;面向Java Web开发者的SSM框架整合资源包&#xff0c;覆盖Spring、Spring MVC、MyBatis集成所需的主要jar依赖&#xff0c;帮助快速搭建项目基础环境&#xff0c;减少手动寻找、下载和导入第三方库的时间。压缩包共109个文件&#xff0c;以64个jar包为主体&…

作者头像 李华
网站建设 2026/10/17 1:56:35

Windows下Hadoop开发必知:winutils.exe配置与避坑指南

简介&#xff1a;winutils.exe是Hadoop在Windows平台上运行所必需的关键组件&#xff0c;专门解决该分布式计算框架对Unix/Linux系统特性的依赖问题。这一zip资源包共189个文件&#xff0c;压缩后约5.96MB&#xff0c;包含exe主程序、dll动态库、lib链接库、pdb调试信息&#x…

作者头像 李华
网站建设 2026/10/17 1:55:56

layim+环信PC端WebIM聊天Demo:从零搭建网页即时通讯原型

简介&#xff1a;这是一份面向前端开发者的网页即时聊天实战示例&#xff0c;基于 LayIM 界面框架对接环信 WebIM&#xff0c;适合想学习 PC 端聊天室搭建、消息收发与联系人列表渲染的初中级工程师参考。压缩包共 190 个文件&#xff0c;约 1.7MB&#xff0c;以 33 个 js 脚本…

作者头像 李华