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

发布于 2024-11-07 20:46:37 字数 377 浏览 0 评论 0原文

这听起来有点疯狂,但我想知道是否可以引用注释元素,以便我可以用 JavaScript 动态地将其替换为其他内容。

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

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

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

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.

<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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

独留℉清风醉 2024-11-14 20:46:37
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);
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);
白衬杉格子梦 2024-11-14 20:46:37

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

我的问题是,是否有另一个元素可以内联放置,并且没有任何明显的副作用?我看到有些人使用 标记,但我对此进行了研究,发现在 中使用它不是有效的标记。

所以我选择了

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

例如:

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

然后简单地查询这些标签 - 例如:

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

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

请注意,此示例中的 placeholder 并不是任何定义的“真实”事物 - 您应该将其替换为 vendor-name 以确保您的 type > 不会与任何“真实”的东西发生碰撞。

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.

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.

So I settled on the <script> tag.

Use a custom type attribute, 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.

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.

Note that placeholder in this example isn't any defined "real" thing - you should replace that with e.g. vendor-name to make sure your type doesn't collide with anything "real".

≈。彩虹 2024-11-14 20:46:37

有一个用于文档节点遍历的 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

There is an API for document nodes traversal: 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>


Edit: use a NodeIterator instead of a TreeWalker

聆听风音 2024-11-14 20:46:37

基于 hyperslug 的答案,您可以通过使用堆栈而不是函数递归来使其运行得更快。如jsPerf所示,在 Windows 上的 Chrome 36 上,函数递归速度慢了 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;
}

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.

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:

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;
}
优雅的叶子 2024-11-14 20:46:37

使用 document.evaluate 和 xPath:

function getAllComments(node) {
    const xPath = "//comment()",
        result = [];

    let query = document.evaluate(xPath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
    for (let i = 0, length = query.snapshotLength; i < length; ++i) {
        result.push(query.snapshotItem(i));
    }

    return result;
}

getAllComments(document.documentElement);

根据我的测试,使用 xPath 比 treeWalker 更快:
https://jsben.ch/Feagf

Using document.evaluate and xPath:

function getAllComments(node) {
    const xPath = "//comment()",
        result = [];

    let query = document.evaluate(xPath, node, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
    for (let i = 0, length = query.snapshotLength; i < length; ++i) {
        result.push(query.snapshotItem(i));
    }

    return result;
}

getAllComments(document.documentElement);

from my testing, using xPath is faster than treeWalker:
https://jsben.ch/Feagf

酷遇一生 2024-11-14 20:46:37

如果您使用 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;})

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

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:

comment_strings = comments.map(function(){return this.nodeValue;})
如梦亦如幻 2024-11-14 20:46:37

如果您只想从文档或文档的一部分获取所有注释的数组,那么这是我发现在现代 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;

}

我在 Chrome 80 中每秒执行超过 50,000 次操作,而堆栈和递归方法在 Chrome 80 中每秒执行的操作均少于 5,000 次。我在 node.js 中需要处理数以万计的复杂文档,这对我来说效果最好。

https://jsperf.com/getcomments/6

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.

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.

https://jsperf.com/getcomments/6

深海不蓝 2024-11-14 20:46:37

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

我建议使用以下方法:

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

    ;
  2. 以通常的方式查找占位符(querySelector/classSelector 等)

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

  1. 用注释替换它们并保留这些注释的引用:

var refArray = [];

[...占位符].forEach(函数(占位符){
var comment = document.createComment('这是一个占位符');
refArray.push( placeholder.parentNode.replaceChild(comment, placeholder) );
});

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

<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
<!-- this is a placeholder -->
  1. 现在您可以使用构建的 refArray 直接访问每个注释并执行您想做的任何操作...例如:

将第二个注释替换为标题

let header = document.createElement('h1');
header.innerText = "我是头条!";
refArray[1].parentNode.replaceChild(标题,refArray[1]);

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.

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)

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

  1. Replace them with comments and keep reference of those comments:

var refArray = [];

[...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:

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

执妄 2024-11-14 20:46:37

我建议使用 createTreeWalker 和内部的自定义过滤器:

function findCommentNode(commentText) {
    return document
    .createTreeWalker(
      document.querySelector('html'),
      128, /* NodeFilter.SHOW_COMMENT */
      {
          acceptNode:  (node) => node.textContent.trim() === commentText.trim()
            ? 1 /* NodeFilter.FILTER_ACCEPT */
            : 2 /* NodeFilter.FILTER_REJECT */
      }
    )
    .nextNode();
}

然后:

const node = findCommentNode('some comment')

I recommend to use the createTreeWalker and a custom filter inside:

function findCommentNode(commentText) {
    return document
    .createTreeWalker(
      document.querySelector('html'),
      128, /* NodeFilter.SHOW_COMMENT */
      {
          acceptNode:  (node) => node.textContent.trim() === commentText.trim()
            ? 1 /* NodeFilter.FILTER_ACCEPT */
            : 2 /* NodeFilter.FILTER_REJECT */
      }
    )
    .nextNode();
}

Then:

const node = findCommentNode('some comment')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文