- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
CKEditor 4 从 4.9.0 起引入了imagebase插件,它提供了一套可复用的图片部件(image widget)基础设施,其中captionfeature 负责在图片下方渲染可编辑的<figcaption>说明文字。本指南围绕仓库中的手动测试用例 captiondisabled.md 展开,讲解如何利用 CKEditor 4 的 Advanced Content Filter(ACF)通过disallowedContent配置彻底禁用部件 caption,并深入源码解释其底层生效机制,最终给出可在真实项目中直接使用的配置方案与验证方法。读完本文,你将掌握"让部件永不显示 caption"的标准做法,并理解 ACF 对部件可编辑区域(nested editable)的约束方式。
一、测试用例原文解读:期望与验收标准
仓库中的 captiondisabled.md 是一个 Bender 手动测试(manual test)用例,它的任务定义如下:
- 测试标识:
@bender-tags: 4.9.0, feature, 932(关联 ticket #932),@bender-ui: collapsed - 测试插件集合:
sourcearea, wysiwygarea, toolbar, imagebase, link, htmlwriter, elementspath, basicstyles, easyimage - 测试主题:
Customizing caption existence with ACF(通过 ACF 定制 caption 是否存在)
用例步骤与断言非常精简,只有两步:
- 聚焦部件(Focus widget)。
- 期望结果(Expected):没有任何 caption 出现。
- 非期望结果(Unexpected):聚焦后 caption 显示出来。
最后要求"Repeat for all editors"——即对页面中的每个编辑器实例(classic、divarea)逐一重复验证。
该用例的完整配置从哪里来
手动测试的步骤描述只占一半信息,另一半藏在与之配套的 captiondisabled.html 中。HTML 文件才是真正给出配置的载体,其核心片段如下:
<div id="classic"> <p>Widget:</p> <figure> <img src="../../../image2/_assets/foo.png" alt="foo"> </figure> </div>var cfg = { disallowedContent: 'figcaption', on: { pluginsLoaded: function( evt ) { var editor = evt.editor, imageBase = CKEDITOR.plugins.imagebase; imageBase.addImageWidget( editor, 'testWidget', imageBase.addFeature( editor, 'caption', {} ) ); } }, height: 270 }; CKEDITOR.replace( 'classic', cfg ); CKEDITOR.replace( 'divarea', CKEDITOR.tools.object.merge( cfg, { extraPlugins: 'divarea' } ) );也就是说:该用例故意给编辑器注入了disallowedContent: 'figcaption',然后动态注册一个带captionfeature 的图片部件testWidget。页面中的<figure>原始内容没有<figcaption>子元素,因此验收的关键点是:即使部件被聚焦(正常情况下聚焦一个空 caption 的图片部件会临时显示 caption 并出现占位符),ACF 也应从根源上禁止figcaption,使 caption 完全不出现。
二、ACF 如何决定 caption 的"存在性"
2.1 从disallowedContent说起
CKEditor 4 的 Advanced Content Filter 负责控制编辑器内容中允许出现的元素与属性。disallowedContent配置用于"裁剪"默认允许的内容,并且其优先级高于allowedContent规则。官方配置文档位于 core/filter.js,原文说明为:
"Disallowed content rules. They have precedence over
allowedContentallowed content rules."
因此,disallowedContent: 'figcaption'一经设置,无论imagebase部件定义声明了多少关于figcaption的允许规则,该元素都不会被编辑器保留或创建。
2.2 caption feature 的两次 ACF 检查
关键问题在于:figcaption被禁后,imagebase的 caption feature 在部件初始化阶段就应当自我禁用,而不是等到聚焦时才去"隐藏"。这在 plugins/imagebase/plugin.js 的getCaptionFeature()实现中有两处明确证据:
第一处:setUp阶段检查(plugin.js L553-574)
setUp: function( editor ) { var listeners = []; function listener( evt ) { var path = evt.name === 'blur' ? editor.elementPath() : evt.data.path, sender = path ? path.lastElement : null, widgets = getWidgetsWithFeature( editor.widgets.instances, 'caption' ); if ( !editor.filter.check( 'figcaption' ) ) { return CKEDITOR.tools.array.forEach( listeners, function( listener ) { listener.removeListener(); } ); } // ... 否则为每个带 caption 的部件调用 _refreshCaption } listeners.push( editor.on( 'selectionChange', listener , null, null, 9 ) ); listeners.push( editor.on( 'blur', listener ) ); }editor.filter.check( 'figcaption' )是 ACF 的运行时查询方法:它询问"当前过滤器中是否允许figcaption元素"。在disallowedContent: 'figcaption'生效的情况下,该方法返回false,于是监听器立即把自己从selectionChange/blur事件上全部移除——后续任何焦点变化都不会再触发 caption 的显示逻辑。
第二处:init阶段检查(plugin.js L576-589)
init: function() { if ( !this.editor.filter.check( 'figcaption' ) ) { return; } if ( !this.parts.caption ) { this.parts.caption = createCaption( this ); } // ... }部件初始化时再次调用editor.filter.check( 'figcaption' )。若返回false,则直接return,根本不会调用createCaption()去创建<figcaption>元素,也不会注册 caption 这个 nested editable。这正是"聚焦后 caption 永不出现"的底层保证——caption 从 DOM 层面就不存在。
2.3 部件基础定义中的对照:什么情况下 caption 会被创建
为了理解 2.2 的"禁用"效果,有必要对照 caption 正常启用时的部件定义。createWidgetDefinition()生成的 image widget 基础定义(plugin.js L643-695)包含:
template: '<figure class="{imageClass}">' + '<img alt="{alt}" src="{src}" />' + '<figcaption>{caption}</figcaption>' + '</figure>', allowedContent: { img: { attributes: '!src,alt,width,height' }, figure: true, figcaption: true }, requiredContent: 'figure; img[!src]', editables: { caption: { selector: 'figcaption', pathName: editor.lang.imagebase.pathNameCaption, allowedContent: 'br em strong sub sup u s; a[!href,target]' } }可以看到:正常状态下,部件模板输出figcaption,allowedContent允许figure与figcaption,caption 作为 nested editable 的允许内容为br em strong sub sup u s; a[!href,target]。而disallowedContent: 'figcaption'会覆盖这一切——即使 upcast 时原始 HTML 中存在<figcaption>,ACF 也会在数据进入编辑器时将其剥离。
三、完整实战配置:让图片部件彻底不显示 caption
结合上文原理,在真实项目中禁用 caption 有以下两种层次的做法。
3.1 全局禁用:直接配置disallowedContent
最简单的方式是在创建编辑器时配置:
CKEDITOR.replace( 'editor1', { // 依赖 imagebase 的图片部件(如 easyimage) extraPlugins: 'imagebase', // 从 ACF 层面彻底禁止 figcaption disallowedContent: 'figcaption' } );从源码结构看,这一配置对所有依赖imagebase的图片部件都生效,包括easyimage——因为 plugins/easyimage/plugin.js 在构建 easyimage 部件定义时就是通过同一套 API 装配的:
widgetDefinition = CKEDITOR.plugins.imagebase.addFeature( editor, 'upload', widgetDefinition ); widgetDefinition = CKEDITOR.plugins.imagebase.addFeature( editor, 'caption', widgetDefinition ); CKEDITOR.plugins.imagebase.addImageWidget( editor, WIDGET_NAME, widgetDefinition );因此,禁用figcaption同样会让 Easy Image 部件失去 caption 能力,这可以推断为该配置适用于所有基于 imagebase 的部件。
3.2 动态注册部件时使用相同手法
如果项目是像测试用例那样在运行时通过CKEDITOR.plugins.imagebase.addFeature( editor, 'caption', {} )动态注册部件,只需保证注册前编辑器配置中已含disallowedContent: 'figcaption'即可。注意addFeature的调用顺序:setUp在注册阶段就会执行一次 ACF 检查,所以配置必须在pluginsLoaded或更早的阶段就绪,否则监听器可能已经挂上,需要后续由init的检查兜底(init也会直接 return,部件依然不会创建 caption,两种检查互为保险)。
3.3 可选的 UI 行为定制:聚焦时显示、失焦时隐藏
若需求并非"彻底禁用",而是希望保留<figcaption>但控制其可见性(空 caption 时隐藏、聚焦时显示占位符),则不应使用disallowedContent,而应依赖 caption feature 默认的可见性切换逻辑。其行为由_refreshCaption(plugin.js L599-616)与样式表 plugins/imagebase/styles/imagebase.css 配合实现:
[data-cke-caption-hidden] { display: none; } [data-cke-caption-placeholder]::before { display: inline; color: rgba( 0, 0, 0, .4 ); content: attr(>tests[ 'test integration with ACF' ] = function() { bender.editorBot.create( { name: 'acf_integration', config: { disallowedContent: 'figcaption' } }, function( bot ) { var editor = bot.editor; addTestWidget( editor ); bot.setData( getFixture( 'toggleOneEmpty' ), function() { assert.isFalse( !!widgetTestsTools.getWidgetByDOMOffset( editor, 0 ).parts.caption, 'Caption presence' ); } ); } ); };该测试先用disallowedContent: 'figcaption'创建编辑器并注册带 caption feature 的testWidget,然后加载一个包含空 caption 的部件作为 fixture,最终断言widget.parts.caption不存在(assert.isFalse)。这正是"caption 被 ACF 禁用后部件不再持有 caption part"的直接证据,与手动用例的预期行为完全一致。
同文件中的其他用例(caption.js L228-248)还覆盖了另一种相关场景:当输入 HTML本身就没有<figcaption>时,upcast 后的部件会自动补建一个 caption(createCaption逻辑),这也反向说明:只有 ACF 层面禁止figcaption,才能真正阻止 caption 的"无中生有"。
五、注意事项与边界
disallowedContent优先级最高:它作用于编辑器全局过滤器,会覆盖部件allowedContent中关于figcaption的声明(参见 core/filter.js)。这与extraAllowedContent(增量允许)是相反方向的配置,不应混用。两处 ACF 检查互为保障:
setUp中的检查负责"拆掉监听器",init中的检查负责"不创建元素"。即使部件注册时序稍有不同,init的editor.filter.check( 'figcaption' )依然能保证 caption 不会出现,这是测试用例断言能够稳定的原因。对已有内容的剥离行为:若编辑器已有内容中包含
<figcaption>(例如从其他来源粘贴或setData),在disallowedContent: 'figcaption'下该元素会被 ACF 在数据处理阶段剥离,因此 caption 文字也会一并丢失。这是"彻底禁用"的必然代价,适用于明确不需要图片说明文字的场景。占位符与隐藏逻辑不适用:禁用后,CSS 中的
data-cke-caption-hidden/data-cke-caption-placeholder相关样式与_refreshCaption逻辑都不会再被执行,因为监听器与 caption 元素均已不存在。版本前提:caption feature 与
addFeature/addImageWidgetAPI 自 CKEditor 4.9.0 引入(见 plugins/imagebase/plugin.js 中@since 4.9.0标注),本文所有配置与断言均以仓库当前源码为准。
六、小结
围绕 captiondisabled.md 这一手动用例,可以总结出一条清晰的定制链路:disallowedContent: 'figcaption'→ ACF 拒绝该元素 → caption feature 的setUp/init两处editor.filter.check( 'figcaption' )检查全部短路 → 部件不再创建<figcaption>、不再挂载 caption 可编辑区、不再响应焦点事件 → 无论聚焦与否,caption 都"不存在"。配套的自动化测试 caption.js 用widget.parts.caption是否为空给出了可机器执行的验收标准。这套机制同时适用于easyimage等所有基于imagebase的图片部件,是 CKEditor 4 中控制图片说明文字存在性的标准做法。
- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
相关推荐
CKEditor 4 imagebase 插件:剪切粘贴后图片 caption 占位符可见性修复(1592)解析与验证
CKEditor 4 imagebase 插件:剪切粘贴后图片 caption 占位符可见性修复( 1592)解析与验证 本篇文章以仓库中的手工测试用例 cap
前端UI组件CKEditor 4 图片小部件题注 ACF 定制实战:从手动测试到源码原理
CKEditor 4 图片小部件题注 ACF 定制实战:从手动测试到源码原理 导读 本文围绕 CKEditor 4 仓库中 tests/plugins/imag
前端UI组件CKEditor 4 禁用内置图片拖放处理:`clipboard_handleImages` 配置与第三方插件接管实战
CKEditor 4 禁用内置图片拖放处理: clipboard_handleImages 配置与第三方插件接管实战 本文围绕 CKEditor 4(当前仓库
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考