[英]React: Encapsule selection text
我需要幫助。 在我的應用程序中,我有以下情況:
<p>
<strong> Lorem ipsum </strong> is simply dummy text from the printing and typesetting industry. Lorem Ipsum has been the industry
</p>
當我選擇文本時,我需要在短時間內將其封裝,但不丟失任何標簽。 例如:
假設你選擇了文本"ipsum is simply"
,如果你看,中間是 strong 的結束標簽,那么你真的會選擇以下內容: "ipsum </strong> is simply"
我需要這個選擇使用javascript,文本僅封裝在跨度內,這樣的內容仍然存在:
<p>
<strong> Lorem <span>ipsum</span> </strong> <span> is simply </span> dummy text from the printing and typesetting industry. Lorem Ipsum has been the industry
</p>
如您所見,在<strong>
,位於其中並被選中的文本位於標簽內,而在<strong>
之外,還有另一個被選中的文本。 另一件事是,如果有幾個段落並且在它們之間選擇了文本,則這些段落會被保留,但即便如此,所選文本還是被封裝在一個<span>
,如下所示:
<p> Lorem ipsum, absolem dut </p>
<p> Amen ipsum pain </p>
假設您選擇以下內容: "absolem dut Amen pain"
"absolem dut"
屬於一個段落,而“Amen pain”屬於另一個段落,這應該給出如下結果:
<p> Lorem ipsum, <span> absolem dut </span> </p>````
阿門痛苦ipsum```
我已經嘗試使用窗口的 getSelection 屬性中的 getContains 並且它不起作用,這會生成一個新段落。 如果有人知道如何做到這一點或知道如何使用 JS 節點做到這一點,或者如果問題有任何說明,我將不勝感激
我找到了一個普通的解決方案,因為這個功能在 React 上
好吧,經過長時間的反復試驗,我相信我已經創建了一些可以解決您要求的東西。 如果沒有,請盡快告訴我。 在代碼之前簡要說明以及生產使用需要修復的內容。
首先,如果您想查看實時版本,請訪問此JSBin。 我希望這里有足夠的評論來解釋發生了什么,我試圖解釋我自己並保持這個版本的控制台干凈。 這是 TL; DR; 執行情況:
selectstart
添加一個事件偵聽器(請將其限制為將使用它的容器)mouseup
添加一個事件偵聽器(這是多平台中斷的地方:/)do...while(count < [select].rangeCount)
[range].surroundWith([Node]);
replaceLive
來更改 HTML,請閱讀我在評論中的警告。mouseup
事件偵聽器(良好實踐,加上流氓偵聽器永遠不會有趣......)function remove(arr) {
for(const ele of arr) {
ele.replaceWith(ele.innerText);
}
}
function mouseUP (e) {
// Maybe have to handle this differently, just easier for now :/
if (e.ctrlKey === true) {return;}
let selectedText = document.querySelectorAll(".selected-text");
if (selectedText.length > 0) {
remove(selectedText);
}
let selection = document.getSelection();
//console.log(selection.anchorNode);
let count = 0;
do {
const span = document.createElement("span");
let range = selection.getRangeAt(count);
try {
range.surroundContents(span);
span.classList.add("selected-text");
} catch (e) {
//console.log(e);
// Will extract HTML from selection into div, sadly does not
// include the overall parent might be able to write some
// logic to that for you via startContainer and limit to text
// HTML element such as: p, span, etc. (textarea and input do
// not support selection according to mdn, in firefox!)
const div = document.createElement("div");
div.append(range.cloneContents());
// Ensuring that end will not be skipped, at-least I hope
// May need another check to verify next was not a text node
// Although, I think this can be safely assumed.
if (!range.endContainer instanceof HTMLElement) {
range.setEndAfter(range.endContainer);
}
// To track inside of extracted HTML?
let innerCount = 0;
// Will crawl through range
while (range.startContainer !== range.endContainer) {
let tmp = range.startContainer;
// To ignore the Nodes and only look at HTML Elements
if (!(tmp instanceof HTMLElement)) {
range.setStartAfter(tmp);
continue;
}
//console.log(tmp.tagName);
/**
* Replace Live
*
* Will take in a string and regex match it to the target
* HTMLElement's innerHTML then add span tag with content
* ToSsurround as child. Finally, setting innerHTML of child
* to the newly created DOMString.
*
* @param {HTMLElement} targetNode - Target to surround content inside of
* @param {String} contentToSurround - New Inner HTML for node
*/
function replaceLive (targetElement, newHTML) {
let origHTML = targetElement.innerHTML;
// If you wanted to add a css class or something do it here :)
origHTML = origHTML.replace(newHTML, `<span class="selected-text">${newHTML}</span>`);
console.log(origHTML);
// Warning this will cause a repaint for each execution!
// Extract into Document and replace the whole node if
// possible. For now this was faster, but please convert
// to use fragment to keep off of DOM Tree in production!
targetElement.innerHTML = origHTML;
}
try {
const htmlContent = div.querySelector(tmp.tagName).innerHTML;
replaceLive(tmp, htmlContent)
} catch (e) {
//console.log(div);
const node = div.childNodes.item(innerCount);
if (node instanceof HTMLElement) {
replaceLive(tmp, node.innerHTML);
} else if (node instanceof Node) {
replaceLive(tmp, node.nodeValue);
} else {
throw TypeError("Invalid node passed!");
}
}
range.setStartAfter(tmp);
innerCount++;
}
}
count++;
} while (count < selection.rangeCount);
document.removeEventListener('mouseup', mouseUP); // Clean up listener
}
// This failed inside jsbin
//document.addEventListener('select', e => console.log(e));
document.addEventListener('selectstart', () => {
/*
May have to add a check for platform to know if pointer events are
required instead. I tested on mobile, pointer events seemed spotty
so using mouse events only for now.
*/
document.addEventListener('mouseup', mouseUP);
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.