javascript 文字 コンテンツからテキストを抽出編集可能な div



javascript テキスト 取得 (5)

私は今までこの質問を忘れていました。ニコがそれに恩恵をかけた時です。

私は自分自身が必要とする関数を記述し、既存のjQueryコードベースから関数をクローブし、必要に応じて機能するように修正することで、この問題を解決しました。

私はSafari(WebKit)、IE、Firefox、Operaでこの機能をテストしました。 私はcontentEditable全体が非標準であるので、他のブラウザーをチェックするのは面倒ではありませんでした。 contentEditableの実装方法が変更された場合、ブラウザの更新によってこの関数が中断される可能性もあります。 したがって、プログラマは注意してください。

function extractTextWithWhitespace(elems)
{
    var lineBreakNodeName = "BR"; // Use <br> as a default
    if ($.browser.webkit)
    {
        lineBreakNodeName = "DIV";
    }
    else if ($.browser.msie)
    {
        lineBreakNodeName = "P";
    }
    else if ($.browser.mozilla)
    {
        lineBreakNodeName = "BR";
    }
    else if ($.browser.opera)
    {
        lineBreakNodeName = "P";
    }
    var extractedText = extractTextWithWhitespaceWorker(elems, lineBreakNodeName);

    return extractedText;
}

// Cribbed from jQuery 1.4.2 (getText) and modified to retain whitespace
function extractTextWithWhitespaceWorker(elems, lineBreakNodeName)
{
    var ret = "";
    var elem;

    for (var i = 0; elems[i]; i++)
    {
        elem = elems[i];

        if (elem.nodeType === 3     // text node
            || elem.nodeType === 4) // CDATA node
        {
            ret += elem.nodeValue;
        }

        if (elem.nodeName === lineBreakNodeName)
        {
            ret += "\n";
        }

        if (elem.nodeType !== 8) // comment node
        {
            ret += extractTextWithWhitespace(elem.childNodes, lineBreakNodeName);
        }
    }

    return ret;
}

私はcontentEditable divを設定し、 " white-space:pre "でスタイルを設定しているcontentEditablecontentEditableなります。 Safari、FF、IEでは、divはほとんど同じように見え、同じように動作します。 すべては順調です。 私がやりたいことは、このdivのテキストを抽出することですが、書式設定を失わないような方法、具体的には改行を行うことです。

text()関数は基本的に事前注文DFSを行い、DOMのそのブランチにあるすべてのコンテンツをまとめて1つのまとまりにするjQueryを使用しています。 これは書式設定を失います。

私はhtml()関数を見ましたが、3つのブラウザすべてがcontentEditable div内のシーンの裏に生成される実際のHTMLとは異なることをしているようです。 私はこれを私のdivに入力すると仮定します:

1
2
3

結果は次のとおりです。

Safari 4:

1
<div>2</div>
<div>3</div>

Firefox 3.6:

1
<br _moz_dirty="">
2
<br _moz_dirty="">
3
<br _moz_dirty="">
<br _moz_dirty="" type="_moz">

IE 8:

<P>1</P><P>2</P><P>3</P>

ああ。 ここで一貫しているものはありません。 驚くべきことは、MSIEが最も正気に見えるということです! (大文字のPタグとすべて)

divはCSSを使って行われるスタイル設定(フォントの面、色、サイズ、配置)が動的に設定されるため、 preタグ(Googleを使用して見つけたページの一部)を使用できるかどうかはわかりません。

誰かがJavaScriptコードやjQueryプラグイン、あるいは改行を保存するような方法でcontentEditable divからテキストを抽出するものを知っていますか? 私がする必要がなければ、私は構文解析ホイールを再開発しないことを好むだろう。

更新:私はjQuery 1.4.2からgetText関数をgetTextにし、空白をほとんど無傷で抽出するように修正しました(私は、改行を追加する行を1つだけchnagedしました)。

function extractTextWithWhitespace( elems ) {
    var ret = "", elem;

    for ( var i = 0; elems[i]; i++ ) {
        elem = elems[i];

        // Get the text from text nodes and CDATA nodes
        if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
            ret += elem.nodeValue + "\n";

        // Traverse everything else, except comment nodes
        } else if ( elem.nodeType !== 8 ) {
            ret += extractTextWithWhitespace2( elem.childNodes );
        }
    }

    return ret;
}

私はこの関数を呼び出し、その出力をjQueryなどのXMLノードに割り当てます。

