是否可以通过 JavaScript 获取对注释元素/块的引用?

IT技术 javascript dom comments
2021-03-15 17:28:40

这听起来有点疯狂,但我想知道是否可以引用评论元素,以便我可以用 JavaScript 动态替换它的其他内容。

<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content"></div>
<!-- sidebar place holder: some id-->
</body>
</html>

在上面的页面中,我可以引用评论块并将其替换为本地存储中的一些内容吗?

我知道我可以有一个 div 占位符。只是想知道它是否适用于评论块。谢谢。

6个回答
var findComments = function(el) {
    var arr = [];
    for(var i = 0; i < el.childNodes.length; i++) {
        var node = el.childNodes[i];
        if(node.nodeType === 8) {
            arr.push(node);
        } else {
            arr.push.apply(arr, findComments(node));
        }
    }
    return arr;
};

var commentNodes = findComments(document);

// whatever you were going to do with the comment...
console.log(commentNodes[0].nodeValue);
谢谢。但是遍历 DOM 树的代价肯定很大。所以,我们最好不要使用注释作为占位符。
2021-04-20 17:28:40
有一个Node.COMMENT_NODE常量可以用来测试评论而不是8
2021-04-28 17:28:40
Node.COMMENT_NODE 在 ie8 中没有定义。如果这是您的目标,请保留数字 8。
2021-05-03 17:28:40

使用注释作为占位符似乎存在合理的(性能)问题 - 首先,没有可以匹配注释节点的 CSS 选择器,因此您将无法使用 eg 查询它们document.querySelectorAll(),这使得定位既复杂又缓慢评论元素。

我的问题是,是否有另一个我可以内联的元素,它没有任何可见的副作用?我见过一些人使用这个<meta>标签,但我调查了一下,在里面使用它<body>并不是有效的标记。

所以我选择了<script>标签。

使用自定义type属性,因此它实际上不会作为脚本执行,并将data-属性用于将要初始化占位符的脚本所需的任何初始化数据。

例如:

<script type="placeholder/foo" data-stuff="whatevs"></script>

然后只需查询这些标签 - 例如:

document.querySelectorAll('script[type="placeholder/foo"]')

然后根据需要替换它们 -这是一个普通的 DOM 示例

请注意,placeholder在此示例中并没有定义任何“真实”的东西 - 您应该将其替换为 egvendor-name以确保您type不会与任何“真实”的东西发生冲突。

另一个用作占位符的好标签可能是<template><font>或任何自定义标签,如<my-boundary-marker>.
2021-04-23 17:28:40

基于 hyperslug 的答案,您可以通过使用堆栈而不是函数递归来使其运行得更快。如这个jsPerf所示,在我的 Chrome 36 上,函数递归在 Windows 上慢了 42%,在 IE8 兼容模式下的 IE11 上慢了 71%。它在边缘模式下的 IE11 中运行速度似乎慢了 20%,但在所有其他测试情况下运行速度更快。

function getComments(context) {
    var foundComments = [];
    var elementPath = [context];
    while (elementPath.length > 0) {
        var el = elementPath.pop();
        for (var i = 0; i < el.childNodes.length; i++) {
            var node = el.childNodes[i];
            if (node.nodeType === Node.COMMENT_NODE) {
                foundComments.push(node);
            } else {
                elementPath.push(node);
            }
        }
    }

    return foundComments;
}

或者像在 TypeScript 中所做的那样:

public static getComments(context: any): Comment[] {
    const foundComments = [];
    const elementPath = [context];
    while (elementPath.length > 0) {
        const el = elementPath.pop();
        for (let i = 0; i < el.childNodes.length; i++) {
            const node = el.childNodes[i];
            if (node.nodeType === Node.COMMENT_NODE) {
                foundComments.push(node);
            } else {
                elementPath.push(node);
            }
        }
    }

    return foundComments;
}

有一个用于文档节点遍历的 API Document#createNodeIterator()

var nodeIterator = document.createNodeIterator(
    document.body,
    NodeFilter.SHOW_COMMENT
);

// Replace all comment nodes with a div
while(nodeIterator.nextNode()){
    var commentNode = nodeIterator.referenceNode;
    var id = (commentNode.textContent.split(":")[1] || "").trim();
    var div = document.createElement("div");
    div.id = id;
    commentNode.parentNode.replaceChild(div, commentNode);
}
#header,
#content,
#some_id{
  margin: 1em 0;
  padding: 0.2em;
  border: 2px grey solid;
}

#header::after,
#content::after,
#some_id::after{
  content: "DIV with ID=" attr(id);
}
<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content"></div>
<!-- sidebar placeholder: some_id -->
</body>
</html>


编辑:使用NodeIterator而不是TreeWalker

TreeWalker 在 Chrome 80 和 Safari 13 中最快。NodeIterator 在 Firefox 73 中最快。两者都至少比堆栈或递归快一个数量级。jsperf.com/getcomments/6
2021-04-22 17:28:40
您的过滤器功能本质上是一个空操作,可以安全地删除。
2021-04-25 17:28:40
@bfred.it NodeIterators 更面向节点,而 TreeWalkers 更面向层次结构。请参阅文档对象模型遍历NodeIterator更适合该任务没有 API 差异(用于从上到下循环节点),也许性能更好?
2021-04-28 17:28:40
不确定为什么将其更改为在 TreeWalker 上使用 NodeIterator,因为差异很小,并且仅在节点移动时才存在
2021-05-10 17:28:40
很有意思!我不知道那些 API 存在。
2021-05-12 17:28:40

如果您使用jQuery,您可以执行以下操作来获取所有评论节点

comments = $('*').contents().filter(function(){ return this.nodeType===8; })

如果您只想要正文的注释节点,请使用

comments = $('body').find('*').contents().filter(function(){
     return this.nodeType===8;
 })

如果您希望注释字符串作为数组,则可以使用map

comment_strings = comments.map(function(){return this.nodeValue;})