Sử dụng .text () để chỉ truy xuất văn bản không được lồng trong thẻ con


386

Nếu tôi có html như thế này:

<li id="listItem">
    This is some text
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>

Tôi đang cố gắng sử dụng .text()để truy xuất chỉ chuỗi "Đây là một số văn bản", nhưng nếu tôi muốn nói $('#list-item').text(), tôi nhận được "Đây là một số văn bản nhịp văn bản đầu tiên Văn bản nhịp thứ hai".

Có cách nào để có được (và có thể loại bỏ, thông qua một cái gì đó giống như .text("")) chỉ văn bản miễn phí trong một thẻ chứ không phải văn bản trong các thẻ con của nó không?

HTML không phải do tôi viết, vì vậy đây là những gì tôi phải làm việc với. Tôi biết rằng sẽ rất đơn giản khi chỉ bọc văn bản trong các thẻ khi viết html, nhưng một lần nữa, html được viết sẵn.


Bởi vì tôi chưa có đủ danh tiếng để bình luận và tôi không muốn mất kiến ​​thức (hy vọng nó sẽ giúp người khác), một sự kết hợp giữa câu trả lời của macio.Jun , RegExp và iStranger để thay thế một textNode bằng HTML trong Javascript? cho phép tôi tìm kiếm các nút chỉ văn bản cho một chuỗi và thay thế tất cả các lần xuất hiện bằng các liên kết.
JDQ

Câu trả lời:


509

Tôi thích cách triển khai có thể sử dụng lại này dựa trên clone()phương pháp tìm thấy ở đây để chỉ lấy văn bản bên trong phần tử cha.

Mã được cung cấp để tham khảo dễ dàng:

$("#foo")
    .clone()    //clone the element
    .children() //select all the children
    .remove()   //remove all the children
    .end()  //again go back to selected element
    .text();

5
Với giải pháp này, bạn chỉ nhận được văn bản mà không có con, nhưng bạn không thể chỉ thay thế văn bản.
BenRoe

1
Tôi không nhận được 1 điều: Nếu .end () quay lại phần tử được chọn, hơn văn bản () sẽ sao chép văn bản gốc với các phần tử con. Nhưng trong thực tế tôi thấy rằng văn bản từ bản sao bị thao túng của chúng tôi đang được sao chép. Vì vậy, end () quay trở lại clone ()?

68
Đây là một cách thực sự không hiệu quả để làm điều này
billyonecan

5
@billyonecan, bạn có thể đề xuất một phương pháp hiệu quả hơn không? Điều này hấp dẫn bởi vì nó "sạch" và "ngắn". Bạn có đề nghị gì?
derekmx271

1
@ derekmx271 hãy xem câu trả lời của Stuart
billyonecan

364

Câu trả lời đơn giản:

$("#listItem").contents().filter(function(){ 
  return this.nodeType == 3; 
})[0].nodeValue = "The text you want to replace with" 

38
Tôi không hiểu tại sao câu trả lời hiệu quả (không tạo ra cấu trúc dữ liệu không liên quan) không được bình chọn nhiều như câu trả lời trông ít đáng sợ hơn. +5 nếu tôi có thể.
Steven Lu

16
câu trả lời đơn giản và hiệu quả
Paul Carroll

9
Điều này không chỉ hiệu quả hơn mà còn đúng! Giải pháp này phục vụ cho các tình huống khi văn bản nằm rải rác giữa các yếu tố con. +5
Kyryll Tenin Baum

15
Để rõ ràng hơn nữa, nếu bạn sử dụng IE8 +, bạn có thể sử dụng this.nodeType == Node.TEXT_NODEthay vì this.nodeType == 3. Dễ đọc và hiểu IMO hơn.
NorTicUs

8
Điều này sẽ phá vỡ nếu bạn sử dụng nó trên một cái gì đó không có văn bản. Nếu bạn đang sử dụng chức năng này như một chức năng và có một kịch bản mà bạn có thể có hoặc không có văn bản, chỉ cần ghi lại .contents().filter(...)cuộc gọi vào một biến cục bộ và kiểm tra độ dài của nó, ví dụ: var text = $(this).contents().filter(...); if (text.length) { return text[0].nodeValue; } return "";
Carl Bussema

157

Đây có vẻ như là một trường hợp lạm dụng jquery đối với tôi. Sau đây sẽ lấy văn bản bỏ qua các nút khác:

document.getElementById("listItem").childNodes[0];

Bạn sẽ cần phải cắt nó nhưng nó sẽ mang lại cho bạn những gì bạn muốn trong một dòng dễ dàng.

