画像ファイルをフォームの「参照」ボタンではなく、ドラッグ&ドロップで読み込みたい場面はよくあります。アップロードフォームやプレビュー機能で、ユーザー体験を大きく向上させられます。
HTML5の Drag and Drop API と File API を組み合わせれば、ドロップされた画像ファイルを取得してプレビュー表示・検証・アップロードまで行えます。この記事では最小構成から実用的な実装まで、つまずきやすいポイントとあわせて解説します。
この記事の最重要ポイント:ドロップを受け付けるには
dragover イベントで e.preventDefault() を必ず呼ぶ必要があります。これを忘れると drop イベントが発火せず、ブラウザが画像を別タブで開いてしまいます。ドロップゾーンを作る(基本)
まずドロップを受け付ける領域を用意します。dragover で preventDefault() を呼ぶことで、その要素がドロップ可能になります。
HTML
<div id="dropzone">ここに画像をドロップ</div> <div id="preview"></div>
ドロップゾーンの基本
const dropzone = document.getElementById("dropzone");
// ★これが必須。dragover で preventDefault しないと drop が発火しない
dropzone.addEventListener("dragover", (e) => {
e.preventDefault();
});
dropzone.addEventListener("drop", (e) => {
e.preventDefault(); // ブラウザが画像を開くのを防ぐ
const files = [...e.dataTransfer.files]; // ドロップされたファイル一覧
console.log(files); // File オブジェクトの配列
});
ドロップされた画像ファイルを取得する
e.dataTransfer.files に File オブジェクトの一覧が入っています。画像以外も落とせるので、file.type で画像だけに絞り込みます。
画像ファイルだけ抽出
dropzone.addEventListener("drop", (e) => {
e.preventDefault();
const files = [...e.dataTransfer.files];
// MIMEタイプが image/ で始まるものだけ
const images = files.filter((file) => file.type.startsWith("image/"));
if (images.length === 0) {
console.log("画像ファイルではありません");
return;
}
images.forEach(showPreview); // 次のプレビュー関数へ
});
画像をプレビュー表示する
読み込んだ画像を表示するには2つの方法があります。URL.createObjectURL() が高速でメモリ効率も良く推奨です。
URL.createObjectURL でプレビュー(推奨)
const preview = document.getElementById("preview");
function showPreview(file) {
const img = document.createElement("img");
img.src = URL.createObjectURL(file); // ファイルから一時URLを生成
img.style.maxWidth = "200px";
// 表示後にメモリを解放する
img.onload = () => URL.revokeObjectURL(img.src);
preview.appendChild(img);
}
FileReader でプレビュー(data URL)
function showPreviewWithReader(file) {
const reader = new FileReader();
reader.onload = (e) => {
const img = document.createElement("img");
img.src = e.target.result; // data URL(base64)
preview.appendChild(img);
};
reader.readAsDataURL(file);
}
URL.createObjectURL() は軽量な一時URL(blob:)を作るだけなので大きい画像でも高速です。使い終わったら URL.revokeObjectURL() でメモリを解放しましょう。FileReader はファイル全体をbase64文字列に変換するため、巨大な画像ではメモリを多く使います。ドラッグ中のハイライトと複数画像対応
ドラッグ中にドロップゾーンの見た目を変えると、操作がわかりやすくなります。dragenter/dragleave でクラスを付け外しします。
ハイライト+複数画像
dropzone.addEventListener("dragenter", (e) => {
e.preventDefault();
dropzone.classList.add("dragover");
});
dropzone.addEventListener("dragover", (e) => e.preventDefault());
dropzone.addEventListener("dragleave", () => {
dropzone.classList.remove("dragover");
});
dropzone.addEventListener("drop", (e) => {
e.preventDefault();
dropzone.classList.remove("dragover");
// 複数ファイルをまとめて処理
[...e.dataTransfer.files]
.filter((f) => f.type.startsWith("image/"))
.forEach(showPreview);
});
ハイライト用CSS
#dropzone {
border: 2px dashed #94a3b8;
padding: 2em;
text-align: center;
}
#dropzone.dragover {
border-color: #0284c7;
background: #f0f9ff;
}
形式・サイズを検証する
アップロード前に、許可する画像形式とファイルサイズをチェックします。
画像の検証
function isValidImage(file, maxMB = 5) {
const allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
const okType = allowedTypes.includes(file.type);
const okSize = file.size <= maxMB * 1024 * 1024;
if (!okType) console.log("対応していない形式です:", file.type);
if (!okSize) console.log("ファイルが大きすぎます:", file.size);
return okType && okSize;
}
// 使用例:検証を通った画像だけプレビュー
[...e.dataTransfer.files].filter(isValidImage).forEach(showPreview);
サーバーへアップロードする
取得した画像ファイルは FormData に入れて fetch で送信できます。
FormData でアップロード
async function uploadImage(file) {
const formData = new FormData();
formData.append("image", file);
const res = await fetch("/upload", {
method: "POST",
body: formData, // Content-Type は自動設定される
});
return res.json();
}
ページ全体での誤ドロップに注意:ドロップゾーン以外に画像を落とすと、ブラウザがその画像を開いてしまいます。これを防ぐには
window に対しても dragover・drop で e.preventDefault() を設定しておくとよいでしょう。プレビュー方法の比較
| 方法 | 速度・メモリ | 用途 |
|---|---|---|
URL.createObjectURL() |
高速・省メモリ | プレビュー全般(推奨) |
FileReader(data URL) |
やや重い | base64文字列が必要なとき |
よくある質問(FAQ)
Qドロップしても drop イベントが発火しません。
A
dragover イベントで e.preventDefault() を呼んでいないことが原因です。ブラウザのデフォルト動作(ファイルを開く)が優先され、drop が発火しません。dragover ハンドラ内で必ず e.preventDefault() を呼んでください。Qドロップされたファイルはどこで取得できますか?
A
drop イベントの e.dataTransfer.files(FileList)に入っています。[...e.dataTransfer.files] で配列化し、file.type.startsWith("image/") で画像だけに絞り込めます。QcreateObjectURL と FileReader はどちらを使うべき?
Aプレビュー表示なら
URL.createObjectURL() が高速・省メモリで推奨です。使用後は URL.revokeObjectURL() で解放します。base64のdata URLが必要な場合(そのまま保存・送信したい等)は FileReader.readAsDataURL() を使います。Qスマホ(タッチ)でもドラッグ&ドロップできますか?
AHTML5のDrag and Drop APIはモバイルで動作しないことがあります。スマホ対応が必要なら
<input type="file" accept="image/*"> による選択を併用するか、タッチイベント対応のライブラリ(SortableJS 等)を使うのが現実的です。まとめ
JavaScriptで画像をドラッグ&ドロップで読み込む方法のポイントを整理します。
dragoverでe.preventDefault()を必ず呼ぶ(忘れると drop が発火しない)- ドロップされたファイルは
e.dataTransfer.files、画像判定はfile.type.startsWith("image/") - プレビューは
URL.createObjectURL()が推奨(使用後revokeObjectURL) - 形式・サイズを検証してからアップロード(
FormData+fetch) - ドラッグ中のハイライトや、ページ全体での誤ドロップ防止も入れると親切
関連として、画像が読み込まれたかを判定する方法・画像を遅延読み込みする方法・ファイルのダウンロードボタンを追加する方法もあわせて確認すると、画像・ファイル操作に強くなれます。
