要素のテキストノードを取得する方法


98
<div class="title">
   I am text node
   <a class="edit">Edit</a>
</div>

「I am text node」を取得し、「edit」タグを削除したくなく、クロスブラウザーソリューションが必要です。


この質問は、stackoverflow.com / questions / 3172166 /…とほぼ同じです。Jamesの単純なJSバージョンの回答
Mala

回答:


79
var text = $(".title").contents().filter(function() {
  return this.nodeType == Node.TEXT_NODE;
}).text();

これはcontents選択された要素のを取得し、それにフィルター関数を適用します。filter関数は、テキストノード(つまり、を含むノードnodeType == Node.TEXT_NODE)のみを返します。


@Val-申し訳ありませんが、元のコードからそれを逃しました。回答を更新して表示します。この関数はノードのコンテンツではなくノード自体を返すtext()ため、必要filterです。
James Allardice、2011年

1
理由はわかりませんが、上記の理論をテストするときはうまくいきません。私は以下を実行し、 すべてのノードタイプに対して1jQuery("*").each(function() { console.log(this.nodeType); })を取得しました。
Batandwa 2014年

クリックされたノードでテキストを取得し、そのすべての子でテキストを取得することは可能ですか?
Jenna Kwon

これは興味深く、この問題を解決しますが、状況がより複雑になるとどうなりますか?仕事を終わらせるためのより柔軟な方法があります。
Anthony Rutledge、2018年

jQueryがない場合、document.querySelector( "。title")。childNodes [0] .nodeValue
Balaji Gunasekaran

53

最初のchildNodeのnodeValueを使用して取得できます

$('.title')[0].childNodes[0].nodeValue

http://jsfiddle.net/TU4FB/


4
これは機能しますが、子ノードの位置によって異なります。それが(いつ)変わると、壊れます。
Armstrongest

テキストノードが最初の子でない場合null、戻り値を取得することがあります。
Anthony Rutledge 2018年

14

要素の最初のテキストノードの値を取得する場合は、次のコードが機能します。

var oDiv = document.getElementById("MyDiv");
var firstText = "";
for (var i = 0; i < oDiv.childNodes.length; i++) {
    var curNode = oDiv.childNodes[i];
    if (curNode.nodeName === "#text") {
        firstText = curNode.nodeValue;
        break;
    }
}

あなたはここでこれを実際に見ることができます:http : //jsfiddle.net/ZkjZJ/


curNode.nodeType == 3代わりに使ってもいいと思いますnodeName
Nilloc

1
@Nillocたぶん、でも何が増えるの?
シャドウウィザードはあなたのための耳です

5
@ShadowWizard @Nillocの推奨される方法は、定数を使用することです... curNode.nodeType == Node.TEXT_NODE(数値比較は高速ですが、curNode.nodeType == 3は読み取れません-どのノードに番号3がありますか?)
mikep

@ShadowWizard Use curNode.NodeType === Node.TEXT_NODE。この比較は、未知の可能な反復のループ内で発生しています。2つの小さな数値を比較することは、さまざまな長さの文字列を比較するよりも優れています(時間とスペースに関する考慮事項)。この状況で正しい質問は、「ノードの種類/タイプは何ですか?」であり、「名前は何ですか?」ではありません。developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
Anthony Rutledge

2
@ShadowWizardまた、ループを使用してをふるいにかけるchildNodes場合は、要素ノードに複数のテキストノードを含めることができることを確認してください。一般的なソリューションでは、対象とする要素ノード内のテキストノードのインスタンスを指定する必要がある場合があります(1番目、2番目、3番目など)。
Anthony Rutledge 2018年

13

「複雑な」要素や深くネストされた要素に役立つ別のネイティブJSソリューションは、NodeIteratorを使用することです。プットNodeFilter.SHOW_TEXT第二引数(「whatToShow」)、および反復を超える要素の単なるテキストノードの子として。

var root = document.querySelector('p'),
    iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT),
    textnode;

// print all text nodes
while (textnode = iter.nextNode()) {
  console.log(textnode.textContent)
}
<p>
<br>some text<br>123
</p>

も使用できますTreeWalker。2つの違いNodeIteratorは、単純な線形反復子であり、TreeWalker兄弟や祖先を介してナビゲートすることもできます。


9

純粋なJavaScript:ミニマリスト

まず、DOMでテキストを検索するときは常にこのことを覚えておいてください。

MDN-DOMの空白

この問題は、XML / HTMLの構造に注意を向けさせるでしょう。

