英文:
Lexcial Editor: Spaces inside HTML cause "cannot insert a non-element into a root node" error
问题
I work with a database that contains articles saved as simple HTML and want to switch to a WYSIWYG that plays well with React so I am looking into Lexical.
When "feeding" Lexical with the initial HTML, I need to transform it into Nodes first. But, when the initial HTML is not super clean and contains, for example, spaces between the HTML tags, the transformation breaks.
So this as an input string works:
const htmlString = '<p><b>I am bold</b></p><p><b><i>I am bold and italic</i></b></p>';
But this does not:
const htmlString = '<p><b>I am bold</b></p> <p><b><i>I am bold and italic</i></b></p>';
(because of the space between the p-tags)
How can I fix this? Since I cannot ensure the source code is well-formed HTML.
function InitialContentPlugin() {
const [editor] = useLexicalComposerContext();
editor.update(() => {
const htmlString = '<p><b>I am bold</b></p><p><b><i>I am bold and italic</i></b></p>';
const parser = new DOMParser();
const dom = parser.parseFromString(htmlString, 'text/html');
// Once you have the DOM instance, it's easy to generate LexicalNodes.
const nodes = $generateNodesFromDOM(editor, dom);
// Select the root
$getRoot().select();
// Insert them at a selection.
$insertNodes(nodes);
});
return null;
}
Edit:
I figured out that the empty space creates a TextNode. I cannot insert this into the root because TextNodes are "leaves" and therefore not allowed as direct children of the root. Need to find a way now how to fix this.
英文:
I work with a database that contains articels saved as simple HTML and want to switch to a WYSIWYG that plays well with React so I am looking into Lexical.
When "feeding" Lexical with the intitial HTML I need to transform it into Nodes first. But: When the initial HTML is not superclean and contains e.g. spaces between the HTML tags, the transformation breaks.
So this as input string works:
const htmlString = '<p><b>I am bold</b></p><p><b><i>I am bold and italic</i></b></p>';
But this does not:
const htmlString = '<p><b>I am bold</b></p> <p><b><i>I am bold and italic</i></b></p>';
(because of the space between the p-tags)
How can I fix this? Since I cannot ensure the source code is well-formed HTML.
function InitialContentPlugin() {
const [editor] = useLexicalComposerContext();
editor.update(() => {
const htmlString = '<p><b>I am bold</b></p><p><b><i>I am bold and italic</i></b></p>';
const parser = new DOMParser();
const dom = parser.parseFromString(htmlString, 'text/html');
// Once you have the DOM instance it's easy to generate LexicalNodes.
const nodes = $generateNodesFromDOM(editor, dom);
// Select the root
$getRoot().select();
// Insert them at a selection.
$insertNodes(nodes);
});
return null;
}
Edit:
I figured out that the empty space creates a TextNode. I cannot insert this into the root because TextNodes are "leaves" and therefore not allowed as direct children of the root. Need to find a way now how to fix this.
答案1
得分: 4
我自己找到了一个简单的解决方案,可以移除根级别的“叶子”节点:
editor.update(() => {
const htmlString = '<p><b>I am bold</b></p><br><p><b><i>I am bold and italic</i></b></p>';
const parser = new DOMParser();
const dom = parser.parseFromString(htmlString, 'text/html');
// 一旦您有了DOM实例,就很容易生成词法节点。
const nodes = $generateNodesFromDOM(editor, dom)
.map((node) => {
if (node.getType() === 'text') {
if (node.getTextContent().trim() === '') {
return null;
} else {
return $createParagraphNode().append(node);
}
}
if (node.getType() === 'linebreak') {
return null;
}
return node;
})
.filter((node) => !!node);
// 选择根节点
$getRoot().select();
// 在选择位置插入它们。
$insertNodes(nodes);
});
英文:
OK I figured out a simple solution myself that removes "leaf"-nodes on root level:
editor.update(() => {
const htmlString = '<p><b>I am bold</b></p><br><p><b><i>I am bold and italic</i></b></p>';
const parser = new DOMParser();
const dom = parser.parseFromString(htmlString, 'text/html');
// Once you have the DOM instance it's easy to generate LexicalNodes.
const nodes = $generateNodesFromDOM(editor, dom)
.map((node) => {
if (node.getType() === 'text') {
if (node.getTextContent().trim() === '') {
return null;
} else {
return $createParagraphNode().append(node);
}
}
if (node.getType() === 'linebreak') {
return null
}
return node;
})
.filter((node) => !!node);
// Select the root
$getRoot().select();
// Insert them at a selection.
$insertNodes(nodes);
});
答案2
得分: 0
以下是代码部分的中文翻译:
const nodes = $generateNodesFromDOM(editor, dom);
// 根节点(叶子节点)必须是块级元素,但在解析HTML时,嵌套的`<div>`结构被裁剪为仅保留最终文本节点,从而破坏了获取编辑器状态的步骤。
// 我们的解决方案是将连续的根文本节点汇集到额外的段落节点中
const ajustedNodes: LexicalNode[] = [];
let currentAdditionalParagraphNode: ParagraphNode | null = null;
for (const node of nodes) {
// 仅解析第一层
if (!node.getParent()) {
const nodeType = node.getType();
if (nodeType === 'text') {
if (node.getTextContent().trim() === '') {
continue;
}
if (!currentAdditionalParagraphNode) {
// 在第一层创建段落
currentAdditionalParagraphNode = $createParagraphNode();
ajustedNodes.push(currentAdditionalParagraphNode);
}
if (currentAdditionalParagraphNode) {
currentAdditionalParagraphNode.append(node);
}
} else {
currentAdditionalParagraphNode = null;
if (nodeType === 'linebreak') {
continue;
} else {
ajustedNodes.push(node);
}
}
}
}
const root = $getRoot();
root.append(...ajustedNodes);
希望这有助于您理解代码的功能。如果您需要更多的帮助,请随时提出。
英文:
Thought at first @nerdess solution would work great but it means italic/bold text will be a new paragraph, which break the render.
Here my "final" homemade solution (that gathers root text leaves into paragraphs):
const nodes = $generateNodesFromDOM(editor, dom);
// Root nodes (leaves) must be blocks, but when parsing HTML nested structures of `<div>` are cropped to only keep
// the final text node, breaking the step to get the editor state
// Our solution is to gather successive root text nodes into an additional paragraph node
const ajustedNodes: LexicalNode[] = [];
let currentAdditionalParagraphNode: ParagraphNode | null = null;
for (const node of nodes) {
// Only parse the first layer
if (!node.getParent()) {
const nodeType = node.getType();
if (nodeType === 'text') {
if (node.getTextContent().trim() === '') {
continue;
}
if (!currentAdditionalParagraphNode) {
// Create a paragraph on the first layer
currentAdditionalParagraphNode = $createParagraphNode();
ajustedNodes.push(currentAdditionalParagraphNode);
}
if (currentAdditionalParagraphNode) {
currentAdditionalParagraphNode.append(node);
}
} else {
currentAdditionalParagraphNode = null;
if (nodeType === 'linebreak') {
continue;
} else {
ajustedNodes.push(node);
}
}
}
}
const root = $getRoot();
root.append(...ajustedNodes);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论