web-dev-qa-db-ja.com

contentEditable divからのテキストの抽出

私はdivをcontentEditableに設定し、「_white-space:pre_」でスタイル設定しているため、改行などが保持されます。 Safari、FF、IEでは、divの外観と動作はほぼ同じです。すべては順調です。私がしたいことは、このdivからテキストを抽出することですが、書式設定を失わないように、具体的には改行します。

私たちはjQueryを使用しています。そのtext()関数は基本的に事前注文DFSを実行し、DOMのそのブランチのすべてのコンテンツを1つの塊に接着します。これにより、フォーマットが失われます。

html()関数を確認しましたが、contentEditable divの背後で生成される実際のHTMLを使用すると、3つのブラウザーすべてが異なる動作をするようです。これを私の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を使用して見つかりました)。

改行を保持するような方法でcontentEditable divからテキストを抽出するJavaScriptコードやjQueryプラグイン、または何かを知っている人はいますか?私が解析ホイールを再発明したくない場合は、する必要はありません。

更新:jQuery 1.4.2のgetText関数をクリッブし、空白をほとんどそのままにして抽出するように変更しました(改行を追加する1行のみを変更しました)。

_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の実装が空白をつぶしてしまう可能性があります。これは本当ですか、それとも私は何か間違っていますか?

46
Shaggy Frog

ニコがそれに賞金をぶつけたときまで、私はこの質問を忘れていました。

私は自分で必要な関数を記述し、既存の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;
}
4
Shaggy Frog

残念ながら、ブラウザーごとに個別にpreケースの場合はこれを処理する必要があります(多くの場合、(browser検出を容認しません) 、feature検出を使用します...ただし、この場合は必要です)が、幸運にも、次のようにすべてをかなり簡潔に処理できます。

_var ce = $("<pre />").html($("#edit").html());
if($.browser.webkit) 
  ce.find("div").replaceWith(function() { return "\n" + this.innerHTML; });    
if($.browser.msie) 
  ce.find("p").replaceWith(function() { return this.innerHTML  +  "<br>"; });
if($.browser.mozilla || $.browser.opera ||$.browser.msie )
  ce.find("br").replaceWith("\n");

var textWithWhiteSpaceIntact = ce.text();
_

ここでテストできます 。 IE特にテキスト変換の_&nbsp;_と改行の方法が原因で面倒です。それが一貫性を持たせるために上記の_<br>_の扱いを受ける理由です。したがって、正しく処理するには2つのパスが必要です。

上記の_#edit_はcontentEditableコンポーネントのIDであるため、これを変更するか、これを関数にします。次に例を示します。

_function getContentEditableText(id) {
    var ce = $("<pre />").html($("#" + id).html());
    if ($.browser.webkit)
      ce.find("div").replaceWith(function() { return "\n" + this.innerHTML; });
    if ($.browser.msie)
      ce.find("p").replaceWith(function() { return this.innerHTML + "<br>"; });
    if ($.browser.mozilla || $.browser.opera || $.browser.msie)
      ce.find("br").replaceWith("\n");

    return ce.text();
}
_

ここでテストできます 。または、これはとにかくjQueryメソッドに基づいて構築されているため、次のようにプラグインにします。

_$.fn.getPreText = function () {
    var ce = $("<pre />").html(this.html());
    if ($.browser.webkit)
      ce.find("div").replaceWith(function() { return "\n" + this.innerHTML; });
    if ($.browser.msie)
      ce.find("p").replaceWith(function() { return this.innerHTML + "<br>"; });
    if ($.browser.mozilla || $.browser.opera || $.browser.msie)
      ce.find("br").replaceWith("\n");

    return ce.text();
};
_

次に、$("#edit").getPreText()で呼び出すことができます ここでそのバージョンをテストできます

36
Nick Craver

今日、Firefoxでこれを発見しました:

ホワイトスペースが「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.

問題があります。元のテキストの行の最後でEnterキーを押すと、LFを挿入する代わりに、「Â」が挿入されます。Enterキーをもう一度押すと、そこにLFが挿入されます、しかし初めてではありません。そして、「Â」を削除する必要があります(スペースのように見えます)。図に行く-それはバグだと思います。

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

ここでは目に見える以上に多くのことが起こっていると私は確信しているので、問題についてのどんな入力でも啓発されるでしょう。

<!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>
1
alfadog67

これを見てください フィドル

またはこの投稿

ブラウザ互換で編集可能なDIVのテキストを解析する方法

多くの努力の結果作成された...........

1
user10

これは、iOS Safari(iOS 7および8)、Safari 8、Chrome 43、およびOS XのFirefox 36、および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

本当の答えは、ブラウザが提供するマークアップに興味がない場合は、contenteditable属性を使用してはならないということですが、textareaが適切なツールになります。

0
Jon z