この純粋なJavaScriptの例では、他の種類のノードインターリーブできる複数のテキストノードの可能性を説明します。ただし、最初は空白の判断にパスせず、そのフィルタリングタスクは他のコードに任せています。

このバージョンNodeListでは、呼び出し/クライアントコードからaを渡します。

/**
* Gets strings from text nodes. Minimalist. Non-robust. Pre-test loop version.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @param nodeList The child nodes of a Node, as in node.childNodes.
* @param target A positive whole number >= 1
* @return String The text you targeted.
*/
function getText(nodeList, target)
{
    var trueTarget = target - 1,
        length = nodeList.length; // Because you may have many child nodes.

    for (var i = 0; i < length; i++) {
        if ((nodeList[i].nodeType === Node.TEXT_NODE) && (i === trueTarget)) {
            return nodeList[i].nodeValue;  // Done! No need to keep going.
        }
    }

    return null;
}

もちろん、node.hasChildNodes()最初にテストすることにより、事前テストforループを使用する必要はありません。

/**
* Gets strings from text nodes. Minimalist. Non-robust. Post-test loop version.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @param nodeList The child nodes of a Node, as in node.childNodes.
* @param target A positive whole number >= 1
* @return String The text you targeted.
*/
function getText(nodeList, target)
{
    var trueTarget = target - 1,
        length = nodeList.length,
        i = 0;

    do {
        if ((nodeList[i].nodeType === Node.TEXT_NODE) && (i === trueTarget)) {
            return nodeList[i].nodeValue;  // Done! No need to keep going.
         }

        i++;
    } while (i < length);

    return null;
}

純粋なJavaScript:堅牢

ここでは、関数getTextById()は2つのヘルパー関数を使用しています:getStringsFromChildren()およびfilterWhitespaceLines()


getStringsFromChildren()

/**
* Collects strings from child text nodes.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @version 7.0
* @param parentNode An instance of the Node interface, such as an Element. object.
* @return Array of strings, or null.
* @throws TypeError if the parentNode is not a Node object.
*/
function getStringsFromChildren(parentNode)
{
    var strings = [],
        nodeList,
        length,
        i = 0;

    if (!parentNode instanceof Node) {
        throw new TypeError("The parentNode parameter expects an instance of a Node.");
    }

    if (!parentNode.hasChildNodes()) {
        return null; // We are done. Node may resemble <element></element>
    }

    nodeList = parentNode.childNodes;
    length = nodeList.length;

    do {
        if ((nodeList[i].nodeType === Node.TEXT_NODE)) {
            strings.push(nodeList[i].nodeValue);
         }

        i++;
    } while (i < length);

    if (strings.length > 0) {
        return strings;
    }

    return null;
}

filterWhitespaceLines()

/**
* Filters an array of strings to remove whitespace lines.
* Generic, cross platform solution.
*
* @author Anthony Rutledge
* @version 6.0
* @param textArray a String associated with the id attribute of an Element.
* @return Array of strings that are not lines of whitespace, or null.
* @throws TypeError if the textArray param is not of type Array.
*/
function filterWhitespaceLines(textArray) 
{
    var filteredArray = [],
        whitespaceLine = /(?:^\s+$)/; // Non-capturing Regular Expression.

    if (!textArray instanceof Array) {
        throw new TypeError("The textArray parameter expects an instance of a Array.");
    }

    for (var i = 0; i < textArray.length; i++) {
        if (!whitespaceLine.test(textArray[i])) {  // If it is not a line of whitespace.
            filteredArray.push(textArray[i].trim());  // Trimming here is fine. 
        }
    }

    if (filteredArray.length > 0) {
        return filteredArray ; // Leave selecting and joining strings for a specific implementation. 
    }

    return null; // No text to return.
}

getTextById()

/**
* Gets strings from text nodes. Robust.
* Generic, cross platform solution.
*
* @author Anthony Rutledge
* @version 6.0
* @param id A String associated with the id property of an Element.
* @return Array of strings, or null.
* @throws TypeError if the id param is not of type String.
* @throws TypeError if the id param cannot be used to find a node by id.
*/
function getTextById(id) 
{
    var textArray = null;             // The hopeful output.
    var idDatatype = typeof id;       // Only used in an TypeError message.
    var node;                         // The parent node being examined.

    try {
        if (idDatatype !== "string") {
            throw new TypeError("The id argument must be of type String! Got " + idDatatype);
        }

        node = document.getElementById(id);

        if (node === null) {
            throw new TypeError("No element found with the id: " + id);
        }

        textArray = getStringsFromChildren(node);

        if (textArray === null) {
            return null; // No text nodes found. Example: <element></element>
        }

        textArray = filterWhitespaceLines(textArray);

        if (textArray.length > 0) {
            return textArray; // Leave selecting and joining strings for a specific implementation. 
        }
    } catch (e) {
        console.log(e.message);
    }

    return null; // No text to return.
}