BIÊN TẬP

Ở trên sẽ có được nút văn bản . Để có được văn bản thực tế, sử dụng này:

document.getElementById("listItem").childNodes[0].nodeValue;

31
Câu trả lời hay nhất, bạn không cần phải có plugin cho điều này hoặc một chuỗi 10 cuộc gọi jQuery. $('.foo')[0].childNodes[0].nodeValue.trim()
mưa

5
Điều gì xảy ra nếu nội dung văn bản được chia thành nhiều nút (như chuỗi crlf, text, crlf)? Có bất kỳ (cuộc sống rael) nào đảm bảo rằng dom được xây dựng bởi ua sẽ sử dụng cấu trúc đơn giản nhất không?
sụp đổ

5
Hoàn toàn là câu trả lời tốt nhất ... tại sao những người khác đôi khi sử dụng jQuery?
ncubica

11
Điều này chỉ hoạt động trong trường hợp văn bản <div id = "listItem"> bạn muốn <span> khác </ span> </ div>. Nó sẽ không hoạt động cho <div id = "listItem"> <span> văn bản </ span> khác mà bạn muốn </ div>
Spencer

1
Đôi khi bạn không có document. Đến đây bằng cách sử dụng cheerio.
flash

67

Dễ dàng và nhanh chóng hơn:

$("#listItem").contents().get(0).nodeValue

Trình duyệt chéo này có tương thích không?
Rajat Gupta

Tất nhiên, nó lấy một trong các phần tử khớp với đối tượng jQuery được cung cấp bởi chỉ mục: Jquery Docs .get () .
WakeupMorning

1
@Nate Trong trường hợp bạn cần sử dụng nó trên thẻ <br/>, bạn có thể sử dụng câu trả lời của macio.Jun .
WakeupMorning

Đây phải là câu trả lời được chấp nhận.
Daniel

2
Tại sao get(0)thay vì chỉ [0]?
Clonkex

28

Tương tự như câu trả lời được chấp nhận, nhưng không có nhân bản:

$("#foo").contents().not($("#foo").children()).text();

Và đây là một plugin jQuery cho mục đích này:

$.fn.immediateText = function() {
    return this.contents().not(this.children()).text();
};

Dưới đây là cách sử dụng plugin này:

$("#foo").immediateText(); // get the text without children

T trong t.children () là gì?
FrEaKmAn

Đây là một giải pháp trùng lặp với giải pháp mà pbjk đã viết vào ngày 15 tháng 1 ... dù sao đi nữa - nó có vẻ tốt.
Oskar Holmkratz

1
Không thực sự, @Oskar. Phần .contents()quan trọng ở đây!
DUzun

Giải pháp không tốt nếu các nút của bạn không sử dụng id.
AndroidDev

3
@AndroidDev Bạn luôn có thể thay thế bộ chọn bằng bất cứ thứ gì phù hợp với bạn. Đây chỉ là để minh họa kỹ thuật! Tôi cũng đã thêm một phiên bản Plugin để cho thấy rằng nó hoạt động ngay cả khi không có ID
DUzun

8

không phải là mã:

var text  =  $('#listItem').clone().children().remove().end().text();

chỉ trở thành jQuery vì lợi ích của jQuery? Khi các hoạt động đơn giản liên quan đến nhiều lệnh bị xâu chuỗi & xử lý nhiều (không cần thiết) đó, có lẽ đã đến lúc viết một phần mở rộng jQuery:

(function ($) {
    function elementText(el, separator) {
        var textContents = [];
        for(var chld = el.firstChild; chld; chld = chld.nextSibling) {
            if (chld.nodeType == 3) { 
                textContents.push(chld.nodeValue);
            }
        }
        return textContents.join(separator);
    }
    $.fn.textNotChild = function(elementSeparator, nodeSeparator) {
    if (arguments.length<2){nodeSeparator="";}
    if (arguments.length<1){elementSeparator="";}
        return $.map(this, function(el){
            return elementText(el,nodeSeparator);
        }).join(elementSeparator);
    }
} (jQuery));

để gọi:

var text = $('#listItem').textNotChild();

các đối số trong trường hợp gặp phải một kịch bản khác nhau, chẳng hạn như

<li>some text<a>more text</a>again more</li>
<li>second text<a>more text</a>again more</li>

var text = $("li").textNotChild(".....","<break>");

văn bản sẽ có giá trị:

some text<break>again more.....second text<break>again more

