簡體   English   中英

如何迭代/循環包含 HTML 標記的文本字符串,同時忽略 Javascript 中的那些 HTML 標記?

[英]How to iterate/loop over a text string that contains HTML tags while ignoring those HTML tags in Javascript?

我有一些動態復制文本,一個字符串,其中包含一些用於換行和樣式的 HTML 標記。 例如:“ This is a sample <b>piece of text</b> string ”。

我希望遍歷文本,以便可以將每個字符包裝在<span> </span>標簽中,以便為每個字符設置動畫。 但是,我需要能夠忽略 HTML 標記,例如上面的<b></b>標記,以便與這些標記關聯的任何/所有中斷或 styles 保持原位......?

我可以在文本字符串上運行一個簡單、干凈for of循環,以便將所有內容包裝在 span 標簽中,然后將結果推送到我想要的 div,見下文:

textString = "This is a sample <b>piece of text</b> string."

for (let char of textString) {
    let span = document.createElement('span');
    span.textContent = char;
    let test = document.querySelector("#root");
    test.appendChild(span);
}

這將把該字符串中的所有內容都包含在一個跨度標簽中,但是這些標簽顯然會顯示在屏幕上——這不是我需要的!

我知道這個正則表達式: str.replace( /(<([^>]+)>)/ig, ''); 將從字符串中刪除所有 HTML 標簽,但是,正如我所說,我需要標簽實際上保留在原位以進行樣式設置?

對此的任何幫助將不勝感激......

PS:HTML 標簽必須保留/替換它們的原因,我不能只是在之后設置樣式是因為我收到的文本字符串來自輸入了標簽的提要,它們被用來創建換行符和顏色 styles 用於輸入到創意 HTML5 橫幅廣告中的部分文本。

這是一個助手 function:

const letterize = el => el instanceof Text
  ? el.data
      .split('')
      .map(l => l === ' ' ? l : '<letter>' + l + '</letter>')
      .join('')
  : (
      el.innerHTML = ([...el.childNodes] || []).map(letterize).join(''),
      el.outerHTML
    );

在遍歷 DOM 樹時遞歸調用。

假設是遞歸地沿着子樹遍歷 DOM 結構,最終將導致沒有子節點或Text實例。 我知道我可能是錯的並且沒有徹底測試它(不確定它如何與<script><style><iframe>標簽或整個文檔一起使用,但我考慮了那些邊緣情況)。 對於您的情況,這應該足夠了。

文本節點內容被分解,每個字母都被包裹在一個定制的 html 包裝器中。 其他一切都保持不變。
要同時包裝空格字符,請刪除l === ' '? l: l === ' '? l:從第一個條件開始。
將包裝器(上面的<letter> )更改為您想要的任何內容:例如: <span>

