Javascriptのオリジナルドラッグ&ドロップを深く理解する

44669 ワード

前の話
ドラッグ&ドロップ(drag-and-drop,DnD)は、ドラッグ&ドロップの2つの動作です.したがって、2つの要素に関連しています.1つはドラッグ&ドロップソースと呼ばれるドラッグ&ドロップされた要素です.もう1つは、ドラッグ&ドロップターゲットと呼ばれるターゲットです.本稿では,この2つの概念を分割することによって,原生ドラッグ&ドロップについて詳細に説明する.
 
ソースのドラッグ&ドロップ
どのような要素がドラッグ&ドロップソースですか?
HTML 5はすべてのHTML要素にdraggable属性を規定し、要素がドラッグできるかどうかを示す.
画像とリンクのdraggableプロパティは自動的にtrueに設定され、他の要素のこのプロパティのデフォルト値はfalseです.
[注意]draggable='true'を設定しないと有効になりません.draggableのみを設定しても機能しません.
デフォルトでは、テキストは選択されている場合にのみドラッグでき、画像とリンクはいつでもドラッグできます.他の要素はドラッグ&ドロップできません
<input value="     ">
<img alt="     " src="http://files.cnblogs.com/files/xiaohuochai/zan.gif">
<a href="#">     a>
<div id="test" style="height:30px;width:300px;background:pink;">      div>

エレメントにdraggableプロパティを設定すると、通常のエレメントもドラッグできます.
<div draggable="true" style="height:30px;width:100px;background:pink;">div>

互換性
IE 9-ブラウザではdraggableプロパティはサポートされていませんが、mousedownイベントハンドラでdragDrop()メソッドを呼び出すことでドラッグ効果を実現できます.
<div id="test"  style="height:30px;width:300px;background:pink;">div>    
<script>
test.onmousedown = function(){
    this.dragDrop();
}
script>

[注意]firefoxにdraggableプロパティをサポートさせる場合は、ondragstartイベントハンドラを追加し、dataTransferオブジェクトでsetData()メソッドを使用して効果を開始する必要があります.
ドラッグ&ドロップイベント
ドラッグ&ドロップソースは、3つのドラッグ&ドロップイベントに関連します.ドラッグ&ドロップソースをドラッグすると、dragstart、drag、dragendの3つのイベントが順にトリガーされます.
dragstart
マウスボタンを押してマウスの移動を開始すると、ドラッグ&ドロップされた要素でdragstartイベントがトリガーされます.このときカーソルが「放せない」記号(円環の中に反斜線がある)になり、要素を自分の上に置くことができないことを示します.
drag
dragstartイベントがトリガーされるとdragイベントがトリガーされ、要素がドラッグされている間もイベントがトリガーされ続けます.
dragend
ドラッグが停止するとdragendイベントがトリガーされます(要素を有効な配置ターゲットに配置しても、無効な配置ターゲットに配置しても).
<div id="test"  draggable="true" style="height:30px;width:100px;background:pink;">0div>    
<script>
var timer,i=0;
test.ondragstart = function(){
    this.style.backgroundColor = 'lightgreen';
}
test.ondrag = function(){
    if(timer) return;
    timer = setInterval(function(){
        test.innerHTML =  i++;
    },100)
}
test.ondragend = function(){
    clearInterval(timer);
    timer = 0;
    this.style.backgroundColor = 'pink';
}
script>

