- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
导读
本文以 CKEditor 4 仓库中的手动测试用例 outputtemplatespace.md 为核心,深入讲解 Autocomplete 插件中outputTemplate(自定义插入内容的 HTML 模板)与followingSpace(确认匹配后自动补一个空格)两个配置项的组合使用方式,并还原该组合下的一个经典回归 Bug(#2008):在连续两次自动补全之间插入的文字被错误加粗或丢失斜体。读完本文,你将掌握这两个配置项的完整语义、底层commit()插入流程、如何运行 Bender 手动测试复现该场景,以及配套的自动化测试证据。
一、场景还原:连续两次自动补全之间的样式丢失
该测试用例位于仓库的 Bender 手动测试目录:
- 测试说明:outputtemplatespace.md
- 测试页面:outputtemplatespace.html
测试页面通过CKEDITOR.replace初始化一个 600px 宽的经典编辑器,并在instanceReady事件中创建一个 Autocomplete 实例,配置如下:
<div id="editor1" ></div> <script> // Ignore test due to IE issue (#2077). if ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 ) { bender.ignore(); } CKEDITOR.replace( 'editor1', { width: 600, on: { instanceReady: function( evt ) { new CKEDITOR.plugins.autocomplete( evt.editor, { textTestCallback: autocompleteUtils.getTextTestCallback(), dataCallback: autocompleteUtils.getDataCallback(), outputTemplate: '<strong>{name}</strong>', followingSpace: true } ); } } } ); </script>页面顶部还带有 Bender 元信息标签,声明了该用例的身份、所需插件与依赖:
@bender-tags: 4.20.0, bug, 2008 @bender-ui: collapsed @bender-ckeditor-plugins: wysiwygarea, toolbar, basicstyles, autocomplete, textmatch @bender-include: _helpers/utils.js解读这些标签:该测试针对 4.20.0 版本引入的followingSpace功能(bug 编号 #2008),需要wysiwygarea、toolbar、basicstyles、autocomplete、textmatch这些插件,并包含 tests/plugins/autocomplete/manual/_helpers/utils.js 提供的测试辅助工具(内置@john、@thomas、@anna、@cris、@julia五条候选数据)。
手动复现步骤
按测试说明依次操作:
- 聚焦编辑器(Focus the editor)。
- 点击斜体(italic)样式按钮,让后续输入的文字带上斜体样式。
- 输入
@触发第一次自动补全。 - 接受(回车确认)第一条候选项。
- 输入
and(注意两端各有一个空格)。 - 再次输入
@触发第二次自动补全。 - 再次接受第一条候选项。
期望结果与异常现象
| 对比项 | 内容 |
|---|---|
| Expected result(期望结果) | 两次自动补全结果之间的文字(and)不应被加粗,但应保持斜体 |
| Actual result(异常现象,Bug 修复前) | 中间的文字被错误加粗,或者丢失了斜体样式 |
也就是说,outputTemplate: '<strong>{name}</strong>'本应只让被补全的候选值(如@john)加粗,但在此 Bug 影响下,样式"越界"污染到了补全值之后的普通文本。
二、两个核心配置项:outputTemplate 与 followingSpace
outputTemplate:决定补全结果插入什么 HTML
在 plugins/autocomplete/plugin.js 中,outputTemplate被描述为"作为自动补全项被确认(commit)时插入到编辑器中的标记模板",支持通过候选数据项的属性做模板占位符插值,例如:
var outputTemplate = '<a href="/tracker/{ticket}">#{ticket} ({name})</a>';其构造逻辑(plugin.js):
/** * Template of markup to be inserted as the autocomplete item gets committed. * You can use {@link CKEDITOR.plugins.autocomplete.model.item item} properties to customize the template. * @property {CKEDITOR.template} [outputTemplate=null] */ this.outputTemplate = config.outputTemplate !== undefined ? new CKEDITOR.template( config.outputTemplate ) : null;关键点:
- 未配置时默认值为
null,此时插入的就是候选数据项的name纯文本。 - 配置后会被包装成
CKEDITOR.template实例,占位符{name}、{id}等会被替换为对应数据项属性值。 - 模板输出前,数据项会经过
encodeItem()做 HTML 编码(plugin.js),避免注入风险。
outputTemplate与候选列表里的itemTemplate是不同的:itemTemplate决定下拉面板中每条候选如何渲染,outputTemplate决定用户回车确认后,候选值以何种 HTML 形式写入编辑器内容。
followingSpace:确认匹配后自动补一个空格
followingSpace是 4.20.0 新增的配置项(Bug #2008),语义为"在已接受匹配项的后面追加一个空格",适合需要在补全后立即继续输入的场景。相关实现(plugin.js):
/** * Indicates if a following space should be added after inserted match into an editor. * @since 4.20.0 * @readonly * @property {Boolean} [followingSpace] */ this.followingSpace = config.followingSpace;组合使用:commit() 的完整插入流程
两个配置项共同作用的代码路径在commit()方法中(plugin.js):
commit: function( itemId ) { if ( !this.model.isActive ) { return; } this.close(); if ( itemId == null ) { itemId = this.model.selectedItemId; // If non item is selected abort commit. if ( itemId == null ) { return; } } var item = this.model.getItemById( itemId ), editor = this.editor, html = this.getHtmlToInsert( item ); // Insert space after accepting match (#2008). html += this.followingSpace ? ' ' : ''; editor.fire( 'saveSnapshot' ); editor.getSelection().selectRanges( [ this.model.range ] ); editor.insertHtml( html, 'text' ); if ( this.followingSpace ) { removeLeadingSpace( editor ); } editor.fire( 'saveSnapshot' ); }流程拆解:
getHtmlToInsert( item )(plugin.js)决定基础 HTML:getHtmlToInsert: function( item ) { var encodedItem = encodeItem( item ); return this.outputTemplate ? this.outputTemplate.output( encodedItem ) : encodedItem.name; }即:有
outputTemplate用模板渲染结果,没有则直接使用编码后的name。若
followingSpace为真,在 HTML 末尾追加 (非断行空格,而不是普通空格,避免被浏览器折叠或触发意外的空白符处理)。editor.insertHtml( html, 'text' )将这段 HTML 插入到匹配文本所在的 range 中,替换掉原来的查询文本(例如@john被替换成<strong>@john</strong> )。若
followingSpace为真,再调用removeLeadingSpace( editor )(plugin.js)清理插入点之后紧邻的已有空格,防止"空格加倍":function removeLeadingSpace( editor ) { var selection = editor.getSelection(), nextNode = selection.getRanges()[ 0 ].getNextNode( function( node ) { return Boolean( node.type == CKEDITOR.NODE_TEXT && node.getText() ); } ); if ( nextNode && nextNode.getText().match( /^\s+/ ) ) { var range = editor.createRange(); range.setStart( nextNode, 0 ); range.setEnd( nextNode, 1 ); range.deleteContents(); } }前后各触发一次
saveSnapshot,保证撤销栈正确。
Bug 的根源与修复方向
从 commit 流程可以看出: 被直接拼在outputTemplate渲染出的 HTML 字符串末尾,而editor.insertHtml在插入带内联样式(如斜体)的文本环境时,会把插入点处的现有样式传播到新插入的内容上。在连续两次补全、中间夹着and这类普通文本的场景里,第二个<strong>{name}</strong>的样式边界一旦处理不当,就会把中间的文本一起"加粗";反过来,样式传播机制也可能把中间文本应有的斜体样式"吃掉"。这正是该手动测试用"斜体按钮 + 两次补全"复现的回归点——它验证的是修复后outputTemplate与followingSpace组合时, 及周边文本的样式边界必须精确、可控。
三、同一主题下的其他手动测试用例
该目录下还有一组与followingSpace直接相关的手动测试,相互印证:
1. 基础行为:followingspace
- 测试说明:followingspace.md
- 测试页面:followingspace.html
操作:光标置于编辑器内容末尾 → 输入@→ 回车。
期望结果:插入后自动追加一个空格,形如@john ^(^表示光标)。 异常结果:没有追加空格,形如@john^。
2. 已有空格时不重复:existingspace
- 测试说明:existingspace.md
操作:光标置于两个单词之间(已有空格,如hello ^ world)→ 输入@→ 回车。
期望结果:不会重复添加空格,光标落在已有空格之后,形如hello @john ^world。 异常结果:空格被加倍(hello @john ^ world),或光标紧贴插入文本(hello @john^ world)。
该用例验证的正是removeLeadingSpace的去重逻辑:followingSpace追加的 与紧随其后的已有空格会合并,只保留一个。
3. 仅 outputTemplate:outputtemplate
- 测试说明:outputtemplate.md
- 测试页面:outputtemplate.html(只配置了
outputTemplate: '<strong>{name}</strong>',没有followingSpace)
操作:输入@→ 等待下拉出现 → 回车。
期望结果:插入的名字被加粗。 异常结果:插入的是纯文本。
这个用例单独验证了outputTemplate的基础渲染能力,是组合用例的"单变量对照版"。
四、自动化测试:从手动到断言
除了手动测试,仓库还提供了同名的自动化断言测试,位于 tests/plugins/autocomplete/autocomplete.js,与手动用例一一对应:
'test following space is inserted after accepting match':配置followingSpace: true后回车,断言编辑器数据为<p>item1 </p>(空格确实被插入)。'test following space is not doubled':在已有空格前补全,断言结果为<p>item1 foo</p>,证明不会产生双空格。'test following space with output template':同时配置outputTemplate: '<strong>{name}</strong>'与followingSpace: true,断言结果为<p><strong>item1</strong> foo</p>。'test following space with output template is not doubled':在<p>^ foo</p>上补全,同样断言<p><strong>item1</strong> foo</p>。
这四个测试组合在一起,用自动化断言锁死了本文核心场景的行为契约:
- 有
outputTemplate时,加粗只作用于补全值本身(<strong>item1</strong>), 在 strong 之外; - 有
followingSpace时,追加的空格不被重复。
注意测试中还有// Ignore test due to IE issue (#2077)的跳过逻辑:CKEDITOR.env.ie && CKEDITOR.env.version < 11(IE 10 及以下)时忽略,因为老 IE 在插入带样式 HTML 时的行为与标准浏览器不一致——这与手动测试页面顶部的bender.ignore()完全对应。
五、底层联动:textmatch 与 commit 键
理解该场景还需要知道补全动作如何被触发与确认:
- 触发:
textTestCallback借助 plugins/textmatch/plugin.js 的CKEDITOR.plugins.textMatch.match( range, callback )在 DOM 中定位光标左侧的匹配文本(本测试用例中为@\w*$正则,见 tests/plugins/autocomplete/manual/_helpers/utils.js 的getTextTestCallback)。 - 确认:回车/制表键触发
commit()。默认提交按键由全局配置autocomplete_commitKeystrokes控制,默认值为[ 9, 13 ](Tab 与 Enter,见 plugin.js),也可设为空数组来禁用。
commit()内部selectRanges( [ this.model.range ] )+insertHtml( html, 'text' )的组合,决定了插入内容会与周边文本的样式产生交互——这正是 outputtemplatespace 场景中样式边界问题的直接来源。
六、生态联动:mentions 与 emoji 中的 followingSpace
followingSpace并非 autocomplete 独有,它被集成进了基于 autocomplete 构建的两个官方插件:
Mentions 插件:plugins/mentions/plugin.js 在
configDefinition中透传followingSpace,并在创建内部CKEDITOR.plugins.autocomplete实例时原样传给outputTemplate一起使用。Emoji 插件:plugins/emoji/plugin.js 创建 autocomplete 实例时传入
followingSpace: editor.config.emoji_followingSpace,对应全局配置CKEDITOR.config.emoji_followingSpace = false(默认关闭,见 plugins/emoji/plugin.js)。启用方式:config.emoji_followingSpace = true;
在 CHANGES.md 的 CKEditor 4.20 版本记录中,该特性被官方描述为:
- 扩展了 Mentions 与 Emoji 插件,新增"在接受自动补全匹配后添加一个空格"的功能选项;
- 为 Autocomplete 新增
followingSpace选项,让已接受的匹配以空格收尾。
这解释了该配置的设计动机:@提及或:表情补全后,用户通常需要立刻继续输入文字,自动补空格能显著改善连续输入体验;而outputTemplate则负责让这些补全内容在视觉上更丰富(加粗、链接、图标等)。两者组合时的样式边界问题,正是 outputtemplatespace 用例存在的意义。
七、如何运行这些手动测试
这些manual/目录下的用例属于 Bender 手动测试体系,仓库根目录的 bender.js 是其运行配置(tests分组中Plugins组覆盖plugins/**路径)。手动测试与自动化测试不同,不会断言,而是由测试人员在浏览器中按步骤操作、人工对照 Expected/Actual 结果。
典型运行方式:
# 安装依赖后,启动 bender 服务器并运行手动测试 bender server run # 在浏览器中访问 bender 提供的 manual tests 列表, # 找到 Plugins → Autocomplete → outputtemplatespace 用例按步骤验证自动化断言版则可直接随Plugins分组跑:tests/plugins/autocomplete/autocomplete.js中的四个followingSpace用例会按上文第四节的断言自动验证行为是否符合契约。
结语
outputTemplate与followingSpace是 CKEditor 4 Autocomplete 中两个紧密配合的配置项:前者决定补全结果以何种 HTML 落进正文,后者决定是否自动追加一个空格。而 outputtemplatespace.md 这一手动用例,精确捕捉了二者组合时的样式边界回归——中间的普通文本不该被 的样式传播波及而加粗,也不该丢失已有的斜体。借助配套的 outputtemplate.md、followingspace.md、existingspace.md 手动用例以及 tests/plugins/autocomplete/autocomplete.js 的自动化断言,开发者可以在自己的集成中完整复现、验证并规避这一类样式边界问题。
- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
相关推荐
CKEditor 4 Autocomplete 的 followingSpace 与既有空格场景:手动测试 existingspace 全解析
CKEditor 4 Autocomplete 的 followingSpace 与既有空格场景:手动测试 existingspace 全解析 导读 follo
前端UI组件CKEditor 4 Autocomplete 异步回调清理机制:手动测试用例与源码级修复解析
CKEditor 4 Autocomplete 异步回调清理机制:手动测试用例与源码级修复解析 本文以 CKEditor 4 仓库中 autocomplete
前端UI组件CKEditor 4 Autocomplete 键盘导航:手动测试用例解读与源码实现剖析
CKEditor 4 Autocomplete 键盘导航:手动测试用例解读与源码实现剖析 CKEditor 4 的 autocomplete 插件允许在输入特定
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考