ابزار برای تقسیم محتوای HTML در افزونه CK ویرایشگر بدون شکستن سلسله مراتب

مقدمه
من در یک پروژه React کار می کردم که از ویرایشگر CK به عنوان افزونه ویرایشگر استفاده می کند. و عملکرد ما تولید یک سند PDF با پردازش محتوا در ویرایشگر است. و ما از این هدف استفاده می کردیم تا HTML را به Word به PDF تبدیل کنیم.
بعداً ما یک نیاز جدید برای اجرای برنامه Break Break از افزونه CK Editor دریافت کردیم. این یک گام بسیار ساده برای شامل افزونه Break Break در ویرایشگر CK بود زیرا یکی از ابزار داخلی بود و ما فقط باید این افزونه را اجازه دهیم.
اما سخت ترین بخش پردازش این صفحه در هنگام تولید سند کلمه با استفاده از Aspose بود. انتظار این است که ، هنگامی که یک صفحه استراحت وجود دارد ، Document Word باید بدون شکستن سلسله مراتب و سبک های اعمال شده بر روی مؤلفه والدین ، محتوا را به 2 صفحه تقسیم کند. اما ، هنگامی که ما سعی کردیم کلمه کلمه را با استفاده از Aspose تولید کنیم ، سبک های اعمال شده در والدین بازتاب دهنده نبودند و سبک های اعمال شده به کلاس خاص که وابسته به کلاس والدین است نیز در صفحه دوم پس از تقسیم اعمال نشده است.
مشکل:
برای رفع این مسئله ، ما باید HTML را بر اساس عنصر شکست صفحه به عنوان جداکننده ، بدون شکستن سلسله مراتب و کلاس ها ، به 2 قسمت تقسیم کنیم. اما هیچ بسته نرم افزاری در NPM برای تقسیم HTML در دسترس نبود. همچنین هیچ راه حل مناسبی برای این کار در هر یک از سایتهای وبلاگ و همچنین گپ GPT وجود ندارد.
بیانیه مشکل
عنصر HTML داده شده را بر اساس عنصر جداکننده بدون شکستن سلسله مراتب تقسیم کنید
راه حل:
این راه حل دارای 3 قسمت مختلف است.
هر وقت یک عنصر شکست صفحه را مشخص کرد ، سلسله مراتب را ذخیره کرده و عناصر را تقسیم کنید
اگر یک مورد لیست پیدا کرد ، برای حفظ سلسله مراتب ، ترتیب لیست را ذخیره کنید
پس از شناسایی صفحه در داخل لیست ، از این لیست سلسله مراتب سفارش برای حفظ سفارش برای عناصر باقیمانده در لیست استفاده کنید.
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 seperators = 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 === seperators.length) {
if (["OL", "UL"].includes(nestedChild.nodeName)) {
listIndex[activeListIndex]--;
temp2 = defineNumbering(clonedRoot(childHierarchy), listIndex);
}
temp2.appendChild(nestedChild.cloneNode(true));
} else if (
nestedChild === seperators[sepInd] ||
(nestedChild.childNodes.length === 1 &&
nestedChild.firstChild === seperators[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(seperators[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 === seperators.length) {
tempEl.appendChild(child.cloneNode(true));
} else if (child === seperators[sepInd]) {
splitVals.push(tempEl);
tempEl = clonedEl.cloneNode(false);
sepInd++;
} else if (
child.contains(seperators[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("");
};
export { splitEl };
در این مثال فوق ، خطوط بین متون ، شبیه به شکست صفحه است. و در زیر پردازش انجام شده در HTML ورودی است
هنگامی که استراحت صفحه بین پاراگراف ها مشخص می شود ، به سادگی HTML را تقسیم می کند.
هنگامی که چندین صفحه وجود دارد که یکی از آنها را در زیر دیگری قرار می دهد ، یک ظرف خالی بین شکاف صفحه ایجاد می کند.
هنگامی که یک صفحه در داخل لیست ها وجود دارد ، سفارش را حفظ می کند و همان لیست را پس از تقسیم ادامه می دهد. به عنوان مثال ، ما قبل از وقفه صفحه ، 1. 1. 1. (3 سطح) داریم و پس از تقسیم ، محتوا از 1. 1. 2 تا 1. 1 ادامه می یابد.
همچنین ، قبل از آخرین شکست صفحه ، پس از لیست 2 لیست ، یک صفحه استراحت وجود دارد و پس از استراحت صفحه ، ما یک مورد لیست 3 نداریم ، در عوض ما یک لیست فرعی داریم. بنابراین سلسله مراتب باید مانند 2. 2. 1 جریان یابد.
در زیر ساختار عناصر برای ورودی و خروجی HTML قرار دارد.