How can I tell if a node is in memory or in the dom?
The modern, vanilla answer is to use Node.isConnected
:
let test = document.createElement('p');
console.log(test.isConnected); // Returns false
document.body.appendChild(test);
console.log(test.isConnected); // Returns true
(example taken directly from the MDN docs)
You could assign an ID to the element and then search for it:
var id = element.id || generateRandomId(); // some function generating a random string
if(document.getElementById(id) !== null) {
// element in tree
}
Here is a performance comparison including François' suggestions, comparing each method for an attached and detached element node. Here are the test cases for only existing nodes, to get a better idea for the speed difference.
Test results:
Obviously testing a detached node with the while
is faster, since it terminates nearly instantly (at the second iteration). But if the node has a (potentially) detached ancestor, then assigning an ID to the node and look for it seems to be the fastest way in Chrome 21.
Interestingly though, in Firefox 14, the Node#contains
[MDN] method seems to be much faster than anything else.
Since the speed improvements in Firefox from ID lookup to native .contains
seems to be higher than the performance loss in Chrome, a fast function could look like this:
function in_tree(element) {
if(!element || !element.parentNode) { // fail fast
return false;
}
if(element.contains) {
return document.body.contains(element);
}
var id = element.id || generateRandomId();
element.id = id;
return document.getElementById(id) !== null;
}
But in the end, there will always be differences between browsers, so you have to make a compromise.
You have several options which execute at several different speeds.
var $document = $(document);
var $element = $("#jq-footer");
var exists;
// Test if the element is within a body
exists = $element.closest("body").length;
// Test if the document contains an element
// wrong syntax, use below instead --> exists = $.contains($document, $element);
exists = $.contains(document.documentElement, $element[0]);
// Test if the element is within a body
$($element).parents().is("body");
// Manually loop trough the elements
exists = elementExists($element[0]);
// Used for manual loop
function elementExists(element) {
while (element) {
if (element == document) {
return true;
}
element = element.parentNode;
}
return false;
}
See Performance Test
For this test I copied a huge amount of html to traverse over, I copied the source of one of the jQuery pages into a fiddle, stripping out all the script tags leaving only the body and the html in between.
Feel free you use document instead of "body" and vice-versa or add more tests but it should give you the general idea.
Edit
I updated the performance test to use the correct syntax for contains as the previous one was incorrect and always returned true even if the element did not exist. the blow now returns true if the element exists but if you specify a selector which doesn't exist it will return false.
exists = $.contains(document.documentElement, $element[0]);
I also added the suggested alternative mentioned by MrOBrian in the comments of the question which is again slightly faster than contains.
Nice one MrOBrian.
Edit
Here is the jsPerf performance test with all the nice charts.
Thanks Felix Kling for spotting the issue and helping me fix the jsPerf tests.
Added more test results from the comments, this one is really good:
jsPerf performance test: dom-tree-test-exists