web-dev-qa-db-ja.com

Chromeでコンテンツ編集可能なカーソルが最後までジャンプするのはなぜですか?

シナリオ

Contenteditable <div>領域があり、この領域内にテキストを含む<span contenteditable="false"></span>がある場合があります。これらのスパン要素は、編集できないスタイル付きテキストを表しますが、バックスペースキーを押すことで<div contenteditable="true"></div>領域から削除できます。

問題

カーソルの配置がここでの大きな問題です。これらの<span>要素のいずれかを削除すると、カーソルは<div>の末尾にジャンプします。さらに興味深いことに、カーソルが「最後」にあるときにテキストを入力すると、テキストの配置は問題ありません...次に、新しく入力したテキストを削除すると、カーソルが元に戻ります。

これを実証するフィドルを用意しました。 これはChromeでのみ機能する必要があり、他のブラウザは今のところ問題がないか、回避策があります。 (また、準備されたFiddleは、これをChromeでのみ示すように作成されていることに注意してください)

フィドル


フィドル命令:Chromeバージョン39.0.2171.95m(64ビット)32ビットで再現同じように

  1. <div>エリアをクリックします
  2. 「123」と入力します
  3. バックスペース "3"バックスペース "2"バックスペース "1" enter image description here

関連詳細

これを広範囲に調査して、似たようなさまざまなSOの質問に出くわしましたが、関連するソリューションを借りることが、私が求めている特効薬であるとは証明されていません。また、Chromeプロジェクトで、上記の問題をターゲットにしているように見える問題を見つけました(おそらく正確な方法ではありません)。以下で確認できます。

  1. 問題384357:編集不可能なノードがあるコンテンツ編集可能領域内のキャレット位置
  2. 問題385003:コンテンツ編集可能な要素の行末に達すると、キャレットの挿入スタイルが間違っています
  3. 問題71598:contentEditableの最後にある編集不可能な要素の後のキャレットが間違った位置にあります

私が見つけた最も近いSOソリューションは ここ である可能性があります。このソリューションのアイデアは、&zwnj;要素の後に<span>文字を配置することですが、<span>を削除したい場合は、代わりに&zwnj;...を削除します。カーソルを最後にジャンプして、「最初のDeleteキーストローク」で<span>を削除しないことで、奇妙なUIエクスペリエンスを提供します。

質問

誰かがこの問題を経験し、回避策を見つけましたか? JSがハッキーになったとしても、考えられる解決策を歓迎します。 contenteditableを活用することには、苦労のリストが付属していることを知りましたが、これが私が現在抱えている最後の難しさのようです。

17
scniro

なぜそうなるのかわかりませんが、<div>のサイズと関係があると感じたので、displayプロパティで遊んでみました。 inline-blockに設定し、テキストを少し試してみたところ、編集、具体的には改行を追加すると、問題が解消されたことがわかりました。

何らかの理由で、新しい行を削除した後、<br/>タグがdiv.mainに保持されているのを見ましたが、<div>の外観と、矢印キーへの応答方法は新しい行がない場合と同じです。

そこで、CSSを変更し、<br/>とビオラにdiv.mainタグを付けて、フィドルを再起動しました。

結論として:

  1. display: inline-blockdiv.mainに追加します
  2. <br/>の最後にdiv.mainを追加します

JSFiddle Link

17
dutzi

問題は、contenteditable要素がdivであり、デフォルトではdisplay: blockであるということです。これがキャレット位置の問題の原因です。これは、最も外側のdivを編集不可にし、inline-blockとして扱われる新しい編集可能なdivを追加することで修正できます。

新しいHTMLには、外側のHTMLのすぐ内側に新しいdivがあります(最後に対応する終了タグがあります)。

<div id="main" class="main"><div id="editable" contenteditable="true">...

そしてこれをCSSに追加します:

div#editable {
    display: inline-block;
}

span要素の間にあるときにキャレットを見やすくするために、CSSのmargin: 2pxのルールにdiv.main spanも追加しましたが、これを防ぐために必要ではありません。質問で報告されたキャレットジャンプの問題。

これが フィドル です。

発見し始めたように、contenteditableはブラウザ間で一貫性のない方法で処理されます。数年前、私はcontenteditableですべてが簡単になると思ったプロジェクト(ブラウザー内XMLエディター)の作業を開始しました。その後、アプリケーションを開発すると、contenteditableが無料で提供すると思っていた機能を引き継ぐことにすぐに気付きました。今日、contenteditableが私に与える唯一のことは、編集したい要素のキーボードイベントをオンにすることです。キャレットの動きやキャレットの表示など、その他すべてはカスタムコードによって管理されます。

4
Louis

したがって、inputフィールドが更新されたときにトリガーされる contenteditable イベントに依存する、少しクリーンなアプローチの実装があります。このイベントはIEでサポートされていませんが、contenteditableはIEとにかくかなり不安定なようです。

基本的に、contenteditable="false"とマークされたすべての要素の周りに要素を挿入します。おそらくinputイベントだけでなく、初期ロード時に実行できますが、リージョンにさらにspanを挿入する方法があるかもしれないと考えたので、inputを考慮する必要があります。このため。

制限には、あなたが自分で見たように、矢印キーを取り巻くいくつかの奇妙な振る舞いが含まれます。私が見たほとんどのことは、スパンを後方に移動したときはカーソルが適切な位置を維持しますが、前方に移動したときはそうではないということです。

JSFiddle Link

Javascript

$('#main').on('input', function() {
    var first = true;
    $(this).contents().each(function(){
        if ($(this).is('[contenteditable=false]')) {
            $("<i class='wrapper'/>").insertAfter(this);
            if (first) {
                $("<i class='wrapper'/>").insertBefore(this);
                first = false   
            }
        }
    });
}).trigger('input');

[〜#〜] css [〜#〜]

div.main {
    width: 400px;
    height: 250px;
    border: solid 1px black;
}

div.main span {
    width:40px;
    background-color: red;
    border-radius: 5px;
    color: white;
    cursor: pointer;
}

i.wrapper {
    font-style: normal;
}
1
cmptrgeekken

<div>の最後でカーソルが誤動作するinitial問題をJQueryで解決するためのハックな方法を見つけました。私は基本的に、カーソルが<i>の内容の最後に「ラッチ」するための空の<div>を挿入しています。このタグは、font-style: normal(フィドルを参照)をオーバーライドするために通常のテキストからバックスペースするときにエンドユーザーにUIの違いがなく、挿入するために<span>とは異なるものが必要だったために機能します

私はこの回避策に特にわくわくしていません。特に<i>を選択したからですが、より良い代替案に出くわすことはありません。うまくいけば矢印キーも理解するためにこれに取り組み続けるつもりですが、幸いなことに、私はFiddleのこのグリッチに悩まされており、コードには悩まされていません。


Fiddle(Chromeのみ)

<div id="main" contenteditable="true">
    <span contenteditable="false">item</span>
    <span contenteditable="false">item</span>
    <span contenteditable="false">item</span>
</div>
function hackCursor() {    
    return $('#main :last-child').get(0).tagName === 'SPAN' ? 
        $('#main span:last-child').after('<i style="font-style: normal"></i>') : 
        false;
}

$(function(){

    hackCursor();

    $('#main').bind({
        'keyup': function(e) {
            hackCursor();
        }
    })
});
0
scniro

改行文字を追加するだけです(\ncontenteditableタグを閉じる前。たとえばJavaScriptの場合:var html = '<div id="main" contenteditable="true">' + content + "\n" + '</div>'

0
Mati Horovitz