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

为何document.execCommand()与contenteditable div会拆分格式化文本为节点?

问题:LWC富文本组件格式化后无法获取带格式标签的完整内容

我有一个LWC组件,在另一个组件中通过document.execCommand(action)对其中选中的文本执行格式化操作。

组件HTML结构

<template>
    <div contenteditable="true">
    </div>
    <div class="clone"> </div>
</template>

组件JS代码(原实现)

import { api, track, LightningElement } from 'lwc';

export default class appRichText extends LightningElement {
    @api textvalue;

    renderedCallback(){
        this.divRich = this.template.querySelector("div");
        this.divRich.innerHTML = this.textvalue;
        this.clone = this.template.querySelector(".clone");
    }

    @api
    getHTML(){
        let clonedHtml = this.divRich.cloneNode(true);
        clonedHtml.removeAttribute("contenteditable");
        let completeHTML = this.getDivContentAsString(clonedHtml);
        return completeHTML;
    }

    getDivContentAsString(divElement) {
        let contentString = "";
        function traverseNodes(node) {
            if (node.nodeType === Node.TEXT_NODE) {
              contentString += node.textContent;
            } else if (node.nodeType === Node.ELEMENT_NODE) {
                contentString += node.textContent;
                for (let childNode of node.childNodes) {
                    traverseNodes(childNode);
                }
            } 
        } 
        traverseNodes(divElement);
        return contentString;
      }
}

遇到的问题

  • 执行execCommand后,可编辑div中的选中文本会被拆分为多个节点(格式化前为连续文本节点,格式化后生成带格式的子节点)。
  • 直接获取div的innerHTML/outerHTML,仅能得到第一段未格式化的文本,无法获取带格式标签的完整内容。
  • 自定义的节点遍历方法只能提取纯文本,无法保留格式标签;控制台显示格式化后的节点为HTMLCollection,但通过divElement.children[0]获取失败,遍历childNodes时会跳过这些格式节点。
  • 尝试过克隆节点到其他容器、创建范围、多种子节点获取方式等,均无效。

解决方案

问题根源

LWC的Shadow DOM隔离机制会影响对格式化后动态生成节点的直接访问,加上execCommand操作后的节点结构变化,常规的innerHTML获取可能因组件渲染时机或Shadow DOM限制失效。

优化后的实现

方案1:直接获取innerHTML(推荐)

修改getHTML方法,同时调整渲染逻辑避免覆盖格式化后的内容:

import { api, LightningElement } from 'lwc';

export default class AppRichText extends LightningElement {
    @api textvalue;
    divRich;

    renderedCallback() {
        // 初始化元素引用
        if (!this.divRich) {
            this.divRich = this.template.querySelector('[contenteditable="true"]');
        }
        // 仅在初始渲染或textvalue更新时同步内容,避免覆盖用户操作
        if (this.divRich && this.divRich.innerHTML !== this.textvalue) {
            this.divRich.innerHTML = this.textvalue;
        }
    }

    @api
    getHTML() {
        // 直接获取可编辑div的完整innerHTML
        return this.divRich?.innerHTML || '';
    }
}

方案2:正确遍历节点并保留格式标签

如果需要手动遍历节点生成HTML字符串,修改遍历逻辑以保留标签结构:

getDivContentAsString(divElement) {
    let contentString = "";
    function traverseNodes(node) {
        if (node.nodeType === Node.TEXT_NODE) {
            contentString += node.textContent;
        } else if (node.nodeType === Node.ELEMENT_NODE) {
            // 拼接元素开始标签(包含属性)
            const tagName = node.tagName.toLowerCase();
            contentString += `<${tagName}`;
            // 遍历并拼接所有属性
            Array.from(node.attributes).forEach(attr => {
                contentString += ` ${attr.name}="${attr.value}"`;
            });
            contentString += '>';
            // 递归遍历子节点
            Array.from(node.childNodes).forEach(childNode => {
                traverseNodes(childNode);
            });
            // 拼接元素结束标签
            contentString += `</${tagName}>`;
        }
    }
    traverseNodes(divElement);
    return contentString;
}

关键说明

  1. 渲染时机控制:在renderedCallback中增加内容一致性判断,防止每次渲染时用初始值覆盖用户已格式化的内容。
  2. Shadow DOM访问:组件内部可直接通过querySelector访问Shadow DOM内的元素,无需克隆节点即可获取完整innerHTML。
  3. 节点遍历优化:手动遍历时需拼接元素的标签和属性,而不是仅提取textContent,才能保留格式结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 02:35:29