web-dev-qa-db-fra.com

Comment sélectionner des nœuds de texte avec jQuery?

Je voudrais obtenir tous les nœuds de texte descendant d'un élément, en tant que collection jQuery. Quelle est la meilleure façon de le faire?

378
Christian Oudard

jQuery n'a pas de fonction pratique pour cela. Vous devez combiner contents(), qui donnera uniquement des nœuds enfants, mais comprend des nœuds de texte, avec find(), qui donne tous les éléments descendants mais pas de nœuds de texte. Voici ce que je suis venu avec:

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

getTextNodesIn(el);

Remarque: Si vous utilisez jQuery 1.7 ou une version antérieure, le code ci-dessus ne fonctionnera pas. Pour résoudre ce problème, remplacez addBack() par andSelf() . andSelf() est obsolète en faveur de addBack() à partir de 1.8.

Ceci est quelque peu inefficace par rapport aux méthodes DOM pures et doit inclure un laide solution de contournement pour la surcharge de jQuery avec sa fonction contents() (merci à @rabidsnail dans les commentaires pour l'avoir signalé), donc voici non-jQuery solution utilisant une simple fonction récursive. Le paramètre includeWhitespaceNodes contrôle si les noeuds de texte d'espacement sont inclus ou non dans la sortie (dans jQuery, ils sont automatiquement filtrés).

Mise à jour: Correction d'un bug lorsque includeWhitespaceNodes est faussé.

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);
255
Tim Down

Jauco a posté une bonne solution dans un commentaire, je la copie donc ici:

$(elem)
  .contents()
  .filter(function() {
    return this.nodeType === 3; //Node.TEXT_NODE
  });
207
Christian Oudard
$('body').find('*').contents().filter(function () { return this.nodeType === 3; });
16
He Nrik

jQuery.contents() peut être utilisé avec jQuery.filter pour trouver tous les nœuds de texte enfants. Avec un peu de twist, vous pouvez également trouver des nœuds de texte de petits-enfants. Aucune récursion requise:

_$(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

6
Salman A

Je recevais beaucoup de nœuds de texte vides avec la fonction de filtre acceptée. Si vous souhaitez uniquement sélectionner des nœuds de texte contenant des espaces autres que des espaces, essayez d'ajouter une condition nodeValue à votre fonction filter, comme un simple $.trim(this.nodevalue) !== '':

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

http://jsfiddle.net/ptp6m97v/

Ou, pour éviter des situations étranges où le contenu ressemble à un espace, mais ne le soit pas (par exemple, le trait d'union doux &shy;, les nouvelles lignes \n, les onglets, etc.), vous pouvez essayer d'utiliser une expression régulière. Par exemple, \S correspondra aux caractères non blancs:

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

Si vous pouvez supposer que tous les enfants sont des nœuds d'élément ou des nœuds de texte, c'est une solution.

Pour obtenir tous les nœuds de texte enfants en tant que collection jQuery:

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

Pour obtenir une copie de l'élément d'origine avec les enfants non textuels supprimés:

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

Pour une raison quelconque, contents() n'a pas fonctionné pour moi. Si cela n'a pas fonctionné pour vous, voici une solution que j'ai proposée, j'ai créé jQuery.fn.descendants avec l'option d'inclure ou non des nœuds de texte.

Utilisation


Obtenir tous les descendants, y compris les nœuds de texte et les nœuds d'élément

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

Récupère tous les descendants ne renvoyant que des nœuds de texte

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

Récupère tous les descendants ne renvoyant que des nœuds d'élément

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)

Déposer dans la version 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)}

Version Javascript non-vérifiée: http://Pastebin.com/cX3jMfuD

Ceci est multi-navigateur, un petit Array.indexOf polyfill est inclus dans le code.

2
iConnor

Peut aussi être fait comme ça:

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

Le code ci-dessus filtre les textNodes des nœuds enfants directs des enfants d'un élément donné.

1
Mr_Green

si vous voulez enlever toutes les balises, essayez ceci

fonction:

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

tilisation:

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

J'ai eu le même problème et l'ai résolu avec:

Code:

$.fn.nextNode = function(){
  var contents = $(this).parent().contents();
  return contents.get(contents.index(this)+1);
}

Usage:

$('#my_id').nextNode();

Est comme next() mais renvoie également les nœuds de texte.

0
Guillermo

Pour moi, les vieux .contents() ne contenaient pas de texte, mais vous devez être prudent avec vos sélecteurs pour que vous sachiez qu'ils seront des noeuds de texte.

Par exemple, cela encapsule tout le contenu textuel des TD dans mon tableau avec les balises pre et ne pose aucun problème.

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