ドラッグ&ドロップターゲット
ドラッグアンドドロップターゲットとは、ドラッグされた要素がマウスを離すと配置されるターゲットです.
ドラッグ&ドロップソースがドラッグ&ドロップターゲットにドラッグすると、dragenter、dragover、dragleave、dropの4つのイベントが順にトリガーされます.
dragenter
要素が配置ターゲットにドラッグされるとdragenterイベントがトリガーされます
dragover
ドラッグされた要素がターゲットを配置する範囲内を移動するとdragoverイベントがトリガーされ続けます
dragleave
要素が配置ターゲットからドラッグアウトされた場合、dragleaveイベントがトリガーされます.
drop
要素が配置ターゲットに配置されている場合、dropイベントがトリガーされます.
[注意]firefoxブラウザのdropイベントのデフォルトの動作は、配置先に置かれたURLを開くことです.firefoxが通常のドラッグ&ドロップをサポートするためにdropイベントのデフォルトの動作もキャンセルします.
デフォルトでは、ターゲット要素は配置できないためdropイベントは発生しません.dragoverイベントとdragenterイベントでデフォルトの動作をブロックしてこそ、dropイベントの発生を許可する配置ターゲットになります.このとき、カーソルは配置を許可する記号になります
<div id="test"  draggable="true" style="height:30px;width:130px;background:pink;float:left;">   div>    
<div id="target" style="float:right;height: 200px;width:200px;background:lightblue;">    div>
<script>
var timer,i=0;
var timer1,i1=0;
//  IE8-   
test.onmousedown = function(){
    if(this.dragDrop){
        this.dragDrop();
    }
}
test.ondragstart = function(){
    this.style.backgroundColor = 'lightgreen';
    this.innerHTML = '    ';
}
test.ondrag = function(){
    if(timer) return;
    timer = setInterval(function(){
        test.innerHTML =  '      ' + ++i + ' ';
    },1000);
}
test.ondragend = function(){
    clearInterval(timer);
    timer = 0;i =0;
    this.innerHTML = '    ';
    this.style.backgroundColor = 'pink';
}
target.ondragenter = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    this.innerHTML = '         ';
    this.style.background = 'red';
}
target.ondragover = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    if(timer1) return;
    timer1 = setInterval(function(){
        target.innerHTML =  '     ' + (++i1) + ' ';
    },1000);
}
target.ondragleave = function(){
    clearInterval(timer1);
    timer1 = 0;i1=0;
    this.innerHTML = '         ';
    this.style.backgroundColor = 'lightblue';
}
target.ondrop = function(){
    clearInterval(timer1);
    timer1 = 0;i1=0;
    this.innerHTML = '         ';
    this.style.backgroundColor = 'orange';    
}
script>

DataTransferオブジェクト
ドラッグアンドドロップ操作時にデータ交換を行うためにdataTransferオブジェクトが導入されました.これは、ドラッグされた要素から配置先に文字列形式のデータを渡すイベントオブジェクトの属性です.
DataTransferオブジェクトには、getData()とsetData()の2つの主な方法があります.
getData()は、setData()によって保存された値を取得できます.setData()メソッドの最初のパラメータであり、getData()メソッドの唯一のパラメータでもあり、保存されたデータ型を表す文字列であり、値は「text」または「URL」である.
IEは「text」と「URL」の2つの有効なデータ型のみを定義し、HTML 5はこれを拡張し、様々なMIMEタイプを指定することができる.後方互換性を考慮すると、HTML 5では「text」と「URL」もサポートされていますが、この2つのタイプは「text/plain」と「text/uri-list」にマッピングされます.
実際、dataTransferオブジェクトは、MIMEタイプごとに値を保存できます.すなわち、このオブジェクトにテキストとURLを同時に保存しても問題はありません.
[注意]dataTransferオブジェクトに保存されているデータはdropイベントハンドラでのみ読み込むことができます
テキストボックスのテキストをドラッグすると、ブラウザはsetData()メソッドを呼び出し、ドラッグしたテキストをdataTransferオブジェクトに「text」形式で保存します.同様に、リンクや画像をドラッグ&ドロップすると、setData()メソッドが呼び出され、URLが保存されます.次に、これらの要素が配置ターゲットにドラッグ&ドロップされると、getData()でこれらのデータを読み取ることができます.
<div>                    div>    
<div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">    div>
<div id="result">div>
<script>
target.ondragenter = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    this.innerHTML = '         ';
    this.style.background = 'red';
}
target.ondragover = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
}
target.ondragleave = function(e){
    e = e || event;    
    this.innerHTML = '         ';
    this.style.backgroundColor = 'lightblue';
}
target.ondrop = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    result.innerHTML = '          :' + e.dataTransfer.getData('text');
    this.innerHTML = '         ';
    this.style.backgroundColor = 'orange';    
}
script>

もちろん、dragstartイベントハンドラでsetData()を呼び出し、将来使用するために自分が転送するデータを手動で保存することもできます.
<div id="test" draggable="true" data-value="      " style="height:30px;width:100px;background:pink;">   div>    
<div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">    div>
<div id="result">div>
<script>
 //  IE8-   