var extractedText = extractTextWithWhitespace($(this));
var $someXmlNode = $('<someXmlNode/>');
$someXmlNode.text(extractedText);

生成されたXMLは、最終的にAJAX呼び出しを介してサーバーに送信されます。

これはSafariとFirefoxでうまくいきます。

IEでは、最初の '\ n'だけが何とか保持されているようです。 それをもっと見ると、jQueryはテキストをそういうものに設定しているようです(jQuery-1.4.2.jsの4004行目):

return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );

createTextNode読んで、IEの実装が空白をマッシュアップするようです。 これは本当ですか、私は何か間違っていますか?


Answer #1

Firefoxでこれを発見しました:

私はwhite-spaceが "pre"に設定されているcontenteditable divをこの関数に渡し、それは急激に機能します。

いくつのノードがあるかを示す行を追加し、出力を別のPREに配置するボタンを追加して、改行が完全であることを証明しました。

それは基本的にこう言っている:

For each child node of the DIV,
   if it contains the 'data' property,
      add the data value to the output
   otherwise
      add an LF (or a CRLF for Windows)
}
and return the result.

問題がある、tho。 元のテキストの任意の行の最後にEnterキーを押すと、LFを挿入する代わりに「Â」が挿入されます。再度Enterキーを押すとLFが挿入されますが、最初は挿入されません。 そして、あなたは "Â"(それはスペースのように見える)を削除する必要があります。 Go figure - 私はそれがバグだと思う。

これはIE8では発生しません。 (textContentからinnerTextに変更)そこには別のバグがあります。 Enterキーを押すと、Firefoxと同様にノードが2つのノードに分割されますが、各ノードの「データ」プロパティは「未定義」になります。

私は、目に会うよりもはるかに多くのことがここにあると確信しているので、その問題に関するあらゆるインプットは啓発されるでしょう。

<!DOCTYPE html>
<html>
<HEAD>
<SCRIPT type="text/javascript">
    function htmlToText(elem) {
        var outText="";
        for(var x=0; x<elem.childNodes.length; x++){
            if(elem.childNodes[x].data){
                outText+=elem.childNodes[x].data;
            }else{
                outText+="\n";
            }
        }
        alert(elem.childNodes.length + " Nodes: \r\n\r\n" + outText);
        return(outText);
    }
</SCRIPT>
</HEAD>
<body>

<div style="white-space:pre;" contenteditable=true id=test>Text in a pre element
is displayed in a fixed-width
font, and it preserves
both      spaces and
line breaks
</DIV>
<INPUT type=button value="submit" onclick="document.getElementById('test2').textContent=htmlToText(document.getElementById('test'))">
<PRE id=test2>
</PRE>
</body>
</html>


Answer #3

iOS Safari(iOS 7,8)、Safari 8、Chrome 43、OS XのFirefox 36、WindowsのIE6-11で動作するように見える(アンダースコアとjqueryを使用する)

_.reduce($editable.contents(), function(text, node) {
    return text + (node.nodeValue || '\n' +
        (_.isString(node.textContent) ? node.textContent : node.innerHTML));
}, '')

テストページはこちら: http://brokendisk.com/code/contenteditable.html : http://brokendisk.com/code/contenteditable.html

実際の答えは、ブラウザが提供するマークアップに興味がない場合は、 contenteditable属性を使用すべきではないということです。テキストエリアが適切なツールになります。


Answer #4
this.editableVal = function(cont, opts) 
{
  if (!cont) return '';
  var el = cont.firstChild;
  var v = '';
  var contTag = new RegExp('^(DIV|P|LI|OL|TR|TD|BLOCKQUOTE)$');
  while (el) {
    switch (el.nodeType) {
      case 3:
        var str = el.data.replace(/^\n|\n$/g, ' ').replace(/[\n\xa0]/g, ' ').replace(/[ ]+/g, ' ');
        v += str;
        break;
      case 1:
        var str = this.editableVal(el);
        if (el.tagName && el.tagName.match(contTag) && str) {
          if (str.substr(-1) != '\n') {
            str += '\n';
          }

          var prev = el.previousSibling;
          while (prev && prev.nodeType == 3 && PHP.trim(prev.nodeValue) == '') {
            prev = prev.previousSibling;
          }
          if (prev && !(prev.tagName && (prev.tagName.match(contTag) || prev.tagName == 'BR'))) {
            str = '\n' + str;
          }

        }else if (el.tagName == 'BR') {
          str += '\n';
        }
        v += str;
        break;
    }
    el = el.nextSibling;
  }
  return v;
}




contenteditable