注意
結果:やりたいことはできなかった。
結果としての回避策:URLによって、Dataをhtmlタグのdata attributeなどにセットして、そこからJSを動かすようにして、テストを書くことで回避した。
以下で書いたものは、自分が調べたもので、紆余曲折があるので、暇な人だけが見るべき!
やりたいこと
http://dev.classmethod.jp/references/html5-drag-drop-api-review-dom/ を参考にして、ドラッグ・アンド・ドロップを実装
(DOM = Document Object Model:
などの要素にアクセスする仕組み(参照:http://piyo-js.com/05/dom.html ))
$('.draggable-span').on 'dragstart', (e) ->
e.originalEvent.dataTransfer.setData 'val', this.val()
$('.droppable-span').on 'drop', (e) ->
e.preventDefault()
e.stopPropagation()
# ドラッグされたものの名前などを書いたりする処理。
このコードをCapybaraでテストしたい!
参考
D&Dのテスト書いてるのは、
http://ria10.hatenablog.com/entry/20131230/1388372110
これが一番近そう
- capybara-webkit
- drag_to
- jquery-sortable(https://github.com/johnny/jquery-sortable/blob/master/source/js/jquery-sortable.js)
参考にして、 drag_to
を使って以下のように書いてみた。
draggable = page.find('#draggable')
target = page.find('#goal')
draggable.drag_to target
screenshot_and_open_image
問題
うまく移動されていない。
Capybaraでdrag_to がうまくいかない。
理由: 呼ばれるイベントの違い
細かく、drag_toで何がされてるかを見てみると、以下のmouseイベントがとれていた。(今回は、driverにpoltergeistを利用。実は、capybara-webkitだと下のmouseeventすら取れ無かった。)
draggableの方で、
1. mouseover
2. mouseenter
3. mousemove
4. mousedown
5. mouseleave
6. mouseout
Dropされる方で、
7. mouseover
8. mouseenter
9. mousemove
10. mouseup
しかし、重要な、'dragstart', 'drop'などのドラッグイベントはとれない!マウスイベントのみ。
おそらくjquery-sortableは、mouseeventで、実装されているよう。
まとめると、
ドラッグイベントとマウスイベントは別物である!(以下に簡単にドラッグ・アンド・ドロップに関連しそうなイベントをまとめておく。)
主なドラッグイベント
1. drag
2. dragend
3. dragenter
4. dragexit
5. dragleave
6. dragover
7. dragstart
8. drop
主なマウスイベント(drag_toで取れていたイベントを載せた)
1. mouseover
2. mouseenter
3. mousemove
4. mousedown
5. mouseleave
6. mouseout
7. mouseup
解決策: Triggerでdragstart を発火させる(未解決)
やってみる
jQueryで画像やリンクのクリック状態(マウスイベント)やキーボード入力状態を強制的に作り出すを参考にdragstart
というDragEventを発火させてみる。
ちなみにDragEventとは
DOM event
問題:e.originalEvent.dataTransfer
で使っているoriginalEventがない。
実際にドラッグした場合:
j…y.Event {originalEvent: DragEvent, type: "dragstart", timeStamp: 8800.24, jQuery1113048166412157867233: true, which: 1…}
$('#some_id').trigger('dragstart')
で発火した場合:
…y.Event {type: "dragstart", timeStamp: 1472621534967, jQuery1113048166412157867233: true, isTrigger: 3, namespace: ""…}
実際には、originalEvent: DragEvent
があるのに対して、triggerには、ない!
そもそもoriginalEventとは、
jQueryのbindを使うとdataTransferプロパティがイベントから取得出来ない現象が起こりました。原因は、わかってしまえば単純で、jQueryのイベントハンドラに渡されるeventオブジェクトはjQueryがブラウザ互換性の為に作成した独自オブジェクトだからでした。HTML5等で新たに追加されたプロパティにアクセスするには、大元のeventオブジェクトを取得する必要があります。
と書かれている。
イベントをもう少し細かく作ってやってみる!
ただtrigger
するとダメそうなので、
jQueryのtrigger()みたいに、イベントを作ったり発火させたりしてみよう。
これを参考に、
js
event = new DragEvent('dragstart', {bubbles: true})
el = document.querySelector('#col_0')
el.dispatchEvent(event)
と書いてみると、originalEventに関してはできた。だが、originalEventがdatatransferを持っていない。Defaultでは、nullになるため。。(ここでうまく初期化する方法がわからん。)
-
DragEvent
は、ここ に書いてあるようにDragEventオブジェクトを総合的に作るコンストラクタである。 - DragEvent interfaceは、DOM eventである。
-
MouseEvent
とEvent
を継承している -
DragEvent.dataTransfer
(read only): drag & drop の間にデータが送られる(Although this interface has a constructor, it is not possible to create a useful DataTransfer object from script, since DataTransfer objects have a processing and security model that is coordinated by the browser during drag-and-drops.) -
Document.querySelector()
は、Selectorに合うDocumentのなかの最初のElementを返す。
上の例で$('#col_0')
では動かない。これでは帰ってくるのが、[<span...></span>]
こんなかんじである。
逆に、Document.querySelector()の方は、html tagそのままが取れている。<span class="draggable-span"...></span>
しょうがない解決策
coffeeのクラスをGlobalにして、rspecから、execute_script()
で無理矢理トリガーする。。。
ただ、テストのために、不必要にGlobalにするのが嫌だ。
ドラッグ・アンド・ドロップの詳細
draggableにするには、
draggable属性与えて、dragstartのEventListenerをあたえる。(ドラッグされたデータを保存する。)
event handlerは
ドラッグされたテクスト選択ではなく、 DataTransfer object にデータを保存し、許可された効果をセットする(copy, move, link, など)
dropされる側は、
- dropzoneの属性を与えて、drop イベントリスナーをつける
- dropzoneの値は、データ・タイプを指定する。 ('string:text/plain', 'file:image/png'など)
dragされたOriginalのものを消したい場合は、
dragend
eventを使う
DataTransfer interface
- このObjectは、ドラッグデータをとるときに使われる。
- ドラッグドロップのがFireされている間のみ有効
結論
dragEventは、無理ww
調べ漏れや、もっといい方法があるかもしれないので、何かあればコメントいただければ嬉しいです。