追加时节点从循环中消失
Nodes disappear from loop when appended
我正在尝试用 JS 版本
回答
为什么第二行从我的循环中消失?文本节点是否由于某种原因卡在了 BR 上?
我想隐藏 BR 和后续行,但我没有将第二行移动到 span
预期结果是当遇到 BR 时,它和所有后续节点都被移动到 span,最后 span 附加到 br 曾经所在的 div,使第二行变为红色在这种情况下
我玩过 cloneNode,效果更好,但我想移动节点,而不是克隆它们。我也考虑过 substring the innerHTML 但担心 BR 的各种渲染和 toString 不可靠
输入:
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
预期输出:
<div class="site-info">The quick brown fox <span class="hide"><br>Jumps over the lazy dog</span></div>
尝试:
var si = document.querySelector(".site-info")
var nodes = si.childNodes;
var span = document.createElement("span");
span.className="hide"
var found = false;
nodes.forEach(n => {
console.log(n.tagName || "No tag",n.wholeText || "No text")
if (n.tagName==="BR") { // first br found
found = true;
}
if (found) { // from now on to end of all nodes
span.appendChild(n) // n.cloneNode(true)
}
});
si.appendChild(span)
.hide { color:red }/* display: none }*/
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
预期结果 注意:color:red 在实际版本中将是 display:none,如果 display:none 被代码
删除,则第二行将出现 BR
.hide { color:red }
<div class="site-info">The quick brown fox <span class="hide"><br>Jumps over the lazy dog</span></div>
我们已经有了解决该问题的公认答案。但我们发现真正的问题是在迭代期间删除 br
元素会弄乱原始的 childNodes 数组。
原来是
<div>
A quick brown fox
<br>
Jumps over the lazy dog
</div>
分为3
个节点。
在原始代码中,删除了 br
元素并将其插入到新的跨度中。发生的情况是两个单独的子节点合并为一个。
<div>
A quick brown fox
<!-- <br> is removed -->
Jumps over the lazy dog
</div>
这样第三次迭代永远不会发生,因为节点的长度从 3
变为 2
结果 HTML 变为
<div>
A quick brown fox
Jumps over the lazy dog
<span><br/></span>
</div>
好吧,您的代码似乎完全按照您的指示执行。在这种情况下: 遍历所有子节点,如果找到 <br>
将其附加到新创建的 <span>
并将 <span>
添加到 .site-info
div .
所以你的输出变成这样:
<div class="site-info">The quick brown fox Jumps over the lazy dog<span class="hide"><br></span></div>
编辑: 您可以采取不同的方法,检查字符串中是否出现 <br>
标记,剪切字符串并将选择粘贴到文字字符串中,例如所以:
var si = document.querySelector(".site-info");
var html = si.innerHTML;
var index = html.indexOf('<br>');
var selection = html.slice(index, html.length);
si.innerHTML = html.substring(0, index) + '<span class="hide">' + selection + '</span>';
// Log the output
console.log(si.innerHTML);
.hide { color:red }/* display: none }*/
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
虽然这确实需要一些验证,但确实可以满足您的需求。
当您在循环之前执行 console.log(nodes)
时,您会注意到跨度已作为子节点包含在内。
在遍历节点时,这似乎与 span.appendChild
的逻辑相混淆。
编辑:根据评论,当为 br
标签调用 appendChild
时,元素会从 DOM 中移除,这会改变 nodes
中的引用数组并导致两个文本节点合并,因为它们不再分开。因此循环将不再到达前第三次迭代,因为该数组元素不再存在。
如果您将用于在循环外附加节点的逻辑提取出来,它会起作用:
var si = document.querySelector(".site-info")
var nodes = si.childNodes;
var span = document.createElement("span");
span.className = "hide"
var found = false;
let childs = [];
nodes.forEach((n, i) => {
if (n.tagName === "BR") {
found = true;
}
if (found) {
childs.push(n);
}
});
if (childs.length > 0) {
childs.forEach((n) => {
span.appendChild(n);
});
si.appendChild(span);
}
.hide {
color: red
}
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
话虽如此,我实际上还没有找到发生这种情况的原因,因此可能有更优雅的解决方案。
每次调用 appendChild
时,元素都会 移动 到 span
,这会更改 nodes
的大小。相反,我们可以对所有节点进行深度克隆并维护一个变量 c
以检查要跳过多少节点,因为 nodesCopy
会随循环而变化,而 nodes
不会:
var si = document.querySelector(".site-info")
var nodes = [];
var nodesCopy = si.childNodes;
si.childNodes.forEach(element => nodes.push(element.cloneNode(true)));
var c = 0;
var span = document.createElement("span");
span.className = "hide"
var found = false;
console.log(nodes.length);
nodes.forEach((n, i) => {
console.log(n.tagName || "No tag", n.wholeText || "No text");
if (n.tagName === "BR") {
found = true;
}
if (found) {
c++;
si.removeChild(nodesCopy[i - c + 1]);
span.appendChild(n);
}
});
console.log(nodes.length);
si.appendChild(span);
.hide {
color: red
}
/* display: none }*/
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
因为我们不能从头开始移动,所以在这种情况下我们可以从尾部移动
var si = document.querySelector(".site-info")
var nodes = si.childNodes;
var span = document.createElement("span");
span.className="hide"
for (var i=nodes.length-1; i>=0; i--) {
var n = nodes[i];
console.log(n.tagName || n.wholeText)
span.insertBefore(n,span.firstChild)
if (n.tagName=="BR") break;
};
si.appendChild(span)
.hide { color:red }/* display: none }*/
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
我正在尝试用 JS 版本
回答为什么第二行从我的循环中消失?文本节点是否由于某种原因卡在了 BR 上?
我想隐藏 BR 和后续行,但我没有将第二行移动到 span
预期结果是当遇到 BR 时,它和所有后续节点都被移动到 span,最后 span 附加到 br 曾经所在的 div,使第二行变为红色在这种情况下
我玩过 cloneNode,效果更好,但我想移动节点,而不是克隆它们。我也考虑过 substring the innerHTML 但担心 BR 的各种渲染和 toString 不可靠
输入:
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
预期输出:
<div class="site-info">The quick brown fox <span class="hide"><br>Jumps over the lazy dog</span></div>
尝试:
var si = document.querySelector(".site-info")
var nodes = si.childNodes;
var span = document.createElement("span");
span.className="hide"
var found = false;
nodes.forEach(n => {
console.log(n.tagName || "No tag",n.wholeText || "No text")
if (n.tagName==="BR") { // first br found
found = true;
}
if (found) { // from now on to end of all nodes
span.appendChild(n) // n.cloneNode(true)
}
});
si.appendChild(span)
.hide { color:red }/* display: none }*/
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
预期结果 注意:color:red 在实际版本中将是 display:none,如果 display:none 被代码
删除,则第二行将出现 BR.hide { color:red }
<div class="site-info">The quick brown fox <span class="hide"><br>Jumps over the lazy dog</span></div>
我们已经有了解决该问题的公认答案。但我们发现真正的问题是在迭代期间删除 br
元素会弄乱原始的 childNodes 数组。
原来是
<div>
A quick brown fox
<br>
Jumps over the lazy dog
</div>
分为3
个节点。
在原始代码中,删除了 br
元素并将其插入到新的跨度中。发生的情况是两个单独的子节点合并为一个。
<div>
A quick brown fox
<!-- <br> is removed -->
Jumps over the lazy dog
</div>
这样第三次迭代永远不会发生,因为节点的长度从 3
变为 2
结果 HTML 变为
<div>
A quick brown fox
Jumps over the lazy dog
<span><br/></span>
</div>
好吧,您的代码似乎完全按照您的指示执行。在这种情况下: 遍历所有子节点,如果找到 <br>
将其附加到新创建的 <span>
并将 <span>
添加到 .site-info
div .
所以你的输出变成这样:
<div class="site-info">The quick brown fox Jumps over the lazy dog<span class="hide"><br></span></div>
编辑: 您可以采取不同的方法,检查字符串中是否出现 <br>
标记,剪切字符串并将选择粘贴到文字字符串中,例如所以:
var si = document.querySelector(".site-info");
var html = si.innerHTML;
var index = html.indexOf('<br>');
var selection = html.slice(index, html.length);
si.innerHTML = html.substring(0, index) + '<span class="hide">' + selection + '</span>';
// Log the output
console.log(si.innerHTML);
.hide { color:red }/* display: none }*/
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
虽然这确实需要一些验证,但确实可以满足您的需求。
当您在循环之前执行 console.log(nodes)
时,您会注意到跨度已作为子节点包含在内。
在遍历节点时,这似乎与 span.appendChild
的逻辑相混淆。
编辑:根据评论,当为 br
标签调用 appendChild
时,元素会从 DOM 中移除,这会改变 nodes
中的引用数组并导致两个文本节点合并,因为它们不再分开。因此循环将不再到达前第三次迭代,因为该数组元素不再存在。
如果您将用于在循环外附加节点的逻辑提取出来,它会起作用:
var si = document.querySelector(".site-info")
var nodes = si.childNodes;
var span = document.createElement("span");
span.className = "hide"
var found = false;
let childs = [];
nodes.forEach((n, i) => {
if (n.tagName === "BR") {
found = true;
}
if (found) {
childs.push(n);
}
});
if (childs.length > 0) {
childs.forEach((n) => {
span.appendChild(n);
});
si.appendChild(span);
}
.hide {
color: red
}
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
话虽如此,我实际上还没有找到发生这种情况的原因,因此可能有更优雅的解决方案。
每次调用 appendChild
时,元素都会 移动 到 span
,这会更改 nodes
的大小。相反,我们可以对所有节点进行深度克隆并维护一个变量 c
以检查要跳过多少节点,因为 nodesCopy
会随循环而变化,而 nodes
不会:
var si = document.querySelector(".site-info")
var nodes = [];
var nodesCopy = si.childNodes;
si.childNodes.forEach(element => nodes.push(element.cloneNode(true)));
var c = 0;
var span = document.createElement("span");
span.className = "hide"
var found = false;
console.log(nodes.length);
nodes.forEach((n, i) => {
console.log(n.tagName || "No tag", n.wholeText || "No text");
if (n.tagName === "BR") {
found = true;
}
if (found) {
c++;
si.removeChild(nodesCopy[i - c + 1]);
span.appendChild(n);
}
});
console.log(nodes.length);
si.appendChild(span);
.hide {
color: red
}
/* display: none }*/
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>
因为我们不能从头开始移动,所以在这种情况下我们可以从尾部移动
var si = document.querySelector(".site-info")
var nodes = si.childNodes;
var span = document.createElement("span");
span.className="hide"
for (var i=nodes.length-1; i>=0; i--) {
var n = nodes[i];
console.log(n.tagName || n.wholeText)
span.insertBefore(n,span.firstChild)
if (n.tagName=="BR") break;
};
si.appendChild(span)
.hide { color:red }/* display: none }*/
<div class="site-info">The quick brown fox <br>Jumps over the lazy dog</div>