DOMおれおれAdvent Calendar 2015 – 09日目

2015-12-09

テキストノートはあくまでノードなので、文字列ではありません。 toString() も特に用意されてないみたいです。

文字列を取得するにはテキストノードの nodeValue を見ます。

var elTitle = document.querySelector('title');
var nText = elTitle.firstChild;
var text = nText.nodeValue;
console.log(text);  // => "Ginpen.com"

任意の要素内の文字列を全部取得するなら

要素ノードの textContent を使います。ノードを再帰的に検索して、全ての文字列をかき集めてくれます。(正確には要素ノード以外でも使えるけど、まあ要素ノードで使うよね。)

取得だけじゃなくて設定もできるので、ユーザー入力値を画面に表示する場合は innerHTML じゃなくてこっち使った方が良いですね。

問題点

問題は文字列なら何でも持ってきてくれちゃうところです。 <script> だろうが <style> だろうがお構いなし。 display:none を指定してある要素以下の文字列も持ってきます。jQueryも textContent を使ってるので同じ。

自力で見えない要素を無視して文字列取得

こんな感じではいかがでしょうか。

/**
 * 要素内の文字列を返す。
 * @param {Element} el
 * @returns {String}
 */
function getText(el) {
  var text = '';

  // 子ノードを再帰的に全検索
  var children = el.childNodes;
  for (var i=0; i<children.length; i++) {
    var child = children[i];
    var childText;

    // テキストノードなら、文字列を取得
    if (child.nodeType === child.TEXT_NODE) {
      childText = String.trim(child.nodeValue);  // インデント等を除去
    }
    // 非表示でない要素ノードなら、子孫の文字列を取得
    else if (child.nodeType === child.ELEMENT_NODE) {
      var display = getComputedStyle(child).display;
      if (display !== 'none') {
        childText = getText(child);

        // インラインでなければ改行を追加
        if (childText && display !== 'inline' && display !== 'inline-block') {
          childText += '\n';
        }
      }
    }

    text += childText;
  }

  return text;
}

からの、こう。

var text = getText(document.querySelector('title'));

<iframe> を無視するとかが必要かも。まあいいか。

getComputedStyle() は要素に実際当たっているスタイルを得る仕組みです。最終的に有効なスタイルの位置が要素の style 属性でもCSSのセレクターでも大丈夫。ただこの関数、パフォーマンス的に問題になりやすいので、多用するとよくないかもです。 textContent が非表示要素以下を無視しないのもたぶんこのせい。

環境

textContentgetComputedStyle 共にIEは9+、Androidは問題なさそう。

nodeValue は一通りで使えます。

参考