次に、戻り値(配列、またはnull)が処理されるクライアントコードに送信されます。うまくいけば、配列には空白行ではなく、実際のテキストの文字列要素が必要です。

空の文字列は("")されていない、あなたが適切に有効なテキストの存在を示すために、テキストノードを必要とするので返さ。("")を返すと、テキストノードが存在するという誤った印象を与え、誰かがの値を変更することによってテキストを変更できると思い込む可能性があります.nodeValue。空の文字列の場合、テキストノードは存在しないため、これは誤りです。

例1

<p id="bio"></p> <!-- There is no text node here. Return null. -->

例2

<p id="bio">

</p> <!-- There are at least two text nodes ("\n"), here. -->

この問題は、HTMLを間隔を空けて読みやすくする場合に発生します。これで、人間が読み取れる有効なテキストはあり"\n"ませんが、.nodeValueプロパティに改行()文字を含むテキストノードが残っています。

人間は、例1と2を機能的に同等であると見なします。空の要素が満たされるのを待っています。DOMは人間の推論とは異なります。これが、getStringsFromChildren()関数がテキストノードが存在するかどうかを判断し、.nodeValue値を配列に収集する必要がある理由です。

for (var i = 0; i < length; i++) {
    if (nodeList[i].nodeType === Node.TEXT_NODE) {
            textNodes.push(nodeList[i].nodeValue);
    }
}

例2では、​​2つのテキストノードが存在getStringFromChildren().nodeValue、両方のを返します("\n")。ただし、filterWhitespaceLines()正規表現を使用して、純粋な空白文字の行を除外します。

null改行("\n")文字の代わりに返すことは、クライアント/呼び出しコードに嘘をついている形ですか?人間の言葉では、いいえ。DOM用語では、はい。ただし、ここでの問題は、編集でなくテキストを取得することです。呼び出し元のコードに戻る人間のテキストはありません。

誰かのHTMLにいくつの改行文字が現れるかは決してわかりません。「2番目の」改行文字を探すカウンターの作成は信頼できません。存在しない可能性があります。

もちろん、さらに下の行で、余分な空白を含む空の要素のテキスト編集する問題<p></p>(例2)は、要素が正確に何を含んでいるかを確認するために、段落のタグ間の1つを除くすべてのテキストノードを破棄(おそらくスキップ)することを意味します表示することになっています。

とにかく、あなたが特別なことをしている場合を除いて、どのテキストノードの.nodeValueプロパティがあなたが編集したい真の人間が読めるテキストを持っているかを決定する方法が必要になります。filterWhitespaceLinesそこまでの道のりです。

var whitespaceLine = /(?:^\s+$)/; // Non-capturing Regular Expression.

for (var i = 0; i < filteredTextArray.length; i++) {
    if (!whitespaceLine.test(textArray[i])) {  // If it is not a line of whitespace.
        filteredTextArray.push(textArray[i].trim());  // Trimming here is fine. 
    }
}

この時点で、次のような出力が表示されることがあります。

["Dealing with text nodes is fun.", "Some people just use jQuery."]

これら2つの文字列がDOM内で互いに隣接している保証はありません。そのため、それらを結合すると.join()不自然な合成になる可能性があります。代わりに、を呼び出すコードでgetTextById()、操作する文字列を選択する必要があります。

出力をテストします。

try {
    var strings = getTextById("bio");

    if (strings === null) {
        // Do something.
    } else if (strings.length === 1) {
        // Do something with strings[0]
    } else { // Could be another else if
        // Do something. It all depends on the context.
    }
} catch (e) {
    console.log(e.message);
}

.trim()内部にgetStringsFromChildren()先頭と末尾の空白を取り除くために(または一連のスペースを長さ0の文字列("")に変換するために)追加できますが、どのようにしてすべてのアプリケーションがテキスト(文字列)に発生する必要があるかを事前に知ることができます見つかったら?わからないので、特定の実装に任せてgetStringsFromChildren()、汎用的にしましょう。

このレベルの特異性(targetなど)が不要な場合があります。すばらしい。そのような場合は、シンプルなソリューションを使用してください。ただし、一般化されたアルゴリズムを使用すると、単純な状況と複雑な状況に対応できます。


