Wie wähle ich Textknoten mit jQuery aus?


388

Ich möchte alle untergeordneten Textknoten eines Elements als jQuery-Auflistung erhalten. Was ist der beste Weg das zu tun?

Antworten:


261

jQuery hat hierfür keine praktische Funktion. Sie müssen kombinieren contents(), was nur untergeordnete Knoten find()ergibt, aber Textknoten enthält, mit , was alle untergeordneten Elemente, aber keine Textknoten ergibt. Folgendes habe ich mir ausgedacht:

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

getTextNodesIn(el);

Hinweis: Wenn Sie jQuery 1.7 oder früher verwenden, funktioniert der obige Code nicht. Um dies zu beheben, ersetzen Sie addBack()durch andSelf(). andSelf()wird zugunsten von addBack()ab 1,8 abgelehnt .

Dies ist im Vergleich zu reinen DOM-Methoden etwas ineffizient und muss eine hässliche Problemumgehung für die Überladung seiner contents()Funktion durch jQuery enthalten (dank @rabidsnail in den Kommentaren, um darauf hinzuweisen). Daher handelt es sich hier um eine Nicht-jQuery-Lösung, die eine einfache rekursive Funktion verwendet. Der includeWhitespaceNodesParameter steuert, ob Whitespace-Textknoten in der Ausgabe enthalten sind oder nicht (in jQuery werden sie automatisch herausgefiltert).

Update: Fehler behoben, wenn includeWhitespaceNodes falsch ist.

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

Kann das übergebene Element der Name eines Div sein?
Crosenblum

@ Crosenblum: Sie könnten document.getElementById()zuerst anrufen , wenn Sie das meinen:var div = document.getElementById("foo"); var textNodes = getTextNodesIn(div);
Tim Down

Aufgrund eines Fehlers in jQuery müssen Sie, wenn Sie iframes in el haben, .find (': not (iframe)') anstelle von .find ('*') verwenden.
Bobpoekert

@ Rabidsnail: Ich denke, die Verwendung von .contents()sowieso impliziert, dass es auch den Iframe durchsuchen wird. Ich sehe nicht ein, wie es ein Fehler sein könnte.
Robin Maben

bugs.jquery.com/ticket/11275 Ob dies tatsächlich ein Fehler ist, scheint zur Debatte zu stehen, aber Fehler oder nicht, wenn Sie find ('*'). content () auf einem Knoten aufrufen, der einen Iframe enthält, der keinen hat Wurde dem Dom hinzugefügt, wird an einer undefinierten Stelle eine Ausnahme angezeigt.
Bobpoekert

209

Jauco hat in einem Kommentar eine gute Lösung gepostet, daher kopiere ich sie hier:

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

34
tatsächlich $ (elem) .contents () .filter (function () {return this.nodeType == Node.TEXT_NODE;}); ist genug
Jauco

37
IE7 definiert den Knoten nicht global, daher müssen Sie this.nodeType == 3 leider verwenden: stackoverflow.com/questions/1423599/node-textnode-and-ie7
Christian Oudard

17
Gibt dies nicht nur die Textknoten zurück, die die direkten untergeordneten Elemente des Elements sind, sondern auch die Nachkommen des Elements, wie vom OP angefordert?
Tim Down

7
Dies funktioniert nicht, wenn der Textknoten
minhajul

1
@ Jauco, nein, nicht genug! als .contents () gibt nur die unmittelbaren
untergeordneten

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

6

jQuery.contents()kann verwendet werden jQuery.filter, um alle untergeordneten Textknoten zu finden. Mit einer kleinen Drehung können Sie auch Textknoten für Enkelkinder finden. Keine Rekursion erforderlich:

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

Ich habe viele leere Textknoten mit der akzeptierten Filterfunktion erhalten. Wenn Sie nur daran interessiert sind Textknoten in der Auswahl , die kein Leerzeichen enthalten, versuchen , eine Zugabe nodeValuebedingte zu Ihrer filterFunktion, wie eine einfache $.trim(this.nodevalue) !== '':

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

http://jsfiddle.net/ptp6m97v/

Um seltsame Situationen zu vermeiden, in denen der Inhalt wie ein Leerzeichen aussieht, dies jedoch nicht ist (z. B. das weiche Bindestrichzeichen &shy;, Zeilenumbrüche \n, Tabulatoren usw.), können Sie versuchen, einen regulären Ausdruck zu verwenden. Entspricht beispielsweise \Sallen Nicht-Leerzeichen:

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

3

Wenn Sie davon ausgehen können, dass alle untergeordneten Elemente entweder Elementknoten oder Textknoten sind, ist dies eine Lösung.

So rufen Sie alle untergeordneten Textknoten als JQuery-Sammlung ab:

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

So erhalten Sie eine Kopie des Originalelements, bei der nicht untergeordnete Textelemente entfernt wurden:

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

1
Ich habe gerade Tim Downs Kommentar zu einer anderen Antwort bemerkt. Diese Lösung erhält nur die direkten Kinder, nicht alle Nachkommen.
Colllin

2

Aus irgendeinem Grund contents()hat es bei mir nicht funktioniert. Wenn es bei Ihnen nicht funktioniert hat, habe ich eine Lösung erstellt, die ich jQuery.fn.descendantsmit der Option erstellt habe, Textknoten einzuschließen oder nicht

Verwendungszweck


Holen Sie sich alle Nachkommen einschließlich Textknoten und Elementknoten

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

Lassen Sie alle Nachkommen nur Textknoten zurückgeben

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

Lassen Sie alle Nachkommen nur Elementknoten zurückgeben

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 Javascript Version

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

Nicht minimierte Javascript-Version: http://pastebin.com/cX3jMfuD

Dies ist browserübergreifend, eine kleine Array.indexOfPolyfüllung ist im Code enthalten.


1

Kann auch so gemacht werden:

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

Der obige Code filtert die textNodes von direkten untergeordneten untergeordneten Knoten eines bestimmten Elements.


1
... aber nicht alle untergeordneten untergeordneten Knoten (z. B. ein Textknoten, der das untergeordnete Element eines Elements ist, das dem untergeordneten Element untergeordnet ist).
Tim Down

0

Wenn Sie alle Tags entfernen möchten, versuchen Sie dies

Funktion:

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

Verwendungszweck:

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

0

Für mich .contents()schien einfach alt zu funktionieren, um die Textknoten zurückzugeben. Sie müssen nur vorsichtig mit Ihren Selektoren sein, damit Sie wissen, dass es sich um Textknoten handelt.

Dies hat beispielsweise den gesamten Textinhalt der TDs in meiner Tabelle mit preTags versehen und hatte keine Probleme.

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.