Como verifico se um elemento jQuery está no DOM?

147

Digamos que eu defino um elemento

$foo = $('#foo');

e então eu ligo

$foo.remove()

de algum evento. Minha pergunta é: como verifico se $ foo foi removido do DOM ou não? Descobri que $foo.is(':hidden')funciona, mas é claro que isso também retornaria verdadeiro se eu apenas ligasse $foo.hide().

Trevor Burnham
fonte

Respostas:

238

Como isso:

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

Isso ainda funcionará se um dos pais do elemento for removido (nesse caso, o próprio elemento ainda terá um pai).

SLaks
fonte
14
fazendo $foo.closest(document.documentElement)é mais rápido (se cuidados ninguém jsperf.com/jquery-element-in-dom )
urraka
48
@PerroAzul: Encontrei uma alternativa muito mais rápida: jsperf.com/jquery-element-in-dom/2
SLaks:
3
O desempenho foi minha preocupação imediata com esse método, obrigado por vincular os benchmarks @SLaks. Então parece que $.contains(document.documentElement, $foo.get(0))é o caminho mais rápido.
Christof
10
Há um caminho puro-DOM para fazer isso, que é sintaticamente mais legível, e eu imagino muito semelhante em termos de desempenho:document.contains($foo[0])
Joel Cruz
3
Toda essa conversa sobre performance ...? Os benchmarks mostram um método que leva 15 microssegundos e outro que leva 0,15 microssegundos. Mas, realmente, quem se importa - você não notará nenhuma diferença a menos que faça isso centenas de milhares de vezes. E tudo isso pode mudar se o jQuery ou o mecanismo JavaScript forem otimizados. Basta usar qualquer método que seja melhor para você e para quem precisar manter seu código no futuro.
James
21

Que tal fazer isso:

$element.parents('html').length > 0
Kushagra Gour
fonte
Eu acho que isso é muito mais rápido
Trio Cheung
5

Acabei de perceber uma resposta enquanto digitava minha pergunta: Ligar

$foo.parent()

Se $f00foi removido do DOM, então$foo.parent().length === 0 . Caso contrário, seu comprimento será pelo menos 1.

[Edit: Isso não está totalmente correto, porque um elemento removido ainda pode ter um pai; por exemplo, se você remover a <ul>, cada um de seus filhos <li>ainda terá um pai. Use a resposta do SLaks.

Trevor Burnham
fonte
2
... a menos que o nó removido não era apenas uma criança
Álvaro González
@ Álvaro - Por que isso importa?
usar o seguinte comando
@patrick: Perdi alguma coisa? O que você pode garantir se $ foo.parent (). Length for maior que zero?
Álvaro González
@ Álvaro - o OP está testando para garantir que $foofoi removido do DOM. Se ele foi removido com sucesso, não terá mais um elemento pai. Portanto, $foo.parent().length === 0significa que a remoção foi bem-sucedida.
usar o seguinte comando
1
$ foo.closest ( "body") seria uma correção para esta técnica
georgephillips
4

Como não consigo responder como comentário (carma muito baixo, acho), aqui está uma resposta completa. A maneira mais rápida é facilmente desenrolar a verificação do jQuery para obter suporte ao navegador e reduzir os fatores constantes ao mínimo.

Como também é visto aqui - http://jsperf.com/jquery-element-in-dom/28 - o código ficaria assim:

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

É semanticamente equivalente a jQuery.contains (document.documentElement, elt [0]).

Jaakko Salomaa
fonte
3

Provavelmente a maneira mais performativa é:

document.contains(node); // boolean

Isso também funciona com o jQuery:

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

Origem da MDN

Nota:

lxg
fonte
Como alguém já olhou para esta resposta? Ótima resposta +1 (sugeri uma edição, obrigado)
Guilherme Nascimento
1

Eu gostei dessa abordagem. Sem jQuery e nenhuma pesquisa DOM. Primeiro encontre o pai principal (ancestral). Então veja se esse é o documentElement.

function ancestor(HTMLobj){
  while(HTMLobj.parentElement){HTMLobj=HTMLobj.parentElement}
  return HTMLobj;
}
function inTheDOM(obj){
  return ancestor(obj)===document.documentElement;
}
Brian Button
fonte
1

em vez de iterar os pais, você pode obter o retângulo delimitador, que é todo zeros quando o elemento é desanexado de dom

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

se você quiser lidar com a caixa de borda de um elemento de largura e altura zero na parte superior e esquerda zero, verifique novamente iterando os pais até o documento.body

Erez Pilosof
fonte
Embora esse trecho de código possa resolver o problema, ele não explica por que ou como responde à pergunta. Por favor incluir uma explicação para o seu código , como o que realmente ajuda a melhorar a qualidade do seu post. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código. Sinalizadores / revisores: para respostas somente de código como esta, com voto negativo, não exclua!
Luca Kiebel #
Isso também não reconhece elementos ocultos como "não está no DOM"? Por exemplo, um elemento com display: nonenão tem boundingRect
Philipp
0

Concorde com o comentário de Perro. Também pode ser feito assim:

$foo.parents().last().is(document.documentElement);
Ian Bytchek
fonte
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;
};
Rodrigo Zoff
fonte
-1

Por que não apenas if( $('#foo').length === 0)...:?

stevematdavies
fonte
@stevematdavies A pergunta é "como testar se um elemento em um determinado objeto jQuery está no DOM", não "como testar se um elemento que corresponde a uma determinada sequência de seleção está no DOM".
Trevor Burnham
@TrevorBurnham: com toda a justiça, consultar novamente usando o seletor usado para criar $fooe verificar se a consulta correspondeu a algum elemento, parece uma solução viável para o problema em muitos cenários. Pode até ser mais rápido do que algumas das outras soluções, devido à maneira como o jQuery e os navegadores otimizam certos seletores (como por ID).
Matt
@Matt $ foo pode ser um elemento na memória desanexado do DOM, e pode haver um elemento com o seletor correspondente no DOM. As pessoas que procuram uma resposta para esta pergunta provavelmente desejam verificar se está no DOM ou não. Alguém trabalhando em coisas como essa obviamente sabe como consultar o DOM.
TJ
-1
if($foo.nodeType ){ element is node type}
tharo
fonte