如何基于高度裁剪Lexical.js编辑器当前状态的内容?
如何基于高度裁剪Lexical.js编辑器当前状态的内容?
嘿,我之前也折腾过Lexical的内容裁剪需求,它的状态更新机制确实有点绕,给你理清楚可行的实现步骤和代码:
首先得修正你当前的监听逻辑——你在editorState.read()里直接调用editor.update()会触发状态冲突,因为read是只读上下文,不能同步发起状态更新。得把高度检查和更新逻辑异步处理,确保DOM渲染完成后再判断,避免拿到的高度不准确。
第一步:修正监听与高度检测逻辑
把高度检查放在requestAnimationFrame里,这样能保证我们拿到的是编辑器DOM更新后的真实高度:
// 加个标记避免裁剪过程中重复触发,防止无限循环 let isTrimming = false; editor.registerUpdateListener(({ editorState }) => { if (isTrimming) return; // 异步等待DOM渲染完成 requestAnimationFrame(() => { editorState.read(() => { const editorRoot = editor.getRootElement(); if (!editorRoot) return; // 获取当前内容的总高度(包含溢出部分) const currentContentHeight = editorRoot.scrollHeight; const maxAllowedHeight = 500; // 你的预设高度限制 if (currentContentHeight > maxAllowedHeight) { isTrimming = true; // 发起内容裁剪的状态更新 editor.update(() => { trimContentToFitHeight(editor, maxAllowedHeight); }, { tag: 'content-trim' }); } }); }); });
第二步:核心的内容裁剪实现
Lexical的内容是基于节点树的,我们可以从最后一个节点倒序移除,每次移除后检查高度,直到内容高度符合要求。如果需要更精细的文本截断(而非移除整个节点),也可以针对文本节点单独处理:
方案1:移除整节点(简单直接)
适合内容以段落、块级元素为主的场景:
function trimContentToFitHeight(editor, maxHeight) { const rootNode = editor.getEditorState().getRootNode(); // 复制子节点数组,避免遍历中修改原数组导致的异常 const childNodes = [...rootNode.getChildren()]; // 从最后一个节点开始倒序移除 for (let i = childNodes.length - 1; i >= 0; i--) { const targetNode = childNodes[i]; rootNode.remove(targetNode); // 检查移除后的内容高度 const editorRoot = editor.getRootElement(); if (editorRoot && editorRoot.scrollHeight <= maxHeight) { break; // 高度达标,停止裁剪 } } // 重置标记,允许后续的裁剪触发 isTrimming = false; }
方案2:精细截断文本节点
如果需要保留部分文本而非移除整个节点,可以针对文本节点做内容截断:
function trimContentToFitHeight(editor, maxHeight) { const rootNode = editor.getEditorState().getRootNode(); const childNodes = [...rootNode.getChildren()]; let trimmed = false; // 从最后一个节点倒序处理 for (let i = childNodes.length - 1; i >= 0; i--) { const node = childNodes[i]; if (node.getType() === 'text') { // 文本节点:逐步截断内容 let textContent = node.getTextContent(); while (textContent.length > 0) { textContent = textContent.slice(0, -1); node.setTextContent(textContent); // 检查高度 const editorRoot = editor.getRootElement(); if (editorRoot && editorRoot.scrollHeight <= maxHeight) { trimmed = true; break; } } if (trimmed) break; } else { // 非文本节点:直接移除 rootNode.remove(node); const editorRoot = editor.getRootElement(); if (editorRoot && editorRoot.scrollHeight <= maxHeight) { trimmed = true; break; } } } isTrimming = false; }
额外注意点
- 避免无限循环:一定要加
isTrimming标记,不然裁剪后的状态更新会再次触发监听,导致反复裁剪。 - 高度检测的准确性:用
scrollHeight而非offsetHeight,因为scrollHeight会包含溢出容器的内容总高度,更符合我们的需求。 - 节点类型适配:如果你的编辑器有自定义节点(比如图片、代码块),要在裁剪逻辑里单独处理这些节点的高度计算,比如图片节点的高度可以通过DOM节点的offsetHeight获取。
备注:内容来源于stack exchange,提问作者Skull
相关产品推荐
相关产品推荐

