Làm cách nào để chọn các nút văn bản với jQuery?


388

Tôi muốn lấy tất cả các nút văn bản con cháu của một phần tử, dưới dạng một bộ sưu tập jQuery. Cách tốt nhất để làm điều đó là gì?

Câu trả lời:


261

jQuery không có chức năng thuận tiện cho việc này. Bạn cần kết hợp contents(), sẽ chỉ cung cấp các nút con nhưng bao gồm các nút văn bản, với find(), cung cấp tất cả các phần tử con cháu nhưng không có nút văn bản. Đây là những gì tôi nghĩ ra:

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

getTextNodesIn(el);

Lưu ý: Nếu bạn đang sử dụng jQuery 1.7 trở về trước, mã ở trên sẽ không hoạt động. Để khắc phục điều này, thay thế addBack()bằng andSelf(). andSelf()không được ủng hộ addBack()từ 1.8 trở đi.

Điều này hơi kém hiệu quả so với các phương thức DOM thuần túy và phải bao gồm một cách giải quyết xấu cho việc quá tải contents()chức năng của jQuery (nhờ @rabidsnail trong các nhận xét để chỉ ra điều đó), vì vậy đây là giải pháp không phải jQuery sử dụng hàm đệ quy đơn giản. Các includeWhitespaceNodesđiều khiển tham số hay không nút văn bản trắng có trong đầu ra (trong jQuery họ sẽ được tự động lọc ra).

Cập nhật: Đã sửa lỗi khi includeWhitespaceNodes bị lỗi.

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

Phần tử được truyền vào, có phải là tên của div không?
crosenblum

@crosenblum: Bạn có thể gọi document.getElementById()trước, nếu đó là ý bạn:var div = document.getElementById("foo"); var textNodes = getTextNodesIn(div);
Tim Down

Do lỗi trong jQuery nếu bạn có bất kỳ iframe nào trong el, bạn sẽ cần sử dụng .find (': not (iframe)') thay vì .find ('*').
bobpoekert

@rabidsnail: Tôi nghĩ rằng, việc sử dụng .contents()anyways ngụ ý nó cũng sẽ tìm kiếm thông qua iframe. Tôi không thấy làm thế nào nó có thể là một lỗi.
Robin Maben

bug.jquery.com/ticket/11275 Cho dù đây thực sự là một lỗi có thể gây tranh cãi, nhưng có lỗi hay không nếu bạn gọi find ('*'). nội dung () trên một nút có chứa iframe không có đã được thêm vào dom bạn sẽ nhận được một ngoại lệ tại một điểm không xác định.
bobpoekert

209

Jauco đã đăng một giải pháp tốt trong một bình luận, vì vậy tôi đang sao chép nó ở đây:

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

34
thực sự $ (elem) .contents () .filter (function () {return this.nodeType == Node.TEXT_NODE;}); là đủ
Jauco

37
IE7 không xác định Node toàn cầu, vì vậy bạn phải sử dụng this.nodeType == 3, thật không may: stackoverflow.com/questions/1423599/node-textnode-and-ie7
Christian Oudard

17
Điều này không chỉ trả về các nút văn bản là con trực tiếp của phần tử chứ không phải là hậu duệ của phần tử như OP yêu cầu?
Tim Down

7
điều này sẽ không hoạt động khi nút văn bản được lồng sâu bên trong các phần tử khác, bởi vì phương thức nội dung () chỉ trả về các nút con ngay lập tức, api.jquery.com/contents
minhajul

1
@Jauco, không, không đủ! as .contents () chỉ trả về các nút con ngay lập tức
minhajul

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

6

jQuery.contents()có thể được sử dụng jQuery.filterđể tìm tất cả các nút văn bản con. Với một chút thay đổi, bạn cũng có thể tìm thấy các nút văn bản cháu. Không yêu cầu đệ quy:

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

Tôi đã nhận được rất nhiều nút văn bản trống với chức năng lọc được chấp nhận. Nếu bạn chỉ quan tâm đến việc chọn các nút văn bản có chứa khoảng trắng, hãy thử thêm một nodeValueđiều kiện vào filterhàm của bạn , như đơn giản $.trim(this.nodevalue) !== '':

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

http://jsfiddle.net/ptp6m97v/

Hoặc để tránh các tình huống lạ khi nội dung trông giống như khoảng trắng, nhưng không phải (ví dụ: &shy;ký tự dấu gạch nối mềm , dòng mới \n, tab, v.v.), bạn có thể thử sử dụng Biểu thức chính quy. Ví dụ: \Ssẽ khớp với bất kỳ ký tự không phải khoảng trắng nào:

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

3

Nếu bạn có thể đưa ra giả định rằng tất cả trẻ em là Nút yếu tố hoặc Nút văn bản, thì đây là một giải pháp.

Để có được tất cả các nút văn bản con dưới dạng một bộ sưu tập jquery:

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

Để có được một bản sao của phần tử gốc với các phần tử không phải là văn bản đã bị xóa:

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

1
Chỉ cần chú ý bình luận của Tim Down về một câu trả lời khác. Giải pháp này chỉ có được những đứa trẻ trực tiếp, không phải tất cả con cháu.
colllin

2

Vì một số lý do contents()không hiệu quả với tôi, vì vậy nếu nó không hiệu quả với bạn, đây là giải pháp tôi đã thực hiện, tôi đã tạo jQuery.fn.descendantsvới tùy chọn bao gồm các nút văn bản hoặc không

Sử dụng


Nhận tất cả các hậu duệ bao gồm các nút văn bản và các nút phần tử

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

Nhận tất cả con cháu chỉ trả lại các nút văn bản

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

Nhận tất cả con cháu chỉ trả lại các nút phần tử

jQuery('body').descendants();

Coffeescript gốc :

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)

Phiên bả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)}

Phiên bản Javascript chưa hoàn thành: http://pastebin.com/cX3jMfuD

Đây là trình duyệt chéo, một Array.indexOfpolyfill nhỏ được bao gồm trong mã.


1

Cũng có thể được thực hiện như thế này:

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

Đoạn mã trên lọc các textNodes từ các nút con trực tiếp của một phần tử đã cho.


1
... nhưng không phải tất cả các nút con hậu duệ (ví dụ: nút văn bản là con của một phần tử là con của phần tử gốc).
Tim Down

0

nếu bạn muốn loại bỏ tất cả các thẻ, hãy thử điều này

chức năng:

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

sử dụng:

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

0

Đối với tôi, đơn giản cũ .contents()dường như hoạt động để trả về các nút văn bản, chỉ cần cẩn thận với các bộ chọn của bạn để bạn biết chúng sẽ là các nút văn bản.

Ví dụ, phần này bao bọc tất cả nội dung văn bản của các TD trong bảng của tôi bằng precác thẻ và không có vấn đề gì.

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

0

Tôi đã có cùng một vấn đề và giải quyết nó với:

Mã số:

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

Sử dụng:

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

Là thích next()nhưng cũng trả về các nút văn bản.


.nextSibling lấy từ thông số kỹ thuật của Dom: developer.mozilla.org/en/Document_Object_Model_(DOM)/
Guillermo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.