You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CKEditor5:点击气球面板按钮时如何更新模型?

CKEditor5 Callout插件对齐功能实现方案

核心思路

CKEditor5采用模型驱动视图的架构,不能直接操作DOM修改CSS类,正确流程是:通过命令修改模型属性 → 转换器将属性映射为视图的CSS类 → 按钮状态绑定到命令的当前值。


1. 扩展模型Schema,添加position属性

首先给callout模型元素添加position属性,定义允许的值和默认值:

// 在你的Callout插件init方法中
const schema = editor.model.schema;
schema.extend('callout', {
    allowAttributes: 'position'
});
// 设置默认对齐为全屏宽度
schema.setAttributeProperties('callout', 'position', {
    default: 'full',
    isFormatting: true
});

2. 创建对齐命令

实现CalloutPositionCommand,负责修改模型的position属性,并处理命令的可用/激活状态:

import { Command } from 'ckeditor5/src/core';

export class CalloutPositionCommand extends Command {
    execute({ position }) {
        const model = this.editor.model;
        const selection = model.document.selection;

        model.change(writer => {
            // 获取当前选中的Callout元素
            const callout = selection.getSelectedElement() || selection.getFirstPosition().findAncestor('callout');
            if (callout) {
                writer.setAttribute('position', position, callout);
            }
        });
    }

    refresh() {
        const model = this.editor.model;
        const selection = model.document.selection;
        const callout = selection.getSelectedElement() || selection.getFirstPosition().findAncestor('callout');

        // 只有选中Callout时命令可用
        this.isEnabled = !!callout;
        // 按钮激活状态:当前position与按钮对应值匹配
        this.value = callout ? callout.getAttribute('position') : null;
    }
}

在插件init方法中注册命令:

editor.commands.add('calloutPosition', new CalloutPositionCommand(editor));

3. 绑定气球面板按钮的事件与状态

修改气球视图的按钮创建逻辑,并在UI插件中绑定点击事件和状态:

优化CalloutBalloonView的按钮创建

给按钮添加name属性,标记对应的对齐值:

_createButton( name, label, icon) {
    const button = new ButtonView();

    button.set( {
        label,
        icon,
        tooltip: true,
        class: 'testing',
        name // 存储对齐标识:left/full/right
    } );

    return button;
}

在CalloutUI中绑定按钮逻辑

修改_createBalloonView方法,给每个按钮绑定点击事件和激活状态:

_createBalloonView() {
    const editor = this.editor;
    const balloonView = new CalloutBalloonView( editor.locale );
    const positionCommand = editor.commands.get('calloutPosition');

    // 遍历所有对齐按钮,绑定事件和状态
    [balloonView.leftAlignView, balloonView.fullView, balloonView.rightAlignView].forEach(button => {
        // 点击按钮执行对齐命令
        this.listenTo(button, 'execute', () => {
            editor.execute('calloutPosition', { position: button.name });
        });

        // 绑定按钮的激活状态到命令当前值
        button.bind('isOn').to(positionCommand, 'value', currentValue => currentValue === button.name);
    });

    return balloonView;
}

4. 添加模型与视图的转换器

将模型的position属性映射为视图元素的CSS类:

// 在插件init方法中
const conversion = editor.conversion;

// 模型→视图:将position属性转为CSS类
conversion.for('downcast').attributeToAttribute({
    model: 'position',
    view: position => {
        const baseClass = 'callout';
        // 根据position值拼接对应的类
        const alignClass = position === 'left' ? 'callout--left' : 
                          position === 'right' ? 'callout--right' : 'callout--full';
        return {
            key: 'class',
            value: `${baseClass} ${alignClass}`
        };
    }
});

// 视图→模型:从CSS类还原position属性(支持粘贴/手动修改DOM后同步)
conversion.for('upcast').attributeToAttribute({
    view: {
        name: 'div', // 替换为你的Callout视图元素标签
        classes: ['callout--left', 'callout--right', 'callout--full']
    },
    model: viewElement => {
        let position = 'full';
        if (viewElement.hasClass('callout--left')) position = 'left';
        else if (viewElement.hasClass('callout--right')) position = 'right';
        return { key: 'position', value: position };
    }
});

5. 优化气球面板的显示逻辑(可选)

监听编辑器选中变化,仅当选中Callout时显示气球面板:

// 在CalloutUI的init方法中添加
this.listenTo(editor.editing.view.document, 'selectionChange', () => {
    const selection = editor.model.document.selection;
    const callout = selection.getSelectedElement() || selection.getFirstPosition().findAncestor('callout');
    
    if (callout && !this._balloon.hasView(this.balloonView)) {
        this._showBalloon();
    } else if (!callout && this._balloon.hasView(this.balloonView)) {
        this._balloon.remove(this.balloonView);
    }
});

内容的提问来源于stack exchange,提问作者Brian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 15:45:07