Reputation: 1929
I'm trying to get the contents of the last HTML comment on the page -- it contains some session information written by the web application server. It is written right before the </html>
tag. After reading this question (Selecting HTML Comments with jQuery), we've come up with something that works, but not consistently:
var commentString = $("*").contents().filter(function(){
return this.nodeType == 8 ? this.nodeValue.indexOf("Expected Comment Text") > -1: false;
})[0].nodeValue;
The problem is that evidently if the page contains an iframe (from a different domain), the * selector will hit it and then we get stopped by the cross-origin policy. We can change this selector to $("body")
and it appears to work in most browsers, but this appears to rely on the browser moving the comment inside the body node, and I'm not convinced that this will universally be the case.
We can use jQuery 1.7.2 if necessary (but no plugins), and good browser support (IE < 9) is important.
Would it be best to traverse the DOM without jQuery "in reverse" until we hit a nodeType === 8
? That way it wouldn't matter if the browser puts the comment node as a child of <html>
or <body>
as long as its the last comment in the document node tree. What would this look like? (My DOM-traversal-fu is no good.)
Upvotes: 1
Views: 72
Reputation:
There's quite a lot of overhead if you go with the kind of solution you're attempting.
Just walk the DOM by making a simple, reusable function.
function walk(node, cb) {
cb(node);
if (node.childNodes) {
for (var i = 0; i < node.childNodes.length; i++) {
walk(node.childNodes[i], cb);
}
}
}
// And use it like this:
walk(document, function(node) {
if (node.nodeType === 8) {
// do something with comment
alert(node.data);
}
});
<div>
foo
<iframe src="http://www.linuxmint.com"></iframe>
<!-- some comment -->
</div>
Much more efficient and flexible this way.
Here's another variation on the walk
function, that doesn't use a counter loop.
function walk(node, cb) {
cb(node);
if (node = node.firstChild) {
do walk(node, cb);
while (node = node.nextSibling)
}
}
A little shorter and cleaner this way.
If you're looking to return a single node, then you can have your callback as well as the recursive call return a value, and then return immediately when a value is actually returned.
As per your comments, this version walks backwards through the DOM.
function walk(node, cb) {
var res = cb(node);
if (res)
return res;
if (node = node.lastChild) {
do if (res = walk(node, cb))
return res;
while (node = node.previousSibling)
}
}
var result = walk(document, function(node) {
if (node.nodeType === 8) {
return node;
}
});
if (result)
alert(result.data);
<div>
foo
<!-- Some other comment -->
<iframe src="http://www.linuxmint.com"></iframe>
<!-- Success! - Expected Comment Text -->
</div>
Upvotes: 1
Reputation: 36703
Why don't you use
$("*").not("iframe")
as a selector, if rest of the code workd fine and iframe
is the only element causing issue?
var commentString = $("*").not("iframe").contents().filter(function(){
return this.nodeType == 8 ? this.nodeValue.indexOf("Expected Comment Text") > -1: false;
})[0].nodeValue;
Upvotes: 1