Можно ли получить ссылку на элемент/блок комментария JavaScript?
Звучит немного сумасшедшим, но мне интересно, можно ли получить ссылку на элемент комментария, чтобы я мог динамически заменить его другим контентом на JavaScript.
<html>
<head>
</head>
<body>
<div id="header"></div>
<div id="content"></div>
<!-- sidebar place holder: some id-->
</body>
</html>
На вышеприведенной странице я могу получить ссылку на блок комментариев и заменить его некоторым содержимым в локальном хранилище?
Я знаю, что у меня может быть владелец места. Просто интересно, относится ли это к блоку комментариев.
Спасибо.
Ответы
Ответ 1
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);
Ответ 2
Кажется, есть законные (производительность) проблемы с использованием комментариев в качестве заполнителей - например, нет селектора CSS, который может соответствовать узлам комментариев, поэтому вы не сможете запросить их, например. document.querySelectorAll()
, что делает его сложным и медленным для нахождения элементов комментариев.
Мой вопрос тогда был, есть ли еще один элемент, который я могу разместить в строке, который не имеет видимых побочных эффектов? Я видел некоторых людей, использующих тег <meta>
, но я изучил это, и использование этого в <body>
не является допустимой разметкой.
Итак, я остановился на теге <script>
.
Использовать собственный атрибут type
, поэтому он фактически не будет выполняться как script и использовать атрибуты data-
для любых данных инициализации, требуемых script, которые собираются инициализировать ваши заполнители.
Например:
<script type="placeholder/foo" data-stuff="whatevs"></script>
Затем просто запросите те теги - например:
document.querySelectorAll('script[type="placeholder/foo"]')
Затем замените их по мере необходимости - здесь простой пример DOM.
Обратите внимание, что placeholder
в этом примере не является какой-либо определенной "реальной" вещью - вы должны заменить ее, например. vendor-name
, чтобы убедиться, что ваш type
не сталкивается ни с чем "реальным".
Ответ 3
Построив ответ на гиперслог, вы можете ускорить его, используя стек вместо рекурсии функции. Как показано в этом jsPerf, рекурсия функций на моем Chrome 36 на Windows медленнее на 42% и на 71% с IE11 в режиме совместимости с IE8. Похоже, что он работает примерно на 20% медленнее в IE11 в граничном режиме, но быстрее во всех других протестированных случаях.
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[] {
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;
}
Ответ 4
Если вы используете 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;})
Ответ 5
Существует 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>
Ответ 6
Это старый вопрос, но здесь мои два цента на DOM "заполнители" IMO, элемент комментария идеально подходит для работы (действительный html, не виден и не вводит в заблуждение в любом случае). Однако обходить дом в поисках комментариев не обязательно, если вы строите свой код наоборот.
Я бы предложил использовать следующий метод:
-
Пометьте места, которые вы хотите "контролировать", с помощью разметки по вашему выбору (например, элемент div с определенным классом)
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
<div class="placeholder"></div>
-
Найдите заполнители обычным способом (querySelector/classSelector и т.д.)
var placeholders = document.querySelectorAll('placeholder');
- Замените их комментариями и сохраните ссылки на эти комментарии:
var refArray = [];
[...placeholders].forEach(function(placeholder){ var comment = document.createComment('this is a placeholder'); 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 -->
- Теперь вы можете получить доступ к каждому из этих комментариев непосредственно с помощью встроенного refArray и делать то, что хотите, например...
заменить второй комментарий заголовком
let headline = document.createElement('h1'); headline.innerText = "I am a headline!"; refArray[1].parentNode.replaceChild(headline,refArray[1]);
Ответ 7
Вот как я это делаю:
function getCommentNodes(element)
{
let treeWalker = document.createTreeWalker(element, NodeFilter.SHOW_COMMENT);
let commentNodes = [];
while(treeWalker.nextNode()) {commentNodes.push(treeWalker.currentNode);}
return commentNodes;
};