1
Đẹp. Làm thế nào về việc biến điều này thành một yêu cầu kéo cho phiên bản tiếp theo của jQuery?
Jared Tomaszewski

8

Thử cái này:

$('#listItem').not($('#listItem').children()).text()

6

Nó sẽ cần phải là một cái gì đó phù hợp với nhu cầu, phụ thuộc vào cấu trúc bạn trình bày. Đối với ví dụ bạn đã cung cấp, điều này hoạt động:

$(document).ready(function(){
     var $tmp = $('#listItem').children().remove();
     $('#listItem').text('').append($tmp);
});

Bản giới thiệu: http://jquery.nodnod.net/case/2385/run

Nhưng nó hoàn toàn phụ thuộc vào đánh dấu tương tự như những gì bạn đã đăng.


2
Người đọc tương lai hãy cẩn thận: mã trong câu trả lời này giết chết trẻ em trong phần tử thực tế. Người ta nên sử dụng clonephương pháp ở đây nếu đó không phải là hiệu quả dự định.
Mahn

Câu trả lời của @ DotNetWala, bên dưới, và nên được sử dụng thay cho câu trả lời này. Hoặc ít nhất, sử dụng .detach()phương pháp thay vì .remove().
Don McCurdy


4
jQuery.fn.ownText = function () {
    return $(this).contents().filter(function () {
        return this.nodeType === Node.TEXT_NODE;
    }).text();
};

1
Cảm ơn bạn vì đoạn mã này, có thể cung cấp một số trợ giúp ngay lập tức. Một lời giải thích phù hợp sẽ cải thiện đáng kể giá trị giáo dục của nó bằng cách chỉ ra lý do tại sao đây là một giải pháp tốt cho vấn đề và sẽ giúp nó hữu ích hơn cho những độc giả tương lai với những câu hỏi tương tự, nhưng không giống nhau. Vui lòng chỉnh sửa câu trả lời của bạn để thêm giải thích và đưa ra dấu hiệu về những hạn chế và giả định được áp dụng.
Toby Speight

3

Đây là một câu hỏi cũ nhưng câu trả lời hàng đầu là rất không hiệu quả. Đây là một giải pháp tốt hơn:

$.fn.myText = function() {
    var str = '';

    this.contents().each(function() {
        if (this.nodeType == 3) {
            str += this.textContent || this.innerText || '';
        }
    });

    return str;
};

Và chỉ cần làm điều này:

$("#foo").myText();

3

Tôi cho rằng đây cũng sẽ là một giải pháp tốt - nếu bạn muốn lấy nội dung của tất cả các nút văn bản là phần tử con trực tiếp của phần tử được chọn.

$(selector).contents().filter(function(){ return this.nodeType == 3; }).text();

Lưu ý: Tài liệu jQuery sử dụng mã tương tự để giải thích chức năng nội dung: https://api.jquery.com/contents/

PS Cũng có một cách xấu hơn để làm điều đó, nhưng điều này cho thấy sâu hơn cách mọi thứ hoạt động và cho phép phân tách tùy chỉnh giữa các nút văn bản (có thể bạn muốn ngắt dòng ở đó)

$(selector).contents().filter(function(){ return this.nodeType == 3; }).map(function() { return this.nodeValue; }).toArray().join("");

1

Tôi đề xuất sử dụng createTreeWalker để tìm tất cả các phần tử văn bản không được đính kèm với các phần tử html (chức năng này có thể được sử dụng để mở rộng jQuery):

function textNodesOnlyUnder(el) {
  var resultSet = [];
  var n = null;
  var treeWalker  = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, function (node) {
    if (node.parentNode.id == el.id && node.textContent.trim().length != 0) {
      return NodeFilter.FILTER_ACCEPT;
    }
    return NodeFilter.FILTER_SKIP;
  }, false);
  while (n = treeWalker.nextNode()) {
    resultSet.push(n);
  }
  return resultSet;
}



window.onload = function() {
  var ele = document.getElementById('listItem');
  var textNodesOnly = textNodesOnlyUnder(ele);
  var resultingText = textNodesOnly.map(function(val, index, arr) {
    return 'Text element N. ' + index + ' --> ' + val.textContent.trim();
  }).join('\n');
  document.getElementById('txtArea').value = resultingText;
}
<li id="listItem">
    This is some text
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>
<textarea id="txtArea" style="width: 400px;height: 200px;"></textarea>


1

Nếu vị trí indexcủa nút văn bản được cố định giữa các anh chị em của nó, bạn có thể sử dụng

$('parentselector').contents().eq(index).text()

1

