CKEditor5自定义占位符插件修改ViewElement innerText致编辑器异常
CKEditor5 自定义占位符插件外部DOM操作兼容问题
场景说明
- 开发目标:为Neos CMS开发适配的CKEditor5自定义占位符插件,Neos本身搭载了经过自定义视图改造的CKEditor5实现
- 插件基础逻辑:支持配置存储「标识符、标签」键值对的数据源,编辑器下拉菜单自动加载所有配置项,用户选中对应项后即可插入占位符元素,最终输出为携带若干data属性的span标签
- 初始方案问题:最初设计是生成仅携带data属性的空span元素,通过class和data属性标识元素后,由独立JS脚本注入实时业务数据;实际开发中发现该方案存在适配问题:外部JS直接修改编辑器内的span元素DOM时,CKEditor无法识别这类变更,会触发运行异常
- 当前验证状态:在downCasting阶段给占位符元素写入固定innerText、且后续不对元素内容做替换时,插件运行完全正常;预期实现效果为空span支持动态注入实时数据,无需在转换阶段写死占位文本

现有核心代码
当前插件尚未开发完成,动态数据注入为核心待实现特性,核心逻辑代码如下:
import {Plugin, toWidget, viewToModelPositionOutsideModelElement, Widget,} from "ckeditor5-exports"; import PlaceholderCommand from "./placeHolderCommand"; export default class PlaceholderEditing extends Plugin { static get requires() { return [Widget]; } init() { this._defineSchema(); this._defineConverters(); this.editor.commands.add( "placeholder", new PlaceholderCommand(this.editor) ); this.editor.editing.mapper.on( "viewToModelPosition", viewToModelPositionOutsideModelElement(this.editor.model, (viewElement) => viewElement.hasClass("internezzo-placeholder") ) ); this.editor.config.define("placeholderProps", { types: ["name", "node", "nodePath"], }); this.editor.config.define("placeholderBrackets", { open: "[", close: "]", }); } _defineSchema() { const schema = this.editor.model.schema; schema.register("placeholder", { allowWhere: "$text", isInline: true, isObject: true, allowAttributes: [ "name", "node", "nodePath", "data-placeholder-identifier", "data-node-identifier", "data-node-path", ], }); } _defineConverters() { const conversion = this.editor.conversion; const config = this.editor.config; conversion.for("upcast").elementToElement({ view: { name: "span", classes: ["foobar-placeholder"], }, model: (viewElement, writer) => { const name = viewElement.getAttribute('data-placeholder-identifier'); const node = viewElement.getAttribute('data-node-identifier'); const nodePath = viewElement.getAttribute('data-node-path'); const modelWriter = writer.writer || writer; return modelWriter.createElement("placeholder", {name, node, nodePath, editable: false}); }, }); conversion.for("editingDowncast").elementToElement({ model: "placeholder", view: (modelItem, writer) => { const viewWriter = writer.writer || writer; const widgetElement = createPlaceholderView(modelItem, viewWriter); return toWidget(widgetElement, viewWriter); }, }); conversion.for("dataDowncast").elementToElement({ model: "placeholder", view: (modelItem, writer) => { const viewWriter = writer.writer || writer; return createPlaceholderView(modelItem, viewWriter); }, }); // Helper method for downcast converters. function createPlaceholderView(modelItem, viewWriter) { const name = modelItem.getAttribute("name"); const node = modelItem.getAttribute("node"); const nodePath = modelItem.getAttribute("nodePath"); const placeholderView = viewWriter.createContainerElement("span", { class: "foobar-placeholder", "data-placeholder-identifier": name, "data-node-identifier": node, "data-node-path": nodePath, }); // Would be nice to remove that and have just empty spans that get dynamic data let innerText = config.get("placeholderBrackets.open") + name; innerText += config.get("placeholderBrackets.close"); viewWriter.insert( viewWriter.createPositionAt(placeholderView, 0), viewWriter.createText(innerText) ); return placeholderView; } } }
补充信息与核心疑问
站点前端使用的独立JS脚本会查找所有class为foobar-placeholder的span元素,向其中写入实时业务数据,该逻辑在普通前端页面运行完全正常,但在搭载CKEditor的CMS后台编辑页执行时,会因DOM被外部修改导致编辑器运行异常。
核心疑问:是否可以直接操作DOM层对应的CKEditor ViewElement,同时保证编辑器正常运行?
内容的提问来源于stack exchange,提问作者Markus
相关产品推荐
相关产品推荐