測試:

 const letterize = el => el instanceof Text? el.data.split('').map(l => l === ' '? l: '<letter>' + l + '</letter>').join(''): ( el.innerHTML = ([...el.childNodes] || []).map(letterize).join(''), el.outerHTML ); [...document.querySelectorAll('.test')].map(letterize)
 letter:hover { background-color: red; color: white; cursor: none; } body { font-family: monospace; font-size: 20px; } div > span { color: red; } code { padding: 1rem; background-color: #272727; color: white; display: block; margin-bottom: 1rem; font-size: 14px; }
 <p class="test">This is a "<i>.test</i>"</p> <code class="another test">And this is another one...</code> <h1 class="test">I'll be parsed</h1> <h4>I won't be parsed</h4> <div style="border: 1px solid red;" class="test"> I am a div. <span>And I am a <b>span</b>.</span> </div>

大警告:上述內容(就像任何替換您的 DOM 元素一樣 - 例如設置innerHTMLouterHTML值)將從該 DOM 中刪除所有事件。 如果保留事件偵聽器是一項要求……這是不同程度的復雜性。
我不會在這里介紹它。


注意:它不必是頁面 DOM 的一部分,您可以在 memory 中完成所有操作(但它確實使用 DOM API;假設您將在瀏覽器中運行它):

 const letterize = el => el instanceof Text? el.data.split('').map(l => l === ' '? l: '<span class="letter">' + l + '</span>').join(''): ( el.innerHTML = ([...el.childNodes] || []).map(letterize).join(''), el.outerHTML ); const letterizeHTML = input => { const d = document.createElement('div'); d.innerHTML = input; letterize(d); return d.innerHTML; } console.log( letterizeHTML(`<p class="test">This is a <b>test</b>.</p> <code class="another test">And this is another one...</code>`) );

這是一個 animation 測試(忍不住:):

 const letterize = el => el instanceof Text? el.data.split('').map(l => l === ' '? l: '<letter>' + l + '</letter>').join(''): ( el.innerHTML = ([...el.childNodes] || []).map(letterize).join(''), el.outerHTML ); [...document.querySelectorAll('.test')].map(letterize); [...document.querySelectorAll('letter')].forEach((l, k) => { setTimeout(() => l.classList.add('on'), 10*k) })
 * { box-sizing: border-box; } p { margin-bottom: 0; } body { perspective-origin: center; position: relative; min-height: 100vh; overflow-x: hidden; perspective: 0; font-family: sans-serif; padding-bottom: 50vh; } letter { transform-style: preserve-3d; perspective-origin: 100000px 0; display: inline-block; position: relative; opacity: 0; transform: perspective(120px) translateZ(120px) translateY(15px); transition: transform 1s cubic-bezier(.5,0,.3,1), opacity 1.5s ease-out; } letter.on { opacity: 1; transform: perspective(120px) translateZ(0) translateY(0); } letter body { font-family: monospace; font-size: 20px; overflow: hidden; } div > span { color: red; } code { padding: 1rem; background-color: #272727; color: white; display: block; margin-bottom: 1rem; font-size: 14px; }
 <p class="test">This is a "<i>.test</i>"</p> <code class="another test">And this is another one...</code> <h1 class="test">I'll be parsed</h1> <h4>I won't be parsed</h4> <div style="border: 1px solid red;" class="test"> I am a div. <span>And I am a <b>span</b>.</span> <p>Lorem ipsum dolor sit amet. The cat is sleeping. <br>Over.</p> <p>Sometimes a financial Bacardi Silver flies into a rage, but a line dancer always makes a pact with the Keystone light, When a jersey cow about some Heineken is familiar. a tipsy jersey cow makes a pact with the cantankerous Coors. <br><br> Hold my beer!</p> </div>

最后一點:雖然了解這些東西是如何工作的以及如何從頭開始編寫代碼是很有趣和有用的,但如果你對動畫很認真,我建議你看看更嚴肅的工具,它提供了方便的補間、鏈接、暫停和/或恢復動畫,在不遠的過去,編碼需要更長的時間。
查看有關GSAP 3 中 API 更改的視頻(以及它的功能,幾乎沒有代碼)。 或在他們的展示頁面上大吃一驚。

PS我不以任何方式隸屬於greensock。 我只是對動畫充滿熱情,出於顯而易見的原因,我已經使用了他們的庫。

這就是我修復它的方法:

const isElement = node => node.nodeType === 1; // element
const isTextNode = node => node.nodeType === 3; // text 
const wrap = node => (node || '').split('').map(c => `<span>${c}</span>`).join('');

function wrapNodes(nodeList) {
  nodeList = Array.from(nodeList).map(node => {
    if (isTextNode(node))  {
      return wrap(node.textContent);
    }
    if (isElement(node)) {
      node.innerHTML = wrapNodes(node.childNodes)
      return node.outerHTML;
    }
  });
  
  return nodeList.join('');
}

let textSample = "This is some copy text with a <b>Bold</b> sample to <em>test</em>."
const div = document.createElement('div');
div.innerHTML = textSample; 
div.innerHTML = wrapNodes(div.childNodes);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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