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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6027830/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 19:11:45  来源:igfitidea点击:

Is it possible to get reference to comment element/block by JavaScript?

javascriptdomcomments

提问by Morgan Cheng

This sounds a little crazy, but I'm wondering whether possible to get reference to comment element so that I can dynamically replace it other content with JavaScript.

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

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

In above page, can I get reference to the comment block and replace it with some content in local storage?

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

I know that I can have a div place holder. Just wondering whether it applies to comment block. Thanks.

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

回答by hyperslug

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);

回答by mindplay.dk

It seems there are legitimate (performance) concerns about using comments as placeholders - for one, there's no CSS selector that can match comment nodes, so you won't be able to query them with e.g. document.querySelectorAll(), which makes it both complex and slow to locate comment elements.

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

My question then was, is there another element I can place inline, that doesn't have any visible side-effects? I've seen some people using the <meta>tag, but I looked into that, and using that in <body>isn't valid markup.

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

So I settled on the <script>tag.

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

Use a custom typeattribute, so it won't actually get executed as a script, and use data-attributes for any initialization data required by the script that's going to initialize your placeholders.

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

For example:

例如:

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

Then simply query those tags - e.g.:

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

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

Then replace them as needed - here's a plain DOM example.

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

Note that placeholderin this example isn't any defined "real" thing - you should replace that with e.g. vendor-nameto make sure your typedoesn't collide with anything "real".

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

回答by Joseph Lennox

Building off of hyperslug's answer, you can make it go faster by using a stack instead of function recursion. As shown in this jsPerf, function recursion is 42% slower on my Chrome 36 on Windows and 71% with IE11 in IE8 compatibility mode. It appears to run about 20% slower in IE11 in edge mode but faster in all other cases tested.

基于 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;
}

Or as done in TypeScript:

或者像在 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;
}

回答by mems

There is an API for document nodes traversal: Document#createNodeIterator():

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

var nodeIterator = document.createNodeIterator(
    document.body,
    NodeFilter.SHOW_COMMENT,    
    { acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } }
);

// 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 place holder: some_id -->
</body>
</html>



Edit: use a NodeIteratorinstead of a TreeWalker

编辑:使用NodeIterator而不是TreeWalker

回答by Michael_Scharf

If you use jQuery, you can do the following to get all comment nodes

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

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

If you only want the comments nodes of the body, use

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

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

If you want the comment strings as an array you can then use map:

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

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

回答by Yair Levy

This is an old question, but here's my two cents on DOM "placeholders" IMO a comment element is perfect for the job (valid html, not visible, and not misleading in any way). However, traversing the dom looking for comments is not necessary if you build your code the other way around.

这是一个老问题,但这是我对 DOM“占位符”IMO 的两分钱,评论元素非常适合这项工作(有效的 html,不可见,并且不会以任何方式误导)。但是,如果您以相反的方式构建代码,则不需要遍历 dom 查找注释。

I would suggest using the following method:

我建议使用以下方法:

  1. Mark the places you want to "control" with markup of your choice (e.g a div element with a specific class)

    <div class="placeholder"></div>
    <div class="placeholder"></div>
    <div class="placeholder"></div>
    <div class="placeholder"></div>
    <div class="placeholder"></div>
    
  2. Find the placeholders the usual way (querySelector/classSelector etc)

  1. 使用您选择的标记(例如具有特定类的 div 元素)标记您想要“控制”的地方

    <div class="placeholder"></div>
    <div class="placeholder"></div>
    <div class="placeholder"></div>
    <div class="placeholder"></div>
    <div class="placeholder"></div>
    
  2. 以通常的方式找到占位符(querySelector/classSelector 等)

var placeholders = document.querySelectorAll('placeholder');

var placeholders = document.querySelectorAll('placeholder');

  1. Replace them with comments and keep reference of those comments:
  1. 用评论替换它们并保留这些评论的参考:

var refArray = [];

var refArray = [];

[...placeholders].forEach(function(placeholder){ var comment = document.createComment('this is a placeholder'); refArray.push( placeholder.parentNode.replaceChild(comment, placeholder) ); });

[...placeholders].forEach(function(placeholder){ var comment = document.createComment('this is a placeholder'); refArray.push( placeholder.parentNode.replaceChild(comment, placeholder) ); });

at this stage your rendered markup should look like this:

在此阶段,您呈现的标记应如下所示:

<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
  1. Now you can access each of those comments directly with your built refArray and do whatevere it is you wanna do... for example:
  1. 现在,您可以使用构建的 refArray 直接访问这些评论中的每一个,并做您想做的任何事情……例如:

replace the second comment with a headline

用标题替换第二条评论

let headline = document.createElement('h1'); headline.innerText = "I am a headline!"; refArray[1].parentNode.replaceChild(headline,refArray[1]);

let headline = document.createElement('h1'); headline.innerText = "I am a headline!"; refArray[1].parentNode.replaceChild(headline,refArray[1]);

回答by Keith Shaw

If you just want to get an array of all comments from a document or part of a document, then this is the most efficient way I've found to do that in modern JavaScript.

如果您只想从文档或文档的一部分获取所有评论的数组,那么这是我在现代 JavaScript 中发现的最有效的方法。

function getComments (root) {

    var treeWalker = document.createTreeWalker(
        root,
        NodeFilter.SHOW_COMMENT,
        {
            "acceptNode": function acceptNode (node) {
                return NodeFilter.FILTER_ACCEPT;
            }
        }
    );

    // skip the first node which is the node specified in the `root`
    var currentNode = treeWalker.nextNode();

    var nodeList = [];
    while (currentNode) {

        nodeList.push(currentNode);
        currentNode = treeWalker.nextNode();

    }

    return nodeList;

}

I am getting over 50,000 operations per second in Chrome 80 and the stack and recursion methods both get less than 5,000 operations per second in Chrome 80. I had tens of thousands of complex documents to process in node.js and this worked the best for me.

我在 Chrome 80 中每秒获得超过 50,000 次操作,而在 Chrome 80 中堆栈和递归方法每秒都获得不到 5,000 次操作。我在 node.js 中有数万个复杂文档要处理,这对我来说效果最好.

https://jsperf.com/getcomments/6

https://jsperf.com/getcomments/6