簡體   English   中英

反應:封裝選擇文本

[英]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 上

好吧,經過長時間的反復試驗,我相信我已經創建了一些可以解決您要求的東西。 如果沒有,請盡快告訴我。 在代碼之前簡要說明以及生產使用需要修復的內容。

需要優化

  • 更改為在 Document Fragment 而不是 Range 中編輯,然后替換為一個操作
    • 基本上要做到這一點,您需要轉換實時替換以替換節點,或者甚至更好地將整個編輯的塊拉入片段並在對象上而不是在 DOM 樹上運行它。 然而,我會把它留給你
    • 范圍性能
    • 文檔片段的基本原理
  • 找出一個完全多平台的解決方案,雖然這在桌面上的 firefox 上有效,但我不能保證對所有平台都適用,但是,對 Range 和 Selection Web API 的支持已被完全覆蓋,尤其是對於此處使用的功能。
  • 如果在 React 中使用它,您可能希望從本質上跟蹤帶有狀態的選定內容(鈎子或類,我更喜歡鈎子)。 本質上持有一個帶有 refs 的數組(可能是 react refs,但可能不是,如果您有任何問題,請查看React Ref Docs ),也許應該使用組件本身內的錨點,可以使用渲染道具或類似的東西進行引用反而? 我不確定,但也許這會有所幫助。

代碼

首先,如果您想查看實時版本,請訪問JSBin。 我希望這里有足夠的評論來解釋發生了什么,我試圖解釋我自己並保持這個版本的控制台干凈。 這是 TL; DR; 執行情況:

  1. 為文檔對象上的selectstart添加一個事件偵聽器(請將其限制為將使用它的容器)
  2. 在甚至觸發時為mouseup添加一個事件偵聽器(這是多平台中斷的地方:/)
  3. 如果按住 Ctrl 鍵(這可能也需要一些調整:/)返回 null 並繼續監聽 mouseup,如果沒有,則:
    1. 設置計數器並開始執行do...while(count < [select].rangeCount)
    2. 錯誤如果選擇雜交到其它元件被拋出,如果被拋出沒有錯誤,則利用[range].surroundWith([Node]);
    3. 如果拋出錯誤,那么下一個爬蟲將爬取這兩個元素並執行 HTML mutator 函數replaceLive來更改 HTML,請閱讀我在評論中的警告。
    4. 如果沒有更多元素可供爬行,則刪除mouseup事件偵聽器(良好實踐,加上流氓偵聽器永遠不會有趣......)
  4. 聆聽下一個選擇
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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM