158

Let's say that I define an element

$foo = $('#foo');

and then I call

$foo.remove()

from some event. My question is, how do I check whether $foo has been removed from the DOM or not? I've found that $foo.is(':hidden') works, but that would of course also return true if I merely called $foo.hide().

Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196

11 Answers11

245

Like this:

if (!jQuery.contains(document, $foo[0])) {
    //Element is detached
}

This will still work if one of the element's parents was removed (in which case the element itself will still have a parent).

Xavi
  • 20,111
  • 14
  • 72
  • 63
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 14
    doing `$foo.closest(document.documentElement)` is faster (if anyone cares http://jsperf.com/jquery-element-in-dom) – urraka Feb 02 '13 at 14:03
  • 48
    @PerroAzul: I found a _much_ faster alternative: http://jsperf.com/jquery-element-in-dom/2 – SLaks Feb 03 '13 at 00:12
  • For the curious, typically if there are many ways to do something in jquery, the one that most directly calls the underlying Sizzle library is the fastest. jQuery.contains = Sizzle.contains. – Chris Moschini Mar 26 '13 at 08:04
  • 2
    Slightly faster: $.contains(document, $foo[0]) - tests both elements in DOM and out: http://jsperf.com/jquery-element-in-dom/8 – Chris Moschini Mar 26 '13 at 08:20
  • Is this preferred over Kushagra answer? – Hoffmann Sep 18 '13 at 19:42
  • 3
    Performance was my immediate concern with this method, thanks for linking the benchmarks @SLaks. So it looks like `$.contains(document.documentElement, $foo.get(0))` is the fastest way. – Christof May 12 '15 at 11:55
  • 12
    There is a pure-DOM way to do this, which is syntactically more readable, and I imagine very similar in terms of performance: `document.contains($foo[0])` – Joel Cross Dec 01 '15 at 10:16
  • 1
    @SLaks it works in IE as long as you're looking for an element and not something like a text node - also `document.contains()` is what jQuery will use if it exists, and in the particular case of this question a direct call to `document.contains()` will be much faster – Pointy Dec 13 '15 at 21:31
  • 5
    All this talk of performance...? The benchmarks show one method taking 15 microseconds, and another taking 0.15 microseconds. But really, who cares - you are not going to notice any difference unless you do it a hundred thousand times. And it might all change if jQuery or the JavaScript engine gets optimised.Just use whatever method reads better to you and to whoever has to maintain your code in future. – James Feb 10 '16 at 13:55
  • 3
    @James - or unless your website is `facebook.com` and your DOM is as deep as the core of the earth. – vsync Jul 10 '16 at 14:18
  • `$.contains` is fastest compared to `$el.closest` and `$el.parents`, whereas `$el.parents` is the slowest. https://jsperf.com/contains-vs-closest-cxp – CodeXP Feb 20 '19 at 17:25
  • Does not work.... parents() is working .. – mjs Jan 13 '23 at 18:48
25

How about doing this:

$element.parents('html').length > 0
Kushagra Gour
  • 4,568
  • 2
  • 22
  • 26
7

Probably the most performative way is:

document.contains(node); // boolean

This also works with jQuery:

document.contains($element[0]); // var $element = $("#some-element")
document.contains(this[0]); // in contexts like $.each(), `this` is the jQ object

Source from MDN

Note:

Protomen
  • 9,471
  • 9
  • 57
  • 124
lxg
  • 12,375
  • 12
  • 51
  • 73
6

I just realized an answer as I was typing my question: Call

$foo.parent()

If $f00 has been removed from the DOM, then $foo.parent().length === 0. Otherwise, its length will be at least 1.

[Edit: This is not entirely correct, because a removed element can still have a parent; for instance, if you remove a <ul>, each of its child <li>s will still have a parent. Use SLaks' answer instead.

Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196
5

Since I'm unable to respond as a comment (too low karma I guess), here's a full reply. The fastest way is easily to unroll the jQuery check for browser support and to shave the constant factors to minimum.

As seen also here - http://jsperf.com/jquery-element-in-dom/28 - the code would look like this:

var isElementInDOM = (function($) {
  var docElt = document.documentElement, find,
      contains = docElt.contains ?
        function(elt) { return docElt.contains(elt); } :

        docElt.compareDocumentPosition ?
          function(elt) {
            return docElt.compareDocumentPosition(elt) & 16;
          } :
          ((find = function(elt) {
              return elt && (elt == docElt || find(elt.parentNode));
           }), function(elt) { return find(elt); });

  return function(elt) {
    return !!(elt && ((elt = elt.parent) == docElt || contains(elt)));
  };
})(jQuery);

This is semantically equivalent to jQuery.contains(document.documentElement, elt[0]).

Jaakko Salomaa
  • 106
  • 1
  • 4
2

instead of iterating parents you can just get the bounding rect which is all zeros when the element is detached from dom

function isInDOM(element) {
    var rect=element.getBoundingClientRect();
    return (rect.top || rect.bottom || rect.height || rect.width)?true:false;
}

if you want to handle the edge case of a zero width and height element at zero top and zero left you can double check by iterating parents till the document.body

  • While this code snippet may solve the problem, it doesn't explain why or how it answers the question. Please [include an explanation for your code](//meta.stackexchange.com/q/114762/269535), as that really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. **Flaggers / reviewers:** [For code-only answers such as this one, downvote, don't delete!](//meta.stackoverflow.com/a/260413/2747593) – Luca Kiebel May 21 '18 at 11:32
  • Doesn't this also recognize hidden elements as "not in DOM"? e.g. an element with `display: none` has no boundingRect either – Philipp Jun 24 '20 at 20:44
1

I liked this approach. No jQuery and no DOM search. First find the top parent (ancestor). Then see if that is the documentElement.

function ancestor(HTMLobj){
  while(HTMLobj.parentElement){HTMLobj=HTMLobj.parentElement}
  return HTMLobj;
}
function inTheDOM(obj){
  return ancestor(obj)===document.documentElement;
}
0

Agree with Perro's comment. It can also be done like this:

$foo.parents().last().is(document.documentElement);
Ian Bytchek
  • 8,804
  • 6
  • 46
  • 72
0
jQuery.fn.isInDOM = function () {
   if (this.length == 1) {
      var element = this.get(0);
      var rect = element.getBoundingClientRect();
      if (rect.top + rect.bottom + rect.width + rect.height + rect.left + rect.right == 0)
         return false;
      return true;
   }
   return false;
};
-1

Why not just: if( $('#foo').length === 0)... ?

  • @stevematdavies The question is "how to test whether an element in a given jQuery object is in the DOM", not "how to test whether an element matching a given selector string is in the DOM." – Trevor Burnham Jun 18 '15 at 21:27
  • @TrevorBurnham: in all fairness, re-querying using the selector that was used to create `$foo`, and checking whether the re-query matched any element, seems a viable solution to the problem in many scenarios. It may even by *quicker* than some of the other solutions because of how both jQuery and the browsers optimize certain selectors (such as by ID). – Matt Jun 18 '15 at 21:42
  • @Matt $foo might be an element in memory detached from DOM, and there might me an element with matching selector in DOM. People looking for an answer to this question probably wants to check if it's in DOM or not. Someone working in stuff like that obviously knows how to query DOM. – T J Mar 03 '17 at 17:07
-1
if($foo.nodeType ){ element is node type}
tharo
  • 74
  • 5