【JavaScript】ドラッグ&ドロップ機能を作成する方法
JavaScriptを使用して、ドラッグ&ドロップ機能の作成方法を解説します。以下のようなやつですね。ユーザーからの写真やイラストやテキストファイルをサーバーに送信したりする際に使用するプラットフォームです。
こんな感じのファイルを選択も出来て、ドラッグ&ドロップでファイルをアップロードする機能の作り方とかも需要あるのかなあ。
ドラッグ&ドロップの仕様結構気持ち悪くて、dragoverとdropの際にpreventDefault()をしないとうまく挙動しない旨とかも解説すればある程度あるか🤔 pic.twitter.com/9BugzVc6Di
— ys@Webエンジニア(サーバーサイド寄り (@sy_sh7711) September 16, 2019
JavaScriptと合わせて実装することで、ボタンでファイルを選択することだけではなく、ファイルをドラッグ&ドロップで選択出来るようにする事でユーザーが使い易くなり、少しでもサイトのユーザービリティが上がると思います。
ファイルを選択するためのデザインを作成しよう
HTMLは簡易的なもので用意しますが、ドラッグ&ドロップをすることを考慮し、ドラッグできる領域を明示的にした方がユーザー的に分かり易いので、当記事もそのように行います。
今回は、モバイル用のレイアウトは特に考慮していません。
下記2つのレイアウトを用意します。
- ファイルを選択するためのボタン
- ドラッグ&ドロップするための領域
<input type=”file”>でファイルを選択出来るようにしよう
ファイルを選択出来るようにするには<input type="file">
を使用します。
ただ、デフォルトだと以下のようなデザインなので使い辛いですね。
そして実はHTMLの仕様でこの<input file="type">
は直接CSSを当てて、デザインをボタン風にしたりする事ができません。しかし<label>
タグと併用するとデザインを変更することが出来るようになります。
input fileのHTMLを整える
1
<input type="file" id="file_upload">
通常のinput fileでタグを用意して、idセレクタを設置します。この際のidセレクタの値はお好みで付けてOK
2
<label for="file_upload" class="select-file" id="js-select-file">
ファイルを選択してください
<input type="file" id="file_upload">
</label>
次に<label>
タグを先ほど設置した<input type="file">
の上の階層に設置します。
この時に先ほど設定したidセレクタの値と<label>
タグに設定しているfor=””の値は一致させてください。
<label>
に設定する文言は自由に変える事が出来ます。ここは通常<input type="file">
を使用する際には変更することは出来ないのですが、このタグを使用することにより自由に変更が可能です。
ファイル選択ボタンのHTML全体像は以下
<label for="file_upload" class="select-file" id="js-select-file">
ファイルを選択してください
<input type="file" id="file_upload">
</label>
input fileのCSSを整える
1
input {
display: none;
}
デフォルトが邪魔なので隠してしまいましょう。まずは<input type="file">
をdisplay: none;で消します。
1
.select-file {
display: inline-block;
margin: 40px auto;
padding: 25px 40px;
width: 400px;
background-color: #1AA1FF;
border-radius: 5px;
color: #F7F7F7;
cursor: pointer;
font-weight: 700;
z-index: 1;
}
.select-file:hover {
background-color: #136EB6;
}
<label>
タグに対して、デザインを当てていきます。このlabelタグ自体がボタンの役割を担っているのでボタンっぽいデザインに仕上げます。
cursor: pointer;などをつけるとボタンと明示出来ます。また、より分かり易いように、hover時に背景色なども変更します。
CSSの全体像は以下
input {
display: none;
}
.select-file {
display: inline-block;
margin: 40px auto;
padding: 25px 40px;
width: 400px;
background-color: #1AA1FF;
border-radius: 5px;
color: #F7F7F7;
cursor: pointer;
font-weight: 700;
z-index: 1;
}
.select-file:hover {
background-color: #136EB6;
}
ドラッグ&ドロップするためのデザインを作成しよう
まずはHTMLで、ファイルを選択するために必要な領域を作成します。
ここは通常のHTMLだけで細かいデザインはありません。
ドラッグ&ドロップするための領域を作成しよう
まずは大枠のHTMLだけ用意します。overlayと言われる要素を覆うための領域、その覆った要素に表示する文言、選択されたファイルを表示するためのタグを用意します。
ドラッグ&ドロップ領域のHTMLを作成する
1
<div class="container">
</div>
このHTMLは全ての大枠のためのタグです。ほぼ決まり文句のような感じです。
2
<div class="container">
<div class="drop-zone" id="js-dropzone">
</div>
</div>
先ほど作成した要素の中にドラッグ&ドロップ出来るための領域の要素を作成します。
後々記載しますが、JavaScriptの処理はここの要素を中心に書いていきます。
3
<div class="container">
<div class="drop-zone" id="js-dropzone">
<div class="overlay-area" id="js-overlay-area">
<p class="no-active" id="js-overlay-text">ここにドラッグ&ドロップしてください。</p>
</div>
<p class="drop-zone-text">ファイルを選択するか、ドラッグ&ドロップしてください。</p>
</div>
</div>
さらにドロップ領域の中に文言やoverlay要素を作成します。overlay-area
はファイルをドラッグした際に要素を覆い、どこにファイルをドロップするのか明示的にするために置いています。
4
<div class="container">
<div class="drop-zone" id="js-dropzone">
<div class="overlay-area" id="js-overlay-area">
<p class="no-active" id="js-overlay-text">ここにドラッグ&ドロップしてください。</p>
</div>
<p class="drop-zone-text">ファイルを選択するか、ドラッグ&ドロップしてください。</p>
<div class="selected-file no-active" id="js-selected-file">
</div>
</div>
</div>
selected-file
の要素を配置します。この要素は選択されたファイルを表示するための領域です。 input fileではデフォルトでこの機能がありますが、この機能を隠してしまっているため、自前で表示する必要があります。
ドラッグ&ドロップのためのHTMLの全体像
上記で書いたinput fileのHTMLと合体すると以下のようになります。
<div class="container">
<div class="drop-zone" id="js-dropzone">
<div class="overlay-area" id="js-overlay-area">
<p class="no-active" id="js-overlay-text">ここにドラッグ&ドロップしてください。</p>
</div>
<p class="drop-zone-text">ファイルを選択するか、ドラッグ&ドロップしてください。</p>
<label for="file_upload" class="select-file" id="js-select-file">
ファイルを選択してください
<input type="file" id="file_upload">
</label>
<div class="selected-file no-active" id="js-selected-file">
</div>
</div>
</div>
ドラッグ&ドロップ領域のCSSを作成する
1
.container {
padding: 30px;
}
大枠の方にCSSを当てていきます。大枠なのでここら辺は結構適当です。
2
..drop-zone {
position: relative;
width: 1000px;
height: 700px;
margin: 0 auto;
background-color: #eeeeee;
font-size: 20px;
font-weight: 700;
text-align: center;
}
ドロップするための領域にCSSを当てます。background-color
で背景色を少し変えることでドロップする領域を明示するとUI的に良いです。
また、position: relative;
を入れていますが、これについては後々設定するoverlay要素のための準備です。
3
drop-zone-text {
padding: 50px 0px;
}
4
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,.75);
z-index: 100;
}
.overlay-text {
display: block;
position: absolute;
top: 50%;
left: 50%;
color: #F7F7F7;
}
覆う要素であるoverlay要素のCSSを当てていきます。position: absolute;
でドロップ領域を覆うようなデザインにしていきます。
特に特徴はないですがbackground-color
にrgbaを使用すると背景色を指定(4番目の引数)しながら、透明度を指定できるので使用します。
※後々JavaScriptで書いていきますが、実際にファイルがドラッグされた時にこのoverlay要素が表示される仕組みになります。
5
.no-active {
display: none;
}
最後に要素を隠すためのクラスを用意します。overlay要素などはデフォルトでは非表示にする必要があるためこのクラスを当てておきます。
ドラッグ&ドロップのためのCSSの全体像
上記全てのCSSを合わせると以下のような全体像になります。
.no-active {
// 文字数の関係上、上記の⑤をご覧ください
}
.drop-zone {
// 文字数の関係上、上記の②をご覧ください
}
.drop-zone-text {
padding: 50px 0px;
}
.overlay {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,.75);
opacity: 0.8;
z-index: 100;
}
.overlay-text {
display: block;
position: absolute;
top: 50%;
left: 33%;
color: #F7F7F7;
}
input {
display: none;
}
.select-file {
display: inline-block;
margin: 40px auto;
padding: 25px 40px;
width: 400px;
background-color: #1AA1FF;
border-radius: 5px;
color: #F7F7F7;
cursor: pointer;
font-weight: 700;
z-index: 1;
}
.select-file::hover {
background-color: #136EB6;
}
これで全てのHTMLとCSSが書き終わり、画面の雛形が作成出来ました。次に実際にJavaScriptを使用してファイルをドラッグ&ドロップ出来る動作にします。
JavaScriptでドラッグ&ドロップ機能を作成する
JavaScriptでファイルをドラッグ&ドロップする機能を作成するために必要なメソッドは4つ。以下のメソッドを使用して処理を書いていきます。
- dragenter
ドラッグ中のイベントやテキストの選択範囲が、妥当なドロップターゲットに入ったときに発生
引用:Document: dragenter イベント - dragleave
ドラッグしている要素や選択中のテキストが妥当なドロップターゲットを離れたときに発生します。
引用:Document: dragleave イベント - dragover
要素または選択されたテキストが、妥当なドロップターゲットの上にあるときに (数百ミリ秒間隔で) 発生します。
引用:Document: dragover イベント - drop
要素または選択されたテキストが、妥当なドロップターゲットにドロップされたときに発生します。
引用:Document: drop イベント
それぞれのイベントでどういった処理をするのか。
大まかですが基本的には以下のように行います。
・
dragenter
ドロップ領域にファイルが入った段階でoverlay要素を表示・
dragleave
ドロップ領域からファイルが出た段階でoverlay要素を非表示・
drop
ファイルがドロップされた段階でoverlay要素を非表示
ドロップされたファイルを表示する・
dragover
これに関しては後ほど説明しますが、APIの使用上するものになります。
何か必要な処理を別段書くことになります。
この処理に基づいて実装すると以下のようになります。
まずは使用する要素を取得し、変数として宣言。
const dropzone = document.getElementById('js-dropzone');
const overlayText = document.getElementById('js-overlay-text');
const overlayArea = document.getElementById('js-overlay-area');
const fileInput = document.getElementById('file_upload');
const selectedFile = document.getElementById('js-selected-file');
dragenterの処理
overlay要素を表示します。
// ドロップ可能エリアに入った時
dropzone.addEventListener('dragenter', () => {
overlayArea.classList.add('over-lay');
overlayText.classList.add('over-lay-text');
overlayText.classList.remove('no-active');
});
dragleaveの処理
overlay要素を非表示にし、デフォルトで表示されている画面のママに戻します。
// ドロップ可能エリアを出た時
overlayArea.addEventListener('dragleave', () => {
overlayArea.classList.remove('over-lay');
overlayText.classList.remove('over-lay-text');
overlayText.classList.add('no-active');
});
dropの処理
ファイルがドロップされた際も基本はdropleaveの処理と同じです。
ですが、一番重要なドロップされたファイルを取得する処理も記載します。
// ファイルをドロップした時
overlayArea.addEventListener('drop', (e) => {
e.preventDefault();
var fileName = e.dataTransfer.files[0].name;
selectedFile.innerText = fileName;
selectedFile.classList.remove('no-active');
overlayArea.classList.remove('over-lay');
overlayText.classList.remove('over-lay-text');
overlayText.classList.add('no-active');
});
補足:JavaScriptでファイルを取得する方法
JavaScriptでファイルを扱うには、Fileオブジェクトから中身を取得する必要があります。
Fileオブジェクトの中身は以下のようになっています。(Chromeのディベロッパーツールを使用してConsoleタブから確認することが出来ます。)
* FileList {0: File, length: 1}
* 0: File {name: “Index10.html”, lastModified: 1568851615530, lastModifiedDate: Thu Sep 19 2019 09:06:55 GMT+0900 (日本標準時), webkitRelativePath: “”, size: 2063, …} length: 1 __proto__: FileList読みやすいように整形すると以下のようになります。
* lastModified: 1568851615530
* lastModifiedDate: Thu Sep 19 2019 09:06:55 GMT+0900 (日本標準時) {}
* name: “Index10.html”これがファイル名
* size: 2063
* type: “text/html”
* webkitRelativePath: “”
上記でファイルの取得は出来るのですが、ここで注意点。
関数の部分に引数e
を持っていますが、ここの処理ではこの引数が必須です。
この引数はイベントと言われるもので、ここのdropが発火した際にイベントオブジェクトが入ります。
詳しい説明は以下の記事で詳しく解説されているので参考にしてください。
≫ JavaScript初級者から中級者になろう
このイベントオブジェクトに実際にdropされた段階で起きたイベントの詳細が入っているので、そこからファイルを取得します。
また、APIの仕様上このイベントは止めないと流れていってしまい理想の挙動にはならないためe.preventDefault();
で処理を止めないといけません。この仕様はdrop
とdragover
の処理に適用する必要があります。
上記の理由からdragover
にも同様に処理を止めてあげる処理を書きます。
dragoverの処理
// ドロップ可能エリアにカーソルがある時
overlayArea.addEventListener('dragover', (e) => {
e.preventDefault();
});
最後にボタンからファイルを選択しても、ドラッグ&ドロップから選択してもファイル名がきちんと表示されるようにファイル名を表示する仕組みを作成します。
fileInput.addEventListener('change', () => {
var fileName = fileInput.files[0].name;
selectedFile.classList.remove('no-active');
selectedFile.innerText = fileName;
});
javaScriptの全体像
const dropzone = document.getElementById('js-dropzone');
const overlayText = document.getElementById('js-overlay-text');
const overlayArea = document.getElementById('js-overlay-area');
const fileInput = document.getElementById('file_upload');
const selectedFile = document.getElementById('js-selected-file');
// ドロップ可能エリアに入った時
dropzone.addEventListener('dragenter', () => {
overlayArea.classList.add('overlay');
overlayText.classList.add('overlay-text');
overlayText.classList.remove('no-active');
});
// ドロップ可能エリアを出た時
overlayArea.addEventListener('dragleave', () => {
overlayArea.classList.remove('overlay');
overlayText.classList.remove('overlay-text');
overlayText.classList.add('no-active');
});
// ドロップ可能エリアにカーソルがある時
overlayArea.addEventListener('dragover', (e) => {
e.preventDefault();
});
// ファイルをドロップした時
overlayArea.addEventListener('drop', (e) => {
e.preventDefault();
var fileName = e.dataTransfer.files[0].name;
selectedFile.innerText = fileName;
selectedFile.classList.remove('no-active');
overlayArea.classList.remove('overlay');
overlayText.classList.remove('overlay-text');
overlayText.classList.add('no-active');
});
fileInput.addEventListener('change', () => {
var fileName = fileInput.files[0].name;
selectedFile.classList.remove('no-active');
selectedFile.innerText = fileName;
});
これで完成です!
PHPやRubyなどのプログラミング言語と合わせて作成すると、ファイルを実際にサーバーに送受信し、色々処理を施したりなどすることが出来ます。是非試してみてください。
人気記事【独学者おすすめ】プログラミング学習動画サービス3選