用文本节点替换`<span>`:是程序员错误,还是浏览器错误?

问题描述 投票:0回答:2

我正在尝试替换 HTML 中一些看起来像

<span id="Something" />
的占位符。使用以下 JavaScript 代码仅替换了第一次出现的情况,我想知道为什么:

function set_placeholder(cls, txt)
{
    txt = document.createTextNode(txt);
    for (var e of document.getElementsByClassName(cls)) {
        e.parentNode.replaceChild(txt, e);
    }
}

替换

span
后,集合似乎无法找到下一个匹配项。

所以我尝试使用以下变体来插入文本:

function set_placeholder(cls, txt)
{
    for (var e of document.getElementsByClassName(cls)) {
        e.innerText = txt;
    }
}

现在所有的出现都被替换了,让我想知道这是我的错,还是浏览器(Firefox 102)的错,当第一个变体失败时。

HTML 示例

实际的 HTML 要复杂得多,但这里有一些示例:

<html>
 <p><span class="ph-customer" /> bestellte am <span class="ph-customer-date" /> folgende Artikel:</p>
<!-- ... -->
 <table>
  <tr>
    <td><span class="ph-customer-date" />,
    <span class="ph-customer-name" /></td>
  </tr>
 </table>
<!-- ... -->
</html>

例如,

ph-customer-date
出现两次,但调用
set_placeholder('ph-customer-date', '30.12.2023')
只会替换第一次出现。

javascript dom replace collections
2个回答
0
投票

实施是正确的,20年来一直如此。您所观察到的是,您正在处理实时

HTMLCollection
,并且
for
将使用
HTMLCollection.prototype[Symbol.iterator]
来获取成员。正如其他人正确指出的那样,
replaceChild
将弹出第0个项目,第1个将是第0个,第2个将是第一个。相反,将其转换为静态的东西:

Array.from(...)

您的第二个示例之所以有效,是因为它修改了对象引用的内容(在本例中为

span
节点),并且没有从
HTMLCollection
中删除指向对象的指针;这样的“安全”方法是
innerHTML
textContent
innerText
。使用
outerHTML
也会遇到同样的问题,它实际上会删除指针。

记住

HTMLCollections
是直播,您还需要注意
NodeList
是否可以根据直播或非直播表现出类似的行为。
Node.childNodes
返回活动的,但带有
document.querySelector
document.querySelectorAll
的其他所有内容都将返回静态
NodeList

通过

Array.from

将它们转换为常规数组通常是安全的

或使用

document.querySelectorAll(".whateverclass")

Array.from(document.getElementsByTagName("DIV")).forEach((div, i) => {
  if(!i){
    //method1 - DOES NOT WORK
    for (const span of div.getElementsByClassName("a")) {
      div.replaceChild(document.createTextNode("!"), span)
    }
  } else {
    //method2 - WORKS
    for (const span of Array.from(div.getElementsByClassName("b"))) {
      div.replaceChild(document.createTextNode("!"), span)
    }
  }
})
<div>
  <span class="a">x</span>
  <span class="a">y</span>
  <span class="a">z</span>
</div>

<div>
  <span class="b">u</span>
  <span class="b">v</span>
  <span class="b">w</span>
</div>


0
投票

const CLASSNAME = 'txt';
/*
 */

function set_placeholder(cls, txt = '') {
  let textNode = document.createTextNode(txt);
  let elements = Array.from(document.getElementsByClassName(cls));

  [].forEach.call(elements, (elem) => {
    elem.replaceWith(textNode);
  });
}


document.querySelector('button').onclick = function() {
  set_placeholder.call(set_placeholder, CLASSNAME, 'new text');
}
<div>
  This is <span class="txt">text</span>
</div>


<button>Replace</button>

通过

document.querySelectorAll
document.getElementsByClassName
收集元素会返回找到的元素的 NodeList,它是数组的变异版本。因此,通过使用
[...]
(扩展语法)或
Array.from
,可以解决该问题。但毕竟,这是有意的行为。希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.