Como seleciono nós de texto com o jQuery?


388

Gostaria de obter todos os nós de texto descendentes de um elemento, como uma coleção jQuery. Qual o melhor jeito pra fazer isso?

Respostas:


261

O jQuery não possui uma função conveniente para isso. Você precisa combinar contents(), que fornecerá apenas nós filhos, mas incluirá nós de texto, com find(), que fornecerá todos os elementos descendentes, mas nenhum nó de texto. Aqui está o que eu vim com:

var getTextNodesIn = function(el) {
    return $(el).find(":not(iframe)").addBack().contents().filter(function() {
        return this.nodeType == 3;
    });
};

getTextNodesIn(el);

Nota: Se você estiver usando o jQuery 1.7 ou anterior, o código acima não funcionará. Para corrigir isso, substitua addBack()por andSelf(). andSelf()é descontinuado a favor de addBack()1,8 em diante.

Isso é um tanto ineficiente comparado aos métodos DOM puros e deve incluir uma solução alternativa feia para a sobrecarga de sua contents()função pelo jQuery (graças a @rabidsnail nos comentários por apontar isso), então aqui está a solução não-jQuery usando uma função recursiva simples. O includeWhitespaceNodesparâmetro controla se os nós de texto em espaço em branco são ou não incluídos na saída (no jQuery, eles são filtrados automaticamente).

Atualização: Corrigido o erro quando includeWhitespaceNodes é falso.

function getTextNodesIn(node, includeWhitespaceNodes) {
    var textNodes = [], nonWhitespaceMatcher = /\S/;

    function getTextNodes(node) {
        if (node.nodeType == 3) {
            if (includeWhitespaceNodes || nonWhitespaceMatcher.test(node.nodeValue)) {
                textNodes.push(node);
            }
        } else {
            for (var i = 0, len = node.childNodes.length; i < len; ++i) {
                getTextNodes(node.childNodes[i]);
            }
        }
    }

    getTextNodes(node);
    return textNodes;
}

getTextNodesIn(el);

O elemento passado pode ser o nome de uma div?
10131 crosenblum

@crosenblum: Você pode ligar document.getElementById()primeiro, se é isso que você quer dizer:var div = document.getElementById("foo"); var textNodes = getTextNodesIn(div);
Tim Down

Por causa de um erro no jQuery, se você tiver iframes no el, precisará usar .find (': not (iframe)') em vez de .find ('*').
bobpoekert

@rabidsnail: Eu acho que o uso de .contents()qualquer maneira implica que ele irá pesquisar no iframe também. Não vejo como isso poderia ser um bug.
Robin Maben

bugs.jquery.com/ticket/11275 Se este é realmente um bug parece estar em debate, mas bug ou não se você chamar find ('*'). contents () em um nó que contém um iframe que não possui adicionado ao domínio, você receberá uma exceção em um ponto indefinido.
22412 Bobpoekert

209

Jauco postou uma boa solução em um comentário, por isso estou copiando aqui:

$(elem)
  .contents()
  .filter(function() {
    return this.nodeType === 3; //Node.TEXT_NODE
  });

34
na verdade $ (elem) .contents () .filter (function () {retorna this.nodeType == Node.TEXT_NODE;}); é suficiente
Jauco

37
O IE7 não define o mundial Node, então você tem que usar this.nodeType == 3, infelizmente: stackoverflow.com/questions/1423599/node-textnode-and-ie7
Christian Oudard

17
Isso não apenas retorna os nós de texto que são filhos diretos do elemento, em vez de descendentes do elemento, conforme solicitado pelo OP?
Tim Down

7
isso não vai funcionar quando o nó de texto é profundamente aninhada dentro de outros elementos, porque o conteúdo () método retorna apenas os nós imediatos crianças, api.jquery.com/contents
minhajul

11
@ Jauco, não, não é o suficiente! como .contents () retorna apenas os nós filhos imediatos
minhajul 16/10/2015

17
$('body').find('*').contents().filter(function () { return this.nodeType === 3; });

6

jQuery.contents()pode ser usado com jQuery.filterpara encontrar todos os nós de texto filho. Com uma pequena reviravolta, você também pode encontrar nós de texto dos netos. Não é necessária recursão:

$(function() {
  var $textNodes = $("#test, #test *").contents().filter(function() {
    return this.nodeType === Node.TEXT_NODE;
  });
  /*
   * for testing
   */
  $textNodes.each(function() {
    console.log(this);
  });
});
div { margin-left: 1em; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

<div id="test">
  child text 1<br>
  child text 2
  <div>
    grandchild text 1
    <div>grand-grandchild text 1</div>
    grandchild text 2
  </div>
  child text 3<br>
  child text 4
</div>

jsFiddle


4

Eu estava recebendo muitos nós de texto vazios com a função de filtro aceita. Se você estiver interessado apenas em selecionar nós de texto que não contenham espaços em branco, tente adicionar um nodeValuecondicional à sua filterfunção, como um simples $.trim(this.nodevalue) !== '':

$('element')
    .contents()
    .filter(function(){
        return this.nodeType === 3 && $.trim(this.nodeValue) !== '';
    });

http://jsfiddle.net/ptp6m97v/

Ou, para evitar situações estranhas em que o conteúdo se parece com espaços em branco, mas não é (por exemplo, o &shy;caractere de hífen suave , novas linhas \n, guias, etc.), você pode tentar usar uma Expressão regular. Por exemplo, \Scorresponderá a caracteres que não sejam espaços em branco:

$('element')
        .contents()
        .filter(function(){
            return this.nodeType === 3 && /\S/.test(this.nodeValue);
        });

3

Se você puder supor que todos os filhos sejam nós de elemento ou nós de texto, essa é uma solução.

Para obter todos os nós de texto filho como uma coleção de jquery:

$('selector').clone().children().remove().end().contents();

Para obter uma cópia do elemento original com os filhos que não são de texto removidos:

$('selector').clone().children().remove().end();

11
Só notei o comentário de Tim Down em outra resposta. Essa solução recebe apenas os filhos diretos, nem todos os descendentes.
colllin

2

Por alguma razão contents(), não funcionou para mim; portanto, se não funcionou para você, aqui está uma solução que eu criei, criei jQuery.fn.descendantscom a opção de incluir nós de texto ou não

Uso


Obter todos os descendentes, incluindo nós de texto e nós de elemento

jQuery('body').descendants('all');

Obter todos os descendentes retornando apenas nós de texto

jQuery('body').descendants(true);

Obter todos os descendentes retornando apenas nós de elemento

jQuery('body').descendants();

Original do café :

jQuery.fn.descendants = ( textNodes ) ->

    # if textNodes is 'all' then textNodes and elementNodes are allowed
    # if textNodes if true then only textNodes will be returned
    # if textNodes is not provided as an argument then only element nodes
    # will be returned

    allowedTypes = if textNodes is 'all' then [1,3] else if textNodes then [3] else [1]

    # nodes we find
    nodes = []


    dig = (node) ->

        # loop through children
        for child in node.childNodes

            # push child to collection if has allowed type
            nodes.push(child) if child.nodeType in allowedTypes

            # dig through child if has children
            dig child if child.childNodes.length


    # loop and dig through nodes in the current
    # jQuery object
    dig node for node in this


    # wrap with jQuery
    return jQuery(nodes)

Soltar na versão Javascript

var __indexOf=[].indexOf||function(e){for(var t=0,n=this.length;t<n;t++){if(t in this&&this[t]===e)return t}return-1}; /* indexOf polyfill ends here*/ jQuery.fn.descendants=function(e){var t,n,r,i,s,o;t=e==="all"?[1,3]:e?[3]:[1];i=[];n=function(e){var r,s,o,u,a,f;u=e.childNodes;f=[];for(s=0,o=u.length;s<o;s++){r=u[s];if(a=r.nodeType,__indexOf.call(t,a)>=0){i.push(r)}if(r.childNodes.length){f.push(n(r))}else{f.push(void 0)}}return f};for(s=0,o=this.length;s<o;s++){r=this[s];n(r)}return jQuery(i)}

Versão Javascript não compactada: http://pastebin.com/cX3jMfuD

Este é um navegador cruzado, um pequeno Array.indexOfpolyfill está incluído no código.


1

Também pode ser feito assim:

var textContents = $(document.getElementById("ElementId").childNodes).filter(function(){
        return this.nodeType == 3;
});

O código acima filtra os textNodes dos nós filhos diretos filhos de um determinado elemento.


11
... mas nem todos os nós filhos descendentes (por exemplo, um nó de texto que é filho de um elemento que é filho do elemento original).
Tim Baixo

0

se você deseja remover todas as tags, tente isso

função:

String.prototype.stripTags=function(){
var rtag=/<.*?[^>]>/g;
return this.replace(rtag,'');
}

uso:

var newText=$('selector').html().stripTags();

0

Para mim, o velho simples .contents()parecia funcionar para retornar os nós de texto, apenas tenha cuidado com os seletores para que você saiba que eles serão nós de texto.

Por exemplo, isso envolveu todo o conteúdo de texto dos TDs na minha tabela com pretags e não teve problemas.

jQuery("#resultTable td").content().wrap("<pre/>")

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.