8

最初の#textノードのコンテンツを返すES6バージョン

const extract = (node) => {
  const text = [...node.childNodes].find(child => child.nodeType === Node.TEXT_NODE);
  return text && text.textContent.trim();
}

効率と柔軟性について疑問に思っています。(1)を使用.from()して、浅いコピーの配列インスタンスを作成する。(2)を使用して、.find()を使用して文字列比較を行う.nodeName。使用node.NodeType === Node.TEXT_NODEする方が良いでしょう。(3)値がない場合に空の文字列を返すは、テキストノードが見つからnullない場合に真になります。テキストノードが見つからない場合は、作成する必要があります。空の文字列を返すと、テキストノードが存在し、正常に操作できるという誤った印象を与える可能性があります。本質的に、空の文字列を返すことは白い嘘であり、回避するのが最善です。""
Anthony Rutledge

(4)nodeListに複数のテキストノードがある場合、ここでどのテキストノードを指定するかは指定できません。あなたはしたいことがあり最初のテキストノードを、しかし、あなたは非常によくしたいことがあり、最後のテキストノードを。
アンソニー・ラトリッジ

Array.fromを置き換えるために何を提案しますか?
18年

@スノーマンは、このような実質的な変更について独自の回答を追加するか、OPにそれらを回答に組み込む機会を与えるための推奨事項を作成してください。
TylerH

@jujule- HTMLCollectionを配列[...node.childNodes]に変換するために使用する方が良い
vsync

5

.text() - for jquery

$('.title').clone()    //clone the element
.children() //select all the children
.remove()   //remove all the children
.end()  //again go back to selected element
.text();    //get the text of element

1
標準のJavaScriptのメソッドは「innerText」である必要があると思います
Reporter

2
これはOPが望む方法では機能しません- a要素内のテキストも取得します:jsfiddle.net/ekHJH
James Allardice

1
@James Allardiceは-私は.................今、これは動作しますjqueryの溶液を用いて行われています
Pranayラナを

それは、ほぼ動作しますが、あなたが不足している.、あなたのセレクタの開始時に、あなたが実際のテキストを取得する意味titleを持つ要素ではなく、要素をclass="title"
ジェームズAllardice

@reporter .innerTextは、最近採用された古いIE規約です。標準のDOMスクリプティングに関してnode.nodeValueは、テキストノードのテキストを取得する方法です。
Anthony Rutledge

2

これは空白も無視するため、コアJavascriptを使用して空のtextNodes..codeを取得することはありません。

var oDiv = document.getElementById("MyDiv");
var firstText = "";
for (var i = 0; i < oDiv.childNodes.length; i++) {
    var curNode = oDiv.childNodes[i];
    whitespace = /^\s*$/;
    if (curNode.nodeName === "#text" && !(whitespace.test(curNode.nodeValue))) {
        firstText = curNode.nodeValue;
        break;
    }
}

jsfiddleでそれを確認します- http://jsfiddle.net/webx/ZhLep/


curNode.nodeType === Node.TEXT_NODE方が良いだろう。ループ内で文字列比較と正規表現を使用することは、特に規模がoDiv.childNodes.length大きくなるため、パフォーマンスの低いソリューションです。このアルゴリズムはOPの特定の問題を解決しますが、恐ろしいパフォーマンスコストがかかる可能性があります。テキストノードの配置または数が変更された場合、このソリューションが正確な出力を返すことは保証できません。つまり、希望する正確なテキストノードをターゲットにすることはできません。あなたにはそこにテキストのHTML構造の慈悲と配置である。
アンソニー・ラトリッジ

1

XPathのtext()ノードテストを使用して、テキストノードのみを取得することもできます。例えば

var target = document.querySelector('div.title');
var iter = document.evaluate('text()', target, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
var node;
var want = '';

while (node = iter.iterateNext()) {
    want += node.data;
}

0

これは、すべての子ノード(再帰的)の連結されたテキストを制限する文字列作成する ES6での私のソリューションです。子ノードのshdowrootにもアクセスすることに注意してください。

function text_from(node) {
    const extract = (node) => [...node.childNodes].reduce(
        (acc, childnode) => [
            ...acc,
            childnode.nodeType === Node.TEXT_NODE ? childnode.textContent.trim() : '',
            ...extract(childnode),
            ...(childnode.shadowRoot ? extract(childnode.shadowRoot) : [])],
        []);

    return extract(node).filter(text => text.length).join('\n');
}

このソリューションは、https://stackoverflow.com/a/41051238./1300775のソリューションに触発されました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.