セットアップ
それで、私は contenteditable div を持っています - 私は WYSIWYG エディターを作成しています: 太字、イタリック体、書式設定、何でも、そして最近では (派手なボックスに、キャプション付きで) 派手な画像を挿入します。
<a class="fancy" href="i.jpg" target="_blank">
<img alt="" src="i.jpg" />
Optional Caption goes Here!
</a>
ユーザーは、私が表示するダイアログを使用してこれらの派手な画像を追加します。詳細を入力し、画像をアップロードしてから、他のエディター機能と同じようにdocument.execCommand('insertHTML',false,fancy_image_html);
、ユーザーの選択に使用します。
望ましい機能
これで、ユーザーは派手な画像を挿入できるようになりました。ユーザーは画像を移動できる必要があります。ユーザーは、画像 (ファンシー ボックスなど) をクリック アンド ドラッグして、contenteditable 内の好きな場所に配置できる必要があります。段落間、または段落内でも、必要に応じて 2 つの単語間で移動できる必要があります。
私に希望を与えるもの
心に留めておいてください -- contenteditable では、単純な古い<img>
タグは、この素敵なドラッグ アンド ドロップ機能を備えたユーザー エージェントによって既に祝福されています。デフォルトでは、好き<img>
な場所にタグをドラッグ アンド ドロップできます。デフォルトのドラッグ アンド ドロップ操作は、夢のように動作します。
したがって、このデフォルトの動作がすでに<img>
仲間に非常にうまく機能していることを考えると、この動作を少し拡張して HTML を少し追加したいだけなので、これは簡単に実現できるように思えます。
これまでの私の取り組み
最初に、draggable 属性を使用して派手なタグを設定し、<a>
contenteditable を無効にしました (それが必要かどうかはわかりませんが、オフにしたほうがよいようです)。
<a class="fancy" [...] draggable="true" contenteditable="false">
次に、ユーザーは引き続き画像をファンシー<a>
ボックスからドラッグできるため、CSS を実行する必要がありました。私は Chrome で作業しているので、他のプレフィックスも使用しましたが、-webkit- プレフィックスのみを表示しています。
.fancy {
-webkit-user-select:none;
-webkit-user-drag:element; }
.fancy>img {
-webkit-user-drag:none; }
これで、ユーザーはファンシー ボックス全体をドラッグできるようになり、少し部分的に色あせたクリック アンド ドラッグ表現の画像がこれを反映しています。ボックス全体を選択していることがわかります :)
さまざまな CSS プロパティのいくつかの組み合わせを試しましたが、上記の組み合わせが理にかなっており、最もうまく機能しているようです。
この CSS だけで、ブラウザが要素全体をドラッグ可能なアイテムとして使用し、私が夢見ていた機能をユーザーに自動的に付与するのに十分であることを望んでいました...しかし、それよりも複雑なようです。
HTML5 の JavaScript ドラッグ アンド ドロップ API
このドラッグ アンド ドロップは、必要以上に複雑に思えます。
それで、私はDnD api docsに深く入り始めましたが、今は立ち往生しています。だから、ここに私が装備したものがあります(はい、jQuery):
$('.fancy')
.bind('dragstart',function(event){
//console.log('dragstart');
var dt=event.originalEvent.dataTransfer;
dt.effectAllowed = 'all';
dt.setData('text/html',event.target.outerHTML);
});
$('.myContentEditable')
.bind('dragenter',function(event){
//console.log('dragenter');
event.preventDefault();
})
.bind('dragleave',function(event){
//console.log('dragleave');
})
.bind('dragover',function(event){
//console.log('dragover');
event.preventDefault();
})
.bind('drop',function(event){
//console.log('drop');
var dt = event.originalEvent.dataTransfer;
var content = dt.getData('text/html');
document.execCommand('insertHTML',false,content);
event.preventDefault();
})
.bind('dragend',function(event){
//console.log('dragend');
});
ここで私は立ち往生しています。これはほぼ完全に機能します。ほぼ完全に。最後まで、すべてが機能しています。ドロップ イベントでは、ドロップ位置に挿入しようとしているファンシー ボックスの HTML コンテンツにアクセスできるようになりました。あとは、正しい位置に挿入するだけです!
問題は、正しいドロップ場所、またはそこに挿入する方法が見つからないことです。ファンシーボックスをダンプするためのある種の「dropLocation」dropEvent.dropLocation.content=myFancyBoxHTML;
オブジェクトを見つけたいと思っていました。
私は何かを与えられていますか?
私はそれを完全に間違っていますか?私は何かを完全に見逃していますか?
できるはずだと思っていたように使用しようとしましdocument.execCommand('insertHTML',false,content);
たが、残念ながら、選択キャレットが希望どおりの正確なドロップ位置に配置されていないため、ここでは失敗します。
すべての 's をコメントアウトするevent.preventDefault();
と、選択キャレットが表示されることを発見しました。希望どおり、ユーザーがドロップする準備をしているときに、ドラッグを contenteditable の上に置くと、小さな選択キャレットが文字間に沿って表示されることがわかります。ユーザーのカーソルとドロップ操作に従います。選択キャレットが正確なドロップ位置を表していることをユーザーに示します。この選択キャレットの場所が必要です。
いくつかの実験では、drop イベントと dragend イベントの間に execCommand-insertHTML'ing を試しました。dropping-selection-caret があった場所に HTML を挿入するのではなく、ドラッグ操作の前に選択された場所を使用します。
ドラッグオーバー中に選択キャレットが表示されるので、計画を立てました。
しばらくの間、ドラッグ オーバー イベントで、 の<span class="selection-marker">|</span>
直後にのような一時マーカーを挿入$('.selection-marker').remove();
しようとしていました。これは、ブラウザーが常に (ドラッグ オーバー中に) すべての選択マーカーを削除してから、挿入ポイントに 1 つ追加しようとするためです。基本的に、その挿入ポイントがどこにあっても、いつでも 1 つのマーカーを残します。もちろん、計画は、この一時的なマーカーを、私が持っているドラッグされたコンテンツに置き換えることでした.
もちろん、これはどれも機能しませんでした。選択マーカーを計画どおりに明らかに表示されている選択キャレットに挿入することができませんでした。ここでも、ドラッグ操作の前に、execCommand-insertedHTML が選択キャレットがあった場所に配置されました。
ハフ。それで、私は何を逃したのですか?それはどのように行われますか?
ドラッグ アンド ドロップ操作の正確な位置を取得または挿入するにはどうすればよいですか? これは、明らかに、ドラッグ アンド ドロップの一般的な操作のように感じます。確かに、ある種の重要で露骨な詳細を見落としたに違いありませんか? 私は JavaScript に深く入り込む必要さえありましたか、それとも、ドラッグ可能、ドロップ可能、コンテンツ編集可能、そして派手な CSS3 などの属性だけでこれを行う方法があるのでしょうか?
私はまだ探しています - まだいじくり回しています - 私が失敗しているものを見つけたらすぐに投稿します:)
The Hunt Continues (元の投稿後に編集)
Farrukh は良い提案を投稿しました -- 以下を使用してください:
console.log( window.getSelection().getRangeAt(0) );
選択キャレットが実際にどこにあるかを確認します。これは、選択キャレットが contenteditable 内の編集可能なコンテンツ間を目に見えて飛び回っていることがわかったときです。
残念ながら、返される Range オブジェクトは、ドラッグ アンド ドロップ操作の前に、選択キャレットに属するオフセット インデックスを報告します。
それは勇敢な努力でした。ありがとうファルク。
それで、ここで何が起こっているのですか?飛び回っている小さな選択キャレットが、選択キャレットではないという感覚が得られます。詐欺師だと思います!
さらに検査すると!
結局のところ、それは詐欺師です!ドラッグ操作全体を通して、実際の選択キャレットはそのまま残ります。あなたは小さなバガーを見ることができます!
私はMDN Drag and Drop Docsを読んでいて、これを見つけました:
当然、ドラッグオーバー イベントの周りで挿入マーカーを移動する必要がある場合もあります。他のマウス イベントと同様に、イベントの clientX および clientY プロパティを使用して、マウス ポインターの位置を特定できます。
ええ、これはclientXとclientYに基づいて、自分でそれを理解することになっているということですか?? マウス座標を使用して選択キャレットの位置を自分で決定しますか? 怖い!!
私は明日そうすることにします-私自身、またはこれを読んでいる他の誰かが正気の解決策を見つけることができない限り:)