Bagaimana cara memilih simpul teks dengan jQuery?

388

Saya ingin mendapatkan semua simpul teks turunan dari elemen, sebagai koleksi jQuery. Apa cara terbaik untuk melakukannya?

Christian Oudard
sumber

Jawaban:

261

jQuery tidak memiliki fungsi yang mudah untuk ini. Anda perlu menggabungkan contents(), yang akan memberikan simpul anak saja tetapi termasuk simpul teks, dengan find(), yang memberikan semua elemen turunan tetapi tidak ada simpul teks. Inilah yang saya buat:

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

getTextNodesIn(el);

Catatan: Jika Anda menggunakan jQuery 1.7 atau yang lebih lama, kode di atas tidak akan berfungsi. Untuk memperbaiki ini, ganti addBack()dengan andSelf(). andSelf()tidak digunakan lagi addBack()sejak 1,8 dan seterusnya.

Ini agak tidak efisien dibandingkan dengan metode DOM murni dan harus menyertakan solusi buruk untuk kelebihan contents()fungsi jQuery (berkat @rabidsnail dalam komentar untuk menunjukkan hal itu), jadi di sini adalah solusi non-jQuery menggunakan fungsi rekursif sederhana. The includeWhitespaceNodeskontrol parameter apakah atau tidak node teks spasi termasuk dalam output (di jQuery mereka secara otomatis disaring).

Pembaruan: Fixed bug ketika includeWhitespaceNodes palsu

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);
Tim Down
sumber
Bisakah elemen lewat, menjadi nama div?
crosenblum
@crosenblum: Anda bisa menelepon document.getElementById()dulu, kalau itu yang Anda maksud:var div = document.getElementById("foo"); var textNodes = getTextNodesIn(div);
Tim Down
Karena ada bug di jQuery jika Anda memiliki iframe di el, Anda harus menggunakan .find (': not (iframe)') sebagai ganti .find ('*').
bobpoekert
@rabidsnail: Saya pikir, penggunaan .contents()lagian menyiratkan itu akan mencari melalui iframe juga. Saya tidak melihat bagaimana itu bisa menjadi bug.
Robin Maben
bugs.jquery.com/ticket/11275 Apakah ini benar-benar bug tampaknya sedang diperdebatkan, tetapi bug atau tidak jika Anda memanggil find ('*'). content () pada node yang berisi iframe yang belum telah ditambahkan ke dom Anda akan mendapatkan pengecualian pada titik yang tidak ditentukan.
bobpoekert
209

Jauco memposting solusi yang baik dalam komentar, jadi saya menyalinnya di sini:

$(elem)
  .contents()
  .filter(function() {
    return this.nodeType === 3; //Node.TEXT_NODE
  });
Christian Oudard
sumber
34
sebenarnya $ (elem) .contents () .filter (function () {return this.nodeType == Node.TEXT_NODE;}); sudah cukup
Jauco
37
IE7 tidak mendefinisikan Node global, jadi Anda harus menggunakan this.nodeType == 3, sayangnya: stackoverflow.com/questions/1423599/node-textnode-and-ie7
Christian Oudard
17
Apakah ini tidak hanya mengembalikan node teks yang merupakan anak-anak langsung dari elemen daripada keturunan elemen seperti yang diminta OP?
Tim Down
7
ini tidak akan berfungsi ketika simpul teks bersarang jauh di dalam elemen lain, karena metode content () hanya mengembalikan node anak langsung, api.jquery.com/contents
minhajul
1
@Jauco, tidak, tidak cukup! sebagai .contents () mengembalikan hanya node anak langsung
minhajul
17
$('body').find('*').contents().filter(function () { return this.nodeType === 3; });
He Nrik
sumber
6

jQuery.contents()dapat digunakan dengan jQuery.filteruntuk menemukan semua simpul teks anak. Dengan sedikit sentuhan, Anda dapat menemukan simpul teks cucu juga. Tidak diperlukan rekursi:

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

Salman A
sumber
4

Saya mendapatkan banyak node teks kosong dengan fungsi filter yang diterima. Jika Anda hanya tertarik untuk memilih node teks yang mengandung non-spasi putih, coba tambahkan nodeValuepersyaratan untuk filterfungsi Anda , seperti sederhana $.trim(this.nodevalue) !== '':

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

http://jsfiddle.net/ptp6m97v/

Atau untuk menghindari situasi aneh di mana konten terlihat seperti spasi, tetapi tidak (misalnya &shy;karakter tanda hubung lunak , baris baru \n, tab, dll.), Anda dapat mencoba menggunakan Ekspresi Reguler. Misalnya, \Sakan cocok dengan karakter non-spasi putih:

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

Jika Anda dapat membuat asumsi bahwa semua anak adalah Element Nodes atau Text Nodes, maka ini adalah salah satu solusinya.

Untuk mendapatkan semua simpul teks anak sebagai koleksi jquery:

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

Untuk mendapatkan salinan elemen asli dengan anak-anak non-teks dihapus:

$('selector').clone().children().remove().end();
colllin
sumber
1
Perhatikan komentar Tim Down tentang jawaban lain. Solusi ini hanya mendapatkan anak-anak langsung, tidak semua keturunan.
colllin
2

Untuk beberapa alasan contents()tidak bekerja untuk saya, jadi jika itu tidak berhasil untuk Anda, inilah solusi yang saya buat, saya buat jQuery.fn.descendantsdengan opsi untuk memasukkan node teks atau tidak

Pemakaian


Dapatkan semua keturunan termasuk simpul teks dan simpul elemen

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

Dapatkan semua keturunan hanya mengembalikan node teks

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

Dapatkan semua keturunan hanya mengembalikan node elemen

jQuery('body').descendants();

Coffeescript Asli :

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)

Jatuhkan Versi 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 Javascript yang tidak ditambang: http://pastebin.com/cX3jMfuD

Ini adalah browser silang, Array.indexOfpolyfill kecil termasuk dalam kode.

iConnor
sumber
1

Bisa juga dilakukan seperti ini:

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

Kode di atas menyaring textNodes dari anak-anak langsung node anak dari elemen yang diberikan.

Tuan_Green
sumber
1
... tetapi tidak semua simpul turunan turunan (mis. simpul teks yang merupakan turunan dari elemen yang merupakan turunan dari elemen asli).
Tim Down
0

jika Anda ingin menghapus semua tag, coba ini

fungsi:

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

pemakaian:

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

Bagi saya, old plain .contents()tampaknya berfungsi mengembalikan node teks, cukup hati-hati dengan pemilih Anda sehingga Anda tahu mereka akan menjadi node teks.

Misalnya, ini membungkus semua konten teks dari TDs di meja saya dengan pretag dan tidak memiliki masalah.

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

Saya memiliki masalah yang sama dan menyelesaikannya dengan:

Kode:

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

Pemakaian:

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

Seperti next()tetapi juga mengembalikan node teks.

Guillermo
sumber
.nextSibling berasal dari spesifikasi Dom: developer.mozilla.org/en/Document_Object_Model_(DOM)/...
Guillermo