【JavaScript】画像をドラッグ&ドロップで読み込む方法|ドロップゾーン・プレビュー・アップロード

画像ファイルをフォームの「参照」ボタンではなく、ドラッグ&ドロップで読み込みたい場面はよくあります。アップロードフォームやプレビュー機能で、ユーザー体験を大きく向上させられます。

HTML5の Drag and Drop API と File API を組み合わせれば、ドロップされた画像ファイルを取得してプレビュー表示・検証・アップロードまで行えます。この記事では最小構成から実用的な実装まで、つまずきやすいポイントとあわせて解説します。

この記事の最重要ポイント:ドロップを受け付けるには dragover イベントで e.preventDefault() を必ず呼ぶ必要があります。これを忘れると drop イベントが発火せず、ブラウザが画像を別タブで開いてしまいます。
スポンサーリンク

ドロップゾーンを作る(基本)

まずドロップを受け付ける領域を用意します。dragoverpreventDefault() を呼ぶことで、その要素がドロップ可能になります。

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.filesFile オブジェクトの一覧が入っています。画像以外も落とせるので、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 に対しても dragoverdrope.preventDefault() を設定しておくとよいでしょう。

プレビュー方法の比較

方法 速度・メモリ 用途
URL.createObjectURL() 高速・省メモリ プレビュー全般(推奨)
FileReader(data URL) やや重い base64文字列が必要なとき

よくある質問(FAQ)

Qドロップしても drop イベントが発火しません。
Adragover イベントで e.preventDefault() を呼んでいないことが原因です。ブラウザのデフォルト動作(ファイルを開く)が優先され、drop が発火しません。dragover ハンドラ内で必ず e.preventDefault() を呼んでください。
Qドロップされたファイルはどこで取得できますか?
Adrop イベントの e.dataTransfer.filesFileList)に入っています。[...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で画像をドラッグ&ドロップで読み込む方法のポイントを整理します。

  • dragovere.preventDefault() を必ず呼ぶ(忘れると drop が発火しない)
  • ドロップされたファイルは e.dataTransfer.files、画像判定は file.type.startsWith("image/")
  • プレビューは URL.createObjectURL() が推奨(使用後 revokeObjectURL
  • 形式・サイズを検証してからアップロード(FormDatafetch
  • ドラッグ中のハイライトや、ページ全体での誤ドロップ防止も入れると親切

関連として、画像が読み込まれたかを判定する方法画像を遅延読み込みする方法ファイルのダウンロードボタンを追加する方法もあわせて確認すると、画像・ファイル操作に強くなれます。