CKEditor插件中分割HTML內(nèi)容的實用方法——確保分頁后樣式不丟失
简介
我在一个使用Ck Editor作为编辑器插件的React项目中工作。我们的功能是通过处理编辑器中的内容来生成PDF文档。我们使用Aspose将HTML转换成Word,然后转换成PDF。
后来我们有了一个新的需求,即实现Ck Editor插件中的分页功能。实现分页功能非常简单,因为Ck Editor已经内置了该插件,我们只需启用即可。
但最困难的部分是在使用Aspose生成Word文档时处理页面分割。期望是,Word文档应该在不破坏层级结构的情况下,将内容分成两页,并保留父组件的样式。然而,当我们尝试使用Aspose生成Word文档时,我们在第二页中没有看到父组件的样式,以及依赖于父类的特定类的样式。
问题:
为了修复这个问题,我们不得不将HTML文档根据页面分割元素分成两部分,同时保持其层次结构和类不变。然而,在npm上没有现成的工具包可供使用来分割HTML。此外,在任何博客或者Chat GPT上也找不到合适的方法。
问题描述
根据分隔符元素拆分给定的HTML元素,确保层次结构不被破坏。
解决方法:
这个解决方案包含三个部分。
每当识别到分页符时,则保存层级结构并拆分元素;同时,当找到列表项时,则存储列表顺序以保持层级。在列表中识别到分页后,使用此列表顺序来保持列表中剩余元素的顺序。
const defineNumbering = (element: Node, startInd: number[]) => {
let ind = startInd.length - 1;
const clonedInd = [...startInd];
clonedInd[ind]++;
while (ind >= 0) {
if (["UL", "OL"].includes(element.nodeName)) {
(element as Element).setAttribute("start", `${clonedInd[ind]}`);
ind--;
}
if (element.parentNode) element = element.parentNode;
}
while (element.lastChild) element = element.lastChild;
return element;
};
const getRoot = (element: Node) => {
let root = element;
while (root.parentElement) {
root = root.parentElement;
}
return root;
};
const clonedRoot = (element: Node) => {
let clonedRoot = getRoot(element).cloneNode(true);
while (clonedRoot.lastChild) clonedRoot = clonedRoot.lastChild;
return clonedRoot;
};
const splitEl = (element: Element | null, selector: string) => {
const clonedEl = element?.cloneNode(true) as Element;
const separators = clonedEl.querySelectorAll(selector);
let sepInd = 0;
const splitVals: Node[] = [];
let tempEl = clonedEl.cloneNode(false);
let hierarchyEl: Node;
let temp2: Node;
let resetTemp2 = false;
let listIndex: number[] = [];
let activeListIndex: number;
const processChild = (childEl: ChildNode, hierarchy: Node, initial: Node) => {
let childHierarchy = clonedRoot(hierarchy);
temp2 = clonedRoot(initial);
childEl.childNodes.forEach((nestedChild, index) => {
if (nestedChild.nodeName === "LI") listIndex[activeListIndex]++;
if (sepInd === separators.length) {
if (["OL", "UL"].includes(nestedChild.nodeName)) {
listIndex[activeListIndex]--;
temp2 = defineNumbering(clonedRoot(childHierarchy), listIndex);
}
temp2.appendChild(nestedChild.cloneNode(true));
} else if (
nestedChild === separators[sepInd] ||
(nestedChild.childNodes.length === 1 &&
nestedChild.firstChild === separators[sepInd])
) {
if (index === childEl.childNodes.length - 1) resetTemp2 = true;
else if (index === 0 && temp2.parentNode) {
const removeChild2 = temp2;
temp2 = temp2.parentNode;
temp2.removeChild(removeChild2);
listIndex[activeListIndex]--;
}
tempEl.appendChild(getRoot(temp2).cloneNode(true));
temp2 = defineNumbering(clonedRoot(childHierarchy), listIndex);
splitVals.push(tempEl);
sepInd++;
tempEl = clonedEl.cloneNode(false);
} else if (nestedChild.contains(separators[sepInd])) {
childHierarchy.appendChild(nestedChild.cloneNode(false));
childHierarchy = childHierarchy.lastChild ?? childHierarchy;
temp2.appendChild(nestedChild.cloneNode(false));
if (["OL", "UL"].includes(nestedChild.nodeName)) {
const initialNumber = (nestedChild as Element).getAttribute("start");
listIndex.push(initialNumber ? parseInt(initialNumber) : 0);
activeListIndex++;
}
processChild(nestedChild, childHierarchy, temp2);
if (childHierarchy.parentNode && temp2.parentNode) {
const tempChild = childHierarchy;
childHierarchy = childHierarchy.parentNode;
if (
childHierarchy.nodeName === "LI" &&
["OL", "UL"].includes(tempChild.nodeName)
) {
listIndex.pop();
activeListIndex--;
}
childHierarchy.removeChild(tempChild);
}
if (resetTemp2 && temp2.parentNode) {
resetTemp2 = false;
const tempChild2 = temp2;
temp2 = temp2.parentNode;
temp2.removeChild(tempChild2);
} else temp2 = temp2.parentNode || temp2;
} else {
temp2.appendChild(nestedChild.cloneNode(true));
}
});
};
clonedEl.childNodes.forEach((child) => {
if (sepInd === separators.length) {
tempEl.appendChild(child.cloneNode(true));
} else if (child === separators[sepInd]) {
splitVals.push(tempEl);
tempEl = clonedEl.cloneNode(false);
sepInd++;
} else if (
child.contains(separators[sepInd]) &&
["OL", "UL"].includes(child.nodeName)
) {
const initialNumber = (child as Element).getAttribute("start");
listIndex.push(initialNumber ? parseInt(initialNumber) : 0);
activeListIndex = 0;
hierarchyEl = child.cloneNode(false);
processChild(child, hierarchyEl, hierarchyEl);
tempEl.appendChild(getRoot(temp2));
} else {
tempEl.appendChild(child.cloneNode(true));
}
listIndex = [];
activeListIndex = -1;
});
splitVals.push(tempEl);
return splitVals
.map((val) => (val as Element).outerHTML)
.join("<div class='page-break'></div>");
};
export { splitEl };
全屏 退出
在上面的例子中,文本之间的线看起来像一页的分隔。如下所示,这是处理输入HTML时的操作:
当在段落间识别到分页符时,它只是简单地拆分html。
当有多页分页符连续出现时,它会在分页符之间创建一个空容器。
当分页符出现在列表中时,它会保持原有的顺序,并在拆分后继续相同的列表编号。例如,分页符前是1. 1. 1,分页符后从1. 1. 2延续至1. 1. 3。因此,在输出的HTML中,你会看到分页符后的列表从1. 1. 2开始。
此外,在倒数最后一个分页符前,第二个列表项后有一个分页符,而在分页符之后,没有第三个列表项,而是出现了子列表。因此,层级结构应为2. 2. 1。
以下是输入和输出HTML的结构元素。
如图所示:
这是一张关于...的图片(请根据实际情况填写描述)。
共同學習,寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章