Không chắc chắn mức độ linh hoạt hoặc bao nhiêu trường hợp bạn cần nó để che, nhưng đối với ví dụ của bạn, nếu văn bản luôn xuất hiện trước các thẻ HTML đầu tiên - tại sao không chỉ tách html bên trong ở thẻ đầu tiên và lấy trước:

$('#listItem').html().split('<span')[0]; 

và nếu bạn cần nó rộng hơn có lẽ chỉ cần

$('#listItem').html().split('<')[0]; 

và nếu bạn cần văn bản giữa hai điểm đánh dấu, như sau một điều nhưng trước một điều khác, bạn có thể làm một cái gì đó như (chưa được kiểm tra) và sử dụng các câu lệnh để làm cho nó đủ linh hoạt để có điểm bắt đầu hoặc kết thúc hoặc cả hai, trong khi tránh các lỗi ref :

var startMarker = '';// put any starting marker here
var endMarker = '<';// put the end marker here
var myText = String( $('#listItem').html() );
// if the start marker is found, take the string after it
myText = myText.split(startMarker)[1];        
// if the end marker is found, take the string before it
myText = myText.split(endMarker)[0];
console.log(myText); // output text between the first occurrence of the markers, assuming both markers exist.  If they don't this will throw an error, so some if statements to check params is probably in order...

Tôi thường tạo các hàm tiện ích cho những thứ hữu ích như thế này, không mắc lỗi và sau đó dựa vào chúng thường xuyên một lần, thay vì luôn viết lại kiểu thao tác chuỗi này và mạo hiểm tham chiếu null, v.v. Bằng cách đó, bạn có thể sử dụng lại hàm trong rất nhiều dự án và không bao giờ phải lãng phí thời gian để gỡ lỗi một lần nữa tại sao một tham chiếu chuỗi có lỗi tham chiếu không xác định. Có thể không phải là mã 1 dòng ngắn nhất từng có, nhưng sau khi bạn có chức năng tiện ích, nó là một dòng từ đó trở đi. Lưu ý hầu hết các mã chỉ là xử lý các tham số có hoặc không để tránh lỗi :)

Ví dụ:

/**
* Get the text between two string markers.
**/
function textBetween(__string,__startMark,__endMark){
    var hasText = typeof __string !== 'undefined' && __string.length > 0;
    if(!hasText) return __string;
    var myText = String( __string );
    var hasStartMarker = typeof __startMark !== 'undefined' && __startMark.length > 0 && __string.indexOf(__startMark)>=0;
    var hasEndMarker =  typeof __endMark !== 'undefined' && __endMark.length > 0 && __string.indexOf(__endMark) > 0;
    if( hasStartMarker )  myText = myText.split(__startMark)[1];
    if( hasEndMarker )    myText = myText.split(__endMark)[0];
    return myText;
}

// now with 1 line from now on, and no jquery needed really, but to use your example:
var textWithNoHTML = textBetween( $('#listItem').html(), '', '<'); // should return text before first child HTML tag if the text is on page (use document ready etc)

nếu bạn cần thay thế văn bản, chỉ cần sử dụng $('#listItem').html( newHTML ); trong đó newHTML là một biến đã có văn bản bị loại bỏ.
OG Sean


0

Tôi đã đưa ra một giải pháp cụ thể sẽ hiệu quả hơn nhiều so với việc nhân bản và sửa đổi bản sao. Giải pháp này chỉ hoạt động với hai bảo lưu sau, nhưng sẽ hiệu quả hơn giải pháp hiện được chấp nhận:

  1. Bạn chỉ nhận được văn bản
  2. Văn bản bạn muốn trích xuất là trước các phần tử con

Với những gì đã nói, đây là mã:

// 'element' is a jQuery element
function getText(element) {
  var text = element.text();
  var childLength = element.children().text().length;
  return text.slice(0, text.length - childLength);
}

0

Cũng giống như câu hỏi, tôi đang cố gắng để trích xuất văn bản để thực hiện một số thay thế regex của văn bản nhưng đã nhận được vấn đề mà các yếu tố bên trong của tôi (ví dụ: <i>, <div>, <span>, vv) được việc cũng bị loại bỏ.

Các mã sau đây dường như hoạt động tốt và giải quyết tất cả các vấn đề của tôi.

Nó sử dụng một số câu trả lời được cung cấp ở đây nhưng đặc biệt, sẽ chỉ thay thế văn bản khi phần tử là nodeType === 3.

$(el).contents().each(function() { 
  console.log(" > Content: %s [%s]", this, (this.nodeType === 3));

  if (this.nodeType === 3) {
    var text = this.textContent;
    console.log(" > Old   : '%s'", text);

    regex = new RegExp("\\[\\[" + rule + "\\.val\\]\\]", "g");
    text = text.replace(regex, value);

    regex = new RegExp("\\[\\[" + rule + "\\.act\\]\\]", "g");
    text = text.replace(regex, actual);

    console.log(" > New   : '%s'", text);
    this.textContent = text;
  }
});

Những gì ở trên là vòng lặp thông qua tất cả các yếu tố của cái đã cho el(mà đơn giản là thu được với $("div.my-class[name='some-name']");. Đối với mỗi phần tử bên trong, về cơ bản nó sẽ bỏ qua chúng. Đối với mỗi phần của văn bản (như được xác định bởiif (this.nodeType === 3) ), nó sẽ chỉ áp dụng thay thế regex cho các phần tử đó .

Các this.textContent = textphần chỉ đơn giản là thay thế các văn bản thay thế, mà trong trường hợp của tôi, tôi đang tìm kiếm tokens như [[min.val]], [[max.val]]vv

Đoạn trích mã ngắn này sẽ giúp bất cứ ai cố gắng làm những gì câu hỏi đang hỏi ... và một chút nữa.


-1

chỉ cần đặt nó trong một <p>hoặc<font> và lấy $ ('# listItem font'). text ()

Điều đầu tiên tôi nghĩ đến

<li id="listItem">
    <font>This is some text</font>
    <span id="firstSpan">First span text</span>
    <span id="secondSpan">Second span text</span>
</li>

6
Tôi không có quyền kiểm soát việc đưa văn bản miễn phí vào thẻ, vì mã tôi đang làm việc không phải do tôi tạo ra. Nếu tôi có thể lấy văn bản đó, tôi có thể xóa nó và thay thế nó bằng các thẻ xung quanh nó, hoặc làm bất cứ điều gì tôi muốn. Nhưng một lần nữa, html đã được viết sẵn.
MegaMatt

à, được rồi Sau đó, tôi nghĩ rằng bạn sẽ phải lọc kết quả: S xin lỗi.
Dorjan

-1

Bạn có thể thử cái này

alert(document.getElementById('listItem').firstChild.data)

-2

Sử dụng một điều kiện bổ sung để kiểm tra xem bên trongHTML và InternalText có giống nhau không. Chỉ trong những trường hợp, thay thế văn bản.

$(function() {
$('body *').each(function () {
    console.log($(this).html());
    console.log($(this).text());
    if($(this).text() === "Search" && $(this).html()===$(this).text())  {
        $(this).html("Find");
    }
})
})

http://jsfiddle.net/7RSGh/


-2

Để có thể cắt kết quả, hãy sử dụng DotNetWala như sau:

$("#foo")
    .clone()    //clone the element
    .children() //select all the children
    .remove()   //remove all the children
    .end()  //again go back to selected element
    .text()
    .trim();

Tôi phát hiện ra rằng việc sử dụng phiên bản ngắn hơn như document.getElementById("listItem").childNodes[0]sẽ không hoạt động với trim () của jQuery.


3
Đó là bởi vì document.getElementById("listItem").childNodes[0]javascript đơn giản, bạn phải bọc nó trong hàm jQuery$(document.getElementById("listItem").childNodes[0]).trim()
Red Taz

Được rồi có ý nghĩa. Haha. Cảm ơn!
Marion đi

1
Điều này gần giống với câu trả lời của DotNetWala . Tất cả bạn đã làm được thêm vào .trim()cuối. Câu trả lời này có cần thiết không?
Tất cả công nhân là cần thiết

-3

Tôi không phải là một chuyên gia jquery, nhưng làm thế nào về,

$('#listItem').children().first().text()

1
Nếu bạn lưu ý một chuyên gia jquery, vậy thì tại sao không trở thành một chuyên gia bằng cách đọc qua các câu trả lời khác trước? ... Một trong số chúng tình cờ giống như những gì bạn đã viết, với các bình luận bên dưới giải thích tại sao nó không một ý tưởng tốt
Oskar Holmkratz

-4

Điều này chưa được kiểm tra, nhưng tôi nghĩ bạn có thể thử một cái gì đó như thế này:

 $('#listItem').not('span').text();

http://api.jquery.com/not/


3
Bởi vì nó giống như $('#listItem').text() . #listItemkhông phải là <span>thêm not('span')không làm gì cả.
Thomas Higginbotham
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.