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

CKEditor5从Word复制粘贴列表功能失效求助

CKEditor5 从Word复制粘贴编号列表时缩进子内容丢失格式的修复

问题详情

集成CKEditor5后,从Word复制带缩进的编号列表,粘贴后缩进的子内容丢失格式。

复制的原内容:

1.Dfgdfgdfg
  Dfgdfgdfgdf
2.Dfgdfgdfg kjdfhgjdfhghdfhjg
  Dfgjhdfjhgjdhfghkjdhfghdfjg  dfjjhg dfjgh dfjhj gh dfhdgf
3.Dfgdfgjhdfj 
4.Dfgdfgdf
5.Dfgdfgdfg
6.Dfgdfgdf

粘贴后效果:

1.Dfgdfgdfg
Dfgdfgdfgdf
2.Dfgdfgdfg kjdfhgjdfhghdfhjg
Dfgjhdfjhgjdhfghkjdhfghdfjg  dfjjhg dfjgh dfjhj gh dfhdgf hjdgf  
3.Dfgdfgjhdfj 
4.Dfgdfgdf
5.Dfgdfgdfg
6.Dfgdfgdf

尝试自定义clipboardInput事件和HTML转换函数未生效,使用免费版本,需修复列表复制粘贴功能,原尝试代码如下:

DecoupledEditor
    .create( document.querySelector( '.editor' ), {
        // Editor configuration.
        link: {
            addTargetToExternalLinks: true,
            defaultProtocol: 'https://',
            allowedProtocols: [ 'https'],
            decorators: {
                addTargetToExternalLinks: {
                    mode: 'automatic',
                    callback: url => /^(https?:)?\/\//.test( url ),
                    attributes: {
                        target: '_blank',
                        rel: 'noopener noreferrer'
                    }
                }
            }
        },
    } )
    .then( editor => {
        window.editor = editor;
        // Set a custom container for the toolbar.
        document.querySelector( '.document-editor__toolbar' ).appendChild( editor.ui.view.toolbar.element );
        document.querySelector( '.ck-toolbar' ).classList.add( 'ck-reset_all' );

        // Assuming editor is already initialized
        editor.editing.view.document.on('clipboardInput', (evt, data) => {
            const htmlData = data.dataTransfer.getData('text/html');
            if (htmlData) {
                // Manipulate the HTML structure to properly integrate text into lists
                const processedHtml = integrateTextIntoLists(htmlData);
                const viewFragment = editor.data.htmlProcessor.toView(processedHtml);
                const modelFragment = editor.data.toModel(viewFragment);
        
                // Insert the transformed content
                editor.model.change(writer => {
                    editor.model.insertContent(modelFragment, editor.model.document.selection);
                });
        
                // Stop the default processing to use our custom transformation
                evt.stop();
            }
        });
    } )
    .catch( handleSampleError );


function handleSampleError( error ) {
    const issueUrl = 'https://github.com/ckeditor/ckeditor5/issues';

    const message = [
        'Oops, something went wrong!',
        `Please, report the following error on ${ issueUrl } with the build id "m7gmbfwakd12-rztuvf3ew3ra" and the error stack trace:`
    ].join( '\n' );

    console.error( message );
    console.error( error );
}


function integrateTextIntoLists(html) {
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, 'text/html');

    // Handle incorrect list item integration
    const lists = doc.querySelectorAll('ol, ul');
    lists.forEach(list => {
        let lastLi = list.lastElementChild; // Track the last <li> element

        // This is the node immediately following the current list
        let followingNode = list.nextSibling;
        while (followingNode) {
            const nextFollowingNode = followingNode.nextSibling;
            if (followingNode.nodeType === Node.ELEMENT_NODE && (followingNode.tagName === 'P' || followingNode.tagName === 'SPAN')) {
                // Move the node inside the last <li> if it's a <p> or <span>
                if (lastLi) {
                    lastLi.appendChild(followingNode);
                } else {
                    // If no <li> exists, create one
                    lastLi = document.createElement('li');
                    lastLi.appendChild(followingNode);
                    list.appendChild(lastLi);
                }
            } else {
                break; // Stop if the next node is not a <p> or <span>
            }
            followingNode = nextFollowingNode;
        }
    });

    return new XMLSerializer().serializeToString(doc);
}

修复方案

核心思路

从Word复制的内容,HTML结构会把编号和缩进子内容拆分为独立段落,而纯文本保留了完整的编号和缩进层级。改用纯文本解析,识别编号行和缩进行,构建正确的有序列表结构。

修改后的完整代码

DecoupledEditor
    .create( document.querySelector( '.editor' ), {
        link: {
            addTargetToExternalLinks: true,
            defaultProtocol: 'https://',
            allowedProtocols: [ 'https'],
            decorators: {
                addTargetToExternalLinks: {
                    mode: 'automatic',
                    callback: url => /^(https?:)?\/\//.test( url ),
                    attributes: {
                        target: '_blank',
                        rel: 'noopener noreferrer'
                    }
                }
            }
        },
    } )
    .then( editor => {
        window.editor = editor;
        document.querySelector( '.document-editor__toolbar' ).appendChild( editor.ui.view.toolbar.element );
        document.querySelector( '.ck-toolbar' ).classList.add( 'ck-reset_all' );

        editor.editing.view.document.on('clipboardInput', (evt, data) => {
            const textData = data.dataTransfer.getData('text/plain');
            if (textData) {
                const processedHtml = convertTextToProperList(textData);
                const viewFragment = editor.data.htmlProcessor.toView(processedHtml);
                const modelFragment = editor.data.toModel(viewFragment);
        
                editor.model.change(writer => {
                    editor.model.insertContent(modelFragment, editor.model.document.selection);
                });
        
                evt.stop();
            }
        });
    } )
    .catch( handleSampleError );


function handleSampleError( error ) {
    const issueUrl = 'https://github.com/ckeditor/ckeditor5/issues';

    const message = [
        'Oops, something went wrong!',
        `Please, report the following error on ${ issueUrl } with the build id &quot;m7gmbfwakd12-rztuvf3ew3ra&quot; and the error stack trace:`
    ].join( '\n' );

    console.error( message );
    console.error( error );
}


function convertTextToProperList(text) {
    const lines = text.split('\n').filter(line => line.trim() !== '');
    const ol = document.createElement('ol');
    let currentLi = null;

    lines.forEach(line => {
        // 匹配以数字+点开头的编号行
        if (/^\d+\./.test(line.trim())) {
            currentLi = document.createElement('li');
            // 移除编号前缀,保留内容
            const content = line.replace(/^\d+\.\s*/, '').trim();
            currentLi.textContent = content;
            ol.appendChild(currentLi);
        } else if (currentLi && line.trim()) {
            // 缩进行添加到当前列表项,用<br>保持换行
            const br = document.createElement('br');
            currentLi.appendChild(br);
            currentLi.appendChild(document.createTextNode(line.trim()));
        }
    });

    return ol.outerHTML;
}

关键说明

  • 优先处理纯文本:Word复制的纯文本保留了清晰的编号和缩进关系,比HTML更易解析
  • 编号行识别:通过正则/^\d+\./匹配编号开头的行,创建新的<li>元素
  • 缩进行合并:将后续缩进的文本添加到当前<li>中,用<br>维持换行格式
  • 构建标准<ol>结构:确保CKEditor能正确识别为有序列表

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 17:54:52