これでできる! クロスブラウザJavaScript入門

第22回 JavaScriptによるUIの実装:ドラッグ

この記事を読むのに必要な時間:およそ 5.5 分

こんにちは。前回に引き続き,JavaScriptでUIを実装する方法を紹介していきます。

要素のドラッグ

ウェブアプリでよく使われるインタフェースのひとつ,ドラッグを実装してみます。ドラッグを使用するケースは幾つかありますが,今回は投稿用フォームをドラッグで移動できるようにするという使い方を想定してみます。

投稿フォームで入力する際,そのページのコンテンツを見ながら入力したいということはよくあります。フォームを好きな位置に移動できればそれが実現できます。

ドラッグの基本HTML

<div class="js-drag" id="js-drag-1">
  <form class="js-drag-form" onsubmit="return false;">
    <textarea></textarea>
    <input class="submit" type="submit" value="送信">
  </form>
</div>

そして,CSSは次のとおりです。ドラッグする要素のpositionをabsoluteにして,margin・padding・borderなどは0にしています。これらを0にすることで,これらの値に対するブラウザ間の差を考えなくてもよくなるので実はこれが重要なポイントです。

ドラッグの基本CSS

.js-drag{
  position:absolute;
  background:#555;
  display:block;
  margin:0;
  padding:0;
  border:0;
}
.js-drag form{
  margin:15px;
}
.js-drag textarea{
  width:400px;
  height:240px;
  display:block;
}
.js-drag input.submit{
  width:400px;
  height:30px;
}

さて,ドラッグを実現するJavaScriptです。まずはなるべくシンプルに実装してみます。

ドラッグの基本JavaScript

var element = document.getElementById('js-drag-1');
// 要素の現在位置を取得
var rect = element.getBoundingClientRect();
// 要素をbody直下に移動
document.body.appendChild(element);
// この時,要素の位置がページ最下部に移動してしまう
var root = document.documentElement;
// 現在のスクロール量を取得
var left = window.pageXOffset || root.scrollLeft;
var top  = window.pageYOffset || root.scrollTop;
// 要素の位置を戻す
element.style.left = (rect.left + left) + 'px';
element.style.top = (rect.top + top) + 'px';

var dragging = false;
element.onmousedown = function(evt){
  dragging = true;
};
document.onmouseup = function(evt){
  dragging = false;
};
document.onmousemove = function(evt){
  if(dragging){
    if(!evt){
      evt = window.event;
    }
    var left = window.pageXOffset || root.scrollLeft;
    var top  = window.pageYOffset || root.scrollTop;
    element.style.left = left + evt.clientX + 'px';
    element.style.top = top + evt.clientY + 'px';
  }
};

ドラッグの下準備として,要素をbodyの直下に移動しています。これをしておくことでページの基点と要素の基点が一致して移動させやすくなります。

肝心のドラッグ処理のコードは至ってシンプルですね。マウスダウンでドラッグを開始し,アップでドラッグ停止,移動中はスクロール量を加味したマウスの位置をドラッグ対象の要素に適用してあげるだけです。下記のボタンを押すと下記のフォームをドラッグできるようになります。

しかし,なんとかドラッグはできるものの,色々と問題・改善点があります。

  • ドラッグを開始すると要素の位置がずれる(要素の左上を掴んだ状態になる)
  • ドラッグ中にテキストを選択してしまう
  • テキストエリア内の文字を選択できない
  • スクロールに追従しない(必ずしも追従したほうがよいというわけではありませんが,今回は追従させます)

といった問題があげられます。

まず,ドラッグを開始時に位置がずれる件は,マウスダウンしたときの要素とマウスの相対位置を記憶しておき,ドラッグ中もその位置関係を残すようにしましょう。

ドラッグ中にテキストを選択してしまう問題については,IE以外のブラウザはmousedownの時にデフォルトの挙動をキャンセルする(onmousedownでreturn falseとするか,preventDefaultを呼び出す)ことで防ぐことができます。IEの場合,mousemoveでデフォルトの挙動をキャンセルする(onmousemoveでreturn falseとするか,returnValueにfalseを設定する)ことでテキスト選択を防ぐことができます。

テキストエリア内の文字を選択できない件については,ドラッグを開始する要素を限定することで対応できます。

スクロールの追従については第20回で扱ったposition:fixedで実現します。

著者プロフィール

太田昌吾(おおたしょうご,ハンドルネーム:os0x)

1983年生まれ。JavaScriptをメインに,HTML/CSSにFlashなどのクライアントサイドを得意とするウェブエンジニア。2009年12月より、Google Chrome ExtensionsのAPI Expertとして活動を開始。

URLhttp://d.hatena.ne.jp/os0x/

コメント

コメントの記入