我正在尝试替换 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>
<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')
只会替换第一次出现。
实施是正确的,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>
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
,可以解决该问题。但毕竟,这是有意的行为。希望这有帮助。