用跨度替换#text元素会导致文档重新整形

时间:2016-08-01 19:09:59

标签: javascript dom

我需要替换每个带有跨距的兄弟的#text,以便它们有id。以下代码应该这样做,但由于某种原因,它会导致许多文档重塑:文档的一部分移动,页面改变其外观。

var eltId = 0;

function genEltId() {
    return "my-id-" + ++eltId;
}

function hashTextsToSpans(elt) {
    for (var i in elt.childNodes) {
        var eltChild = elt.childNodes[i];
        if (eltChild.nodeName == "#text" && elt.childNodes.length > 1) {
            // #text is one of multiple childs
            var eltDiv = document.createElement("span");
            eltDiv.setAttribute("id", genEltId());
            elt.replaceChild(eltDiv, eltChild);
            eltDiv.appendChild(eltChild);
        } else {
            if (eltChild.nodeName != "IFRAME") {
                hashTextsToSpans(eltChild);
            }
        }
    }
}

function onKeyPress(e) {
    if (e.keyCode == 105) {
        // key I
        hashTextsToSpans(document.body);
    }
}

window.parent.addEventListener("keypress", onKeyPress);

例如,将其注入chrome(使用" cjs" addon)注入https://en.wikipedia.org/wiki/Linux,按" i",然后观察页面重塑。

有什么问题? Aren&#tt #text并跨越两个内联元素,并且当文本相同且span没有样式时应该以相同的方式显示?

假设:没有iframe元素,没有其他javascript同时操纵DOM树。

1 个答案:

答案 0 :(得分:1)

问题在于,在HTML中,许多元素限制允许它们包含哪些类型的子节点。

例如,列表元素(例如ulol)只能包含li个元素和空白(仅限空白)文本节点。当您在跨度中包装此类空白文本节点时,该页面突然不符合标准。

在您链接到的Wikipedia页面的特定情况下,原始页面的文本节点仅包含表格的trtd元素之间的空格。当您的脚本运行时,这些文本节点将成为跨度,但不允许在表中包含trtd元素之外的元素。因此,这会导致大多数浏览器自动将幻像列插入到表中,从而弄乱了布局。

作为快速修复,您可以忽略仅包含空格的文本节点。如果您使用TreeWalker API,通过将以下函数作为过滤器传递,这是微不足道的:

node => /\S/.test(node.nodeValue) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT

作为一个更长期的修复,您可能需要查看HTML5 spec以确定哪些元素可以包含span节点,并相应地编写脚本。