test.onmousedown = function(){
    if(this.dragDrop){
        this.dragDrop();
    }
}
test.ondragstart = function(e){
    e = e || event;
    e.dataTransfer.setData('text',test.getAttribute('data-value'));
}
target.ondragenter = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    this.innerHTML = '         ';
    this.style.background = 'red';
}
target.ondragover = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
}
target.ondragleave = function(e){
    e = e || event;    
    this.innerHTML = '         ';
    this.style.backgroundColor = 'lightblue';
}
target.ondrop = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    result.innerHTML = '          :' + e.dataTransfer.getData('text');
    this.innerHTML = '         ';
    this.style.backgroundColor = 'orange';    
}
script>

カーソルを変更
DataTransferオブジェクトを使用すると、データを転送するだけでなく、ドラッグされた要素や、ドロップターゲットとなる要素がどのような操作を受信できるかを決定できます.このためには、dataTransferオブジェクトの2つのプロパティにアクセスする必要があります:dropEffectとeffectAllowed
実際には、この2つのアトリビュートは何の役にも立たず、ソースをドラッグしてターゲットを移動するときに異なるカーソルを変更するだけです(ただし、1つの場合を除きます).
dropEffect
dropEffectプロパティは、ドラッグされた要素がどの配置動作を実行できるかを知ることができます.このプロパティには、次の4つの可能な値があります.
「none」:ドラッグした要素をここに置くことはできません.これはテキストボックス以外のすべての要素のデフォルトです(dropイベントはトリガーできません)
「move」:ドラッグした要素を配置ターゲットに移動する必要があります
「copy」:ドラッグした要素を配置ターゲットにコピーする必要があります
「link」:ターゲットを配置するとドラッグする要素が開きます(ただし、ドラッグする要素はリンクでURLがある必要があります)
エレメントを配置ターゲットにドラッグすると、上記の各値によってカーソルが異なるシンボルとして表示されます.
[注]dropEffectプロパティは、ondragoverイベントハンドラで配置ターゲットに対して設定する必要があります.
effectAllowed
dropEffectプロパティはeffectAllowedプロパティを組み合わせるだけで役立ちます.effectAllowedプロパティは、要素をドラッグできるdropEffectを表します.
effectAllowedプロパティの可能な値は次のとおりです.
「uninitialized」:ドラッグされた要素に配置動作を設定しません.
「none」:ドラッグされた要素は動作しません.
「copy」:値が「copy」のdropEffectのみを許可
「link」では、値が「link」のdropEffectのみが許可されます.
「move」:値が「move」のdropEffectのみを許可
「copyLink」:「copy」と「link」の値を許可するdropEffect
「copyMove」:値が「copy」および「move」のdropEffectを許可
「linkMove」:「link」と「move」の値を許可するdropEffect
「all」:任意のdropEffectを許可
[注]ondragstartイベントハンドラでeffectAllowedプロパティを設定する必要があります
<div id="test" draggable="true"  style="height:30px;width:100px;background:pink;display:inline-block;">   div>
<br>
<div id="target1" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(none)    div>
<div id="target2" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(move)    div>
<div id="target3" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(copy)    div>
<div id="target4" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(link)    div>
<div id="result">div>
<script>
//  IE8-   
test.onmousedown =function(){
    if(this.dragDrop){
        this.dragDrop();
    }
}
test.ondragstart = function(e){
    e = e || event;
    //  firefox   
    e.dataTransfer.setData('text','');
      e.dataTransfer.effectAllowed = 'all';
}
target1.ondragenter = target2.ondragenter =target3.ondragenter =target4.ondragenter =function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }this.style.background = 'red';
}
target1.ondragover = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    e.dataTransfer.dropEffect = 'none';
}
target2.ondragover = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    e.dataTransfer.dropEffect = 'move';
}
target3.ondragover = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    e.dataTransfer.dropEffect = 'copy';
}
target4.ondragover = function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    e.dataTransfer.dropEffect = 'link';
}
target1.ondragleave = target2.ondragleave =target3.ondragleave =target4.ondragleave =function(e){
    e = e || event;    this.style.backgroundColor = 'lightblue';
}
target1.ondrop = target2.ondrop =target3.ondrop =target4.ondrop =function(e){
    e = e || event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue = false;
    }
    this.style.backgroundColor = 'orange';    
}
script>