.text()を使用して子タグにネストされていないテキストのみを取得する


386

私がこのようなhtmlを持っている場合:

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

.text()「This is some text」という文字列だけを取得しようとしていますが、$('#list-item').text()「This is some textFirst span textSecond span text」というメッセージが表示されます。

.text("")子タグ内のテキストではなく、タグ内のフリーテキストのみを取得する(そして、場合によっては削除するなど)方法はありますか?

HTMLは私が作成したものではないため、これを使用して作業する必要があります。HTMLを書き込むときに、テキストをタグで囲むだけで簡単であることはわかっていますが、HTMLは事前に作成されています。


私はコメントにはまだ十分な評判を持っていないと私は知識が失われることを望むものではないので組み合わせ、(うまくいけば、それは他の誰かに役立ちます)macio.Jun「答え、正規表現、およびiStranger」答えはするHTMLとtextNodeを交換してくださいJavaScriptで?文字列のテキストのみのノードを検索し、すべての出現箇所をリンクで置き換えることができました。
JDQ

回答:


509

親要素内のテキストのみを取得するために、ここにあるclone()メソッドに基づくこの再利用可能な実装が好きでした。

簡単に参照できるように提供されたコード:

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

5
このソリューションでは、子なしでテキストのみを取得できますが、テキストのみを置き換えることはできません。
BenRoe、2012年

1
私は1つのことを取得しません:.end()が選択した要素に戻る場合、text()は子要素を含む元のテキストをコピーする必要があります。しかし実際には、操作されたクローンのテキストがコピーされているのがわかります。end()はclone()に戻りますか?

68
これは、これを行うのに非常に非効率的な方法です
ビリョネカン2014

5
@billyonecan、より効率的な方法を提案できますか?これは「クリーン」で「短い」ので魅力的です。何を指示してるんですか?
derekmx271


364

簡単な答え:

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

38
効率的な回答(無関係なデータ構造を生成しない)が、それほど怖くないように見える回答ほど投票されない理由がわかりません。できれば+5。
Steven Lu

16
シンプルで効率的な答え
Paul Carroll

9
これはより効率的であるだけでなく、正確でもあります。このソリューションは、テキストが子要素間に散らばっている状況に対応します。+5
キリルテナンバウム14

15
より明確にするために、IE8 +を使用している場合は、のthis.nodeType == Node.TEXT_NODE代わりに使用できますthis.nodeType == 3。IMOを読みやすく理解しやすくなります。
NorTicUs 2014年

8
これは、テキストのないものに使用すると壊れます。これを関数として使用していて、テキストがある場合とない場合のシナリオがある場合は、.contents().filter(...)呼び出しをローカル変数に取り込み、その長さを確認するだけです。たとえば、 var text = $(this).contents().filter(...); if (text.length) { return text[0].nodeValue; } return "";
Carl Bussema

157

これはjqueryを使いすぎた場合のようです。以下は他のノードを無視してテキストを取得します:

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

あなたはそれをトリミングする必要がありますが、それはあなたが望むものを一つの簡単なラインで取得します。

編集

上記はテキストノードを取得します。実際のテキストを取得するには、これを使用します。

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

31
最良の答えは、このためのプラグインや、10個のjQuery呼び出しのチェーンを必要としないことです。$('.foo')[0].childNodes[0].nodeValue.trim()
2013年

5
テキストコンテンツが複数のノードに分割されている場合(crlf、text、crlfのシーケンスなど)uaによって構築されたdomが最も単純な構造を使用することの保証(rael-life)はありますか?
collapsar 2013年

5
完全に最良の答え...なぜ他の人が時々jQueryを使いすぎるのですか?
ncubica 2014年

11
これは、<div id = "listItem">必要なテキスト<span>その他</ span> </ div>の場合にのみ機能します。<div id = "listItem"> <span>その他</ span>必要なテキスト</ div>に対しては機能しません
スペンサー

1
時々あなたは持っていませんdocument。を使用してここに来ましたcheerio
フラッシュ

67

より簡単かつ迅速に:

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

このクロスブラウザは互換性がありますか?
Rajat Gupta、2014

もちろん、インデックスで指定されたjQueryオブジェクトに一致する要素の1つであるJquery Docs .get()を取得します。
WakeupMorning 2014

1
@Nate <br/>タグで使用する必要がある場合は、macio.Junの回答を使用できます。
WakeupMorning、2014

これは受け入れられる答えになるはずです。
ダニー

2
なぜget(0)単にではなく[0]
Clonkex

28

受け入れられた回答に似ていますが、クローンはありません:

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

そして、これがこの目的のためのjQueryプラグインです:

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

このプラグインの使用方法は次のとおりです。

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

t.children()のtとは何ですか?
FrEaKmAn 2016年

これは、pbjkがJan'15に書いたものの複製ソリューションです...それにもかかわらず、見栄えが良いです。
Oskar Holmkratz 2016

1
いいえ、@オスカー。.contents()一部はここに非常に重要です!
DUzun

ノードがIDを使用しない場合の悪い解決策。
AndroidDev 2017

3
@AndroidDevセレクターは常に、機能するものに置き換えることができます。これはテクニックを説明するためのものです!プラグインバージョンを追加して、IDがなくても機能することを示しました
DUzun

8

コードではありません:

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

jQueryのためにjQueryになるだけですか?単純な操作に多数のチェーンされたコマンドとその多くの(不要な)処理が含まれる場合は、おそらく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));

呼び出す:

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

引数は、別のシナリオが発生した場合などにあります。

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

テキストには値があります:

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

1
いいね。これをjQueryの次のバージョンのプルリクエストにしてみませんか?
Jared Tomaszewski、2014

8

これを試して:

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

6

それはあなたが提示された構造に依存するニーズに合わせたものである必要があります。あなたが提供した例では、これはうまくいきます:

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

デモ:http : //jquery.nodnod.net/cases/2385/run

しかし、それはあなたが投稿したものに似ているマークアップにかなり依存しています。


2
将来の読者は注意してください:この回答のコードは実際の要素の子を殺します。clone意図した効果でない場合は、ここでメソッドを使用する必要があります。
Mahn

以下の@DotNetWalaの回答。この回答の代わりに使用する必要があります。または、少なくとも、.detach()ではなくメソッドを使用し.remove()ます。
Don McCurdy 2013年


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

1
このコードスニペット、ありがとうございます。適切な説明は、なぜこれが問題の優れた解決策であるを示すことによって教育的価値を大幅に改善し、同様の、しかし同一ではない質問を持つ将来の読者にとってより有用になります。回答を編集して説明を追加し、適用される制限と前提を示してください。
Toby Speight 2017

3

これは古い質問ですが、トップの回答は非常に非効率的です。これはより良い解決策です:

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

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

    return str;
};

そして、これをしてください:

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

3

選択した要素の直接の子であるすべてのテキストノードのコンテンツを取得する場合も、これはすばらしい解決策になると思います。

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

注:jQueryドキュメントでは、コンテンツ機能を説明するために同様のコードを使用しています:https : //api.jquery.com/contents/

PSそれを行うには少し醜い方法もありますが、これは物事がどのように機能するかをより深く示し、テキストノード間のカスタムセパレーターを可能にします(多分そこで改行が必要かもしれません)

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

1

createTreeWalkerを使用して、html要素にアタッチされていないすべてのテキスト要素を検索することをお勧めします(この関数を使用して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

indexテキストノードの位置が兄弟間で固定されている場合は、

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

1

カバーする必要のある柔軟性やケースの数はわかりませんが、例として、テキストが常に最初のHTMLタグの前にある場合は、最初のタグで内部のhtmlを分割して、前者を取得するのはどうでしょうか。

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

必要に応じてもっと広くする必要があるかもしれません

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

そして、2つのマーカーの間のテキストが必要な場合(ある事後の後など)、null refエラーを回避しながら、(テストされていない)のようなことを行い、ifステートメントを使用して、開始マーカーまたは終了マーカーまたはその両方を持つのに十分な柔軟性を持たせることができます:

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...

私は通常、このような便利なことのためにユーティリティ関数を作成し、エラーをなくしてから、このタイプの文字列操作を常に書き換えたり、null参照を危険にさらしたりするのではなく、一度確実にそれらに依存します。このようにして、関数を再利用できます。多くのプロジェクトで、文字列参照に未定義の参照エラーがある理由をデバッグするために時間を無駄にする必要はありません。これまでで最短の1行のコードではないかもしれませんが、ユーティリティ関数を使用した後は、それ以降は1行になります。ほとんどのコードは、エラーを回避するために存在するパラメータを処理するだけであることに注意してください。

例えば:

/**
* 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)

テキストを置き換える必要がある場合は、単にを使用してください$('#listItem').html( newHTML )。ここで、newHTMLは、既にテキストが削除された変数です。
OG Sean


0

私は、クローンのクローン作成と変更よりもはるかに効率的な特定のソリューションを思いつきました。このソリューションは、次の2つの予約でのみ機能しますが、現在受け入れられているソリューションよりも効率的です。

  1. あなただけのテキストを取得しています
  2. 抽出するテキストが子要素の前にあります

そうは言っても、ここにコードがあります:

// '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

ただ、質問のように、私は、テキストの一部の正規表現置換を行うために抽出したテキストにしようとしていたが、問題を得ていたところ、私の内側の要素(すなわち:<i><div><span>、など)も削除されたばかりの。

次のコードはうまく機能し、私の問題をすべて解決したようです。

ここではいくつかの回答を使用していますが、特に、要素がの場合にのみテキストを置き換えます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;
  }
});

上記のことは、与えられたすべての要素をループ処理することですel(これは単にで取得されました$("div.my-class[name='some-name']");。各内部要素について、基本的にそれらを無視します。テキストの各部分(if (this.nodeType === 3))については、これらの要素にのみ正規表現置換を適用します。

this.textContent = text部分は、単に私の場合、私のようなトークンを探していたの置換テキストを置き換え[[min.val]][[max.val]]など、

この短いコードの抜粋は、質問が尋ねていることを実行しようとしている人を助けます...そしてもう少し。


-1

それを<p>またはに入れて、<font>その$( '#listItem font')。text()を取得します

最初に頭に浮かんだこと

<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
私が作業しているコードは私が作成したものではないため、タグにフリーテキストを入れることはできません。そのテキストだけを取得できた場合、それを削除して、その周りのタグに置き換えるか、または好きなことを行うことができます。しかし、ここでも、HTMLはすでに事前に記述されています。
MegaMatt

あ、そう。次に、結果をフィルタリングする必要があると思います:S申し訳ありません。
ドルジャン


-2

追加の条件を使用して、innerHTMLとinnerTextが同じかどうかを確認します。それらの場合にのみ、テキストを置き換えます。

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

結果をトリミングできるようにするには、DotNetWalaを次のように使用します。

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

などの短いバージョンを使用するdocument.getElementById("listItem").childNodes[0]と、jQueryのtrim()では機能しないことがわかりました。


3
それdocument.getElementById("listItem").childNodes[0]はプレーンなJavaScript なので、jQuery関数でラップする必要があるためです$(document.getElementById("listItem").childNodes[0]).trim()
Red Taz

わかりました。はは。ありがとう!
Marion Go

1
これはDotNetWalaの回答とほぼ同じです。あなたがしたすべて.trim()は最後に追加されました。この答えは必要ですか?
すべての労働者は必須

-3

私はjqueryのエキスパートではありませんが、どうですか、

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

1
jqueryのエキスパートである場合は、最初に他の回答を読んでエキスパートにならないのですか?...そのうちの1つは、あなたが書いたものと事実上同じであり、その理由を説明する以下のコメントが付いています良い考えです。
Oskar Holmkratz 2016

-4

これはテストされていませんが、次のようなことを試すことができると思います:

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

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


3
と同じですから$('#listItem').text()#listItemはそうではない<span>ので、追加not('span')しても何も起こりません。
トーマスヒギン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.