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?
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:
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 includeWhitespaceNodes
parâ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);
document.getElementById()
primeiro, se é isso que você quer dizer:var div = document.getElementById("foo"); var textNodes = getTextNodesIn(div);
.contents()
qualquer maneira implica que ele irá pesquisar no iframe também. Não vejo como isso poderia ser um bug.
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
});
jQuery.contents()
pode ser usado com jQuery.filter
para 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>
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 nodeValue
condicional à sua filter
função, como um simples $.trim(this.nodevalue) !== ''
:
$('element')
.contents()
.filter(function(){
return this.nodeType === 3 && $.trim(this.nodeValue) !== '';
});
Ou, para evitar situações estranhas em que o conteúdo se parece com espaços em branco, mas não é (por exemplo, o ­
caractere de hífen suave , novas linhas \n
, guias, etc.), você pode tentar usar uma Expressão regular. Por exemplo, \S
corresponderá a caracteres que não sejam espaços em branco:
$('element')
.contents()
.filter(function(){
return this.nodeType === 3 && /\S/.test(this.nodeValue);
});
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();
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.descendants
com 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.indexOf
polyfill está incluído no código.
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.
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();
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 pre
tags e não teve problemas.
jQuery("#resultTable td").content().wrap("<pre/>")
Eu tive o mesmo problema e resolvi-o com:
Código:
$.fn.nextNode = function(){
var contents = $(this).parent().contents();
return contents.get(contents.index(this)+1);
}
Uso:
$('#my_id').nextNode();
É como, next()
mas também retorna os nós de texto.