Me gustaría obtener todos los nodos de texto descendientes de un elemento, como una colección jQuery. ¿Cuál es la mejor manera de hacer eso?
Me gustaría obtener todos los nodos de texto descendientes de un elemento, como una colección jQuery. ¿Cuál es la mejor manera de hacer eso?
Respuestas:
jQuery no tiene una función conveniente para esto. Debe combinar contents()
, lo que dará solo nodos secundarios pero incluye nodos de texto, con find()
, que da todos los elementos descendientes pero no nodos de texto. Esto es lo que se me ocurrió:
var getTextNodesIn = function(el) {
return $(el).find(":not(iframe)").addBack().contents().filter(function() {
return this.nodeType == 3;
});
};
getTextNodesIn(el);
Nota: Si está utilizando jQuery 1.7 o anterior, el código anterior no funcionará. Para solucionar esto, reemplace addBack()
con andSelf()
. andSelf()
está en desuso a favor de addBack()
1.8 en adelante.
Esto es algo ineficiente en comparación con los métodos DOM puros y tiene que incluir una solución fea para la sobrecarga de jQuery de su contents()
función (gracias a @rabidsnail en los comentarios por señalar eso), así que aquí hay una solución que no es jQuery que utiliza una función recursiva simple. El includeWhitespaceNodes
parámetro controla si los nodos de texto de espacios en blanco se incluyen o no en la salida (en jQuery se filtran automáticamente).
Actualización: error corregido cuando includeWhitespaceNodes es 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()
primero, si eso es lo que quieres decir:var div = document.getElementById("foo"); var textNodes = getTextNodesIn(div);
.contents()
todos modos implica que también buscará en el iframe. No veo cómo podría ser un error.
Jauco publicó una buena solución en un comentario, así que la estoy copiando aquí:
$(elem)
.contents()
.filter(function() {
return this.nodeType === 3; //Node.TEXT_NODE
});
jQuery.contents()
se puede usar con jQuery.filter
para encontrar todos los nodos de texto secundarios. Con un pequeño giro, también puede encontrar nodos de texto de nietos. No se requiere recursividad:
$(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>
Estaba obteniendo muchos nodos de texto vacíos con la función de filtro aceptada. Si solo está interesado en seleccionar nodos de texto que contengan espacios en blanco, intente agregar un nodeValue
condicional a su filter
función, como un simple $.trim(this.nodevalue) !== ''
:
$('element')
.contents()
.filter(function(){
return this.nodeType === 3 && $.trim(this.nodeValue) !== '';
});
O para evitar situaciones extrañas en las que el contenido se ve como un espacio en blanco, pero no lo es (por ejemplo, el ­
carácter de guión suave , líneas nuevas \n
, pestañas, etc.), puede intentar usar una Expresión regular. Por ejemplo, \S
coincidirá con los caracteres que no sean espacios en blanco:
$('element')
.contents()
.filter(function(){
return this.nodeType === 3 && /\S/.test(this.nodeValue);
});
Si puede suponer que todos los elementos secundarios son Nodos de elemento o Nodos de texto, entonces esta es una solución.
Para obtener todos los nodos de texto secundarios como una colección jquery:
$('selector').clone().children().remove().end().contents();
Para obtener una copia del elemento original con hijos sin texto eliminados:
$('selector').clone().children().remove().end();
Por alguna razón contents()
no funcionó para mí, así que si no funcionó para usted, aquí hay una solución que hice, creé jQuery.fn.descendants
con la opción de incluir nodos de texto o no
Uso
Obtenga todos los descendientes, incluidos los nodos de texto y los nodos de elementos
jQuery('body').descendants('all');
Obtener todos los descendientes que solo devuelven nodos de texto
jQuery('body').descendants(true);
Obtener todos los descendientes que devuelven solo nodos de elementos
jQuery('body').descendants();
Coffeescript Original :
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)
Drop In Versión 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)}
Versión Javascript no minificada: http://pastebin.com/cX3jMfuD
Este es un navegador cruzado, Array.indexOf
se incluye un pequeño polyfill en el código.
También se puede hacer así:
var textContents = $(document.getElementById("ElementId").childNodes).filter(function(){
return this.nodeType == 3;
});
El código anterior filtra los textNodes de los nodos secundarios hijos directos de un elemento dado.
si desea quitar todas las etiquetas, intente esto
función:
String.prototype.stripTags=function(){
var rtag=/<.*?[^>]>/g;
return this.replace(rtag,'');
}
uso:
var newText=$('selector').html().stripTags();
Para mí, simplemente viejo .contents()
parece funcionar para devolver los nodos de texto, solo tiene que tener cuidado con sus selectores para que sepa que serán nodos de texto.
Por ejemplo, esto envolvió todo el contenido de texto de los TD en mi tabla con pre
etiquetas y no tuvo problemas.
jQuery("#resultTable td").content().wrap("<pre/>")
Tuve el mismo problema y lo resolví con:
Código:
$.fn.nextNode = function(){
var contents = $(this).parent().contents();
return contents.get(contents.index(this)+1);
}
Uso:
$('#my_id').nextNode();
Es como next()
pero también devuelve los nodos de texto.