ファイルアップロードとは

ファイルをドラッグ&ドロップするか、クリックして選ぶための領域。選んだファイル名とサイズを一覧表示する。

概要

ファイルをドラッグ&ドロップするか、クリックして選択ダイアログから選ぶための領域です。<input type="file"> を <label> で囲んでおくと、キーボードでも Tab で入力欄にフォーカスし、Enter や Space で選択ダイアログを開けます。ドラッグ中は領域の見た目を変えて「ここに置けます」と分かるようにし、ドロップまたは選択が完了したら、ファイル名とサイズを一覧で表示します。この部品自体はファイルを選ぶところまでを担い、実際にサーバーへ送信する処理は別に用意します。

別名

表記ゆれ:ドラッグ&ドロップアップロード、ドロップゾーン、file upload

「ドロップゾーン」はドラッグ&ドロップを受け付ける領域そのものを指す呼び方で、クリックによる選択も兼ねるのが一般的な「ファイルアップロード」の部品と、指すものはほぼ同じです。

AI への伝え方

「ファイルを複数選べるドラッグ&ドロップのアップロード領域を作って。ドラッグ中はハイライトして、選んだファイル名とサイズを一覧に出して。アップロード自体はまだしなくていい」のように、複数選択の可否と、アップロード処理まで作るかどうかを伝えます。

デモ

    コード

    HTML
    <div class="file-upload-demo">
      <label class="file-upload-demo__dropzone" for="file-upload-demo-input">
        <svg class="file-upload-demo__icon" viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
          <path d="M12 16V4"></path>
          <path d="M7 9l5-5 5 5"></path>
          <path d="M4 16v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3"></path>
        </svg>
        <span class="file-upload-demo__text">ここにファイルをドラッグ、またはクリックして選択</span>
        <input class="file-upload-demo__input" type="file" id="file-upload-demo-input" multiple />
      </label>
    
      <ul class="file-upload-demo__list"></ul>
    </div>
    CSS
    .file-upload-demo {
      font-family: sans-serif;
      max-width: 320px;
    }
    
    .file-upload-demo__dropzone {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 10px;
      padding: 28px 16px;
      border: 2px dashed #b3b3b3;
      border-radius: 12px;
      color: #666;
      text-align: center;
      cursor: pointer;
      transition: border-color 0.15s ease, background-color 0.15s ease;
    }
    
    .file-upload-demo__dropzone:hover {
      border-color: #666;
    }
    
    .file-upload-demo__dropzone:has(.file-upload-demo__input:focus-visible) {
      outline: 2px solid #0017c1;
      outline-offset: 2px;
    }
    
    .file-upload-demo__dropzone--active {
      border-color: #0017c1;
      background: #f2f2f2;
      color: #0017c1;
    }
    
    .file-upload-demo__icon {
      color: #0017c1;
    }
    
    .file-upload-demo__text {
      font-size: 13px;
    }
    
    .file-upload-demo__input {
      margin-top: 4px;
      font-size: 12px;
    }
    
    .file-upload-demo__list {
      margin: 14px 0 0;
      padding: 0;
      list-style: none;
      font-size: 13px;
      color: #333;
    }
    
    .file-upload-demo__file {
      padding: 6px 0;
      border-bottom: 1px solid #e6e6e6;
    }
    
    @media (prefers-reduced-motion: reduce) {
      .file-upload-demo__dropzone {
        transition: none;
      }
    }
    JS
    const fileUploadDemoDropzone = document.querySelector(".file-upload-demo__dropzone");
    const fileUploadDemoInput = document.getElementById("file-upload-demo-input");
    const fileUploadDemoList = document.querySelector(".file-upload-demo__list");
    
    function fileUploadDemoFormatSize(bytes) {
      if (bytes < 1024) return `${bytes}B`;
      if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
      return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
    }
    
    function fileUploadDemoRenderFiles(files) {
      fileUploadDemoList.innerHTML = "";
      Array.from(files).forEach((file) => {
        const li = document.createElement("li");
        li.className = "file-upload-demo__file";
        li.textContent = `${file.name}(${fileUploadDemoFormatSize(file.size)})`;
        fileUploadDemoList.appendChild(li);
      });
    }
    
    fileUploadDemoInput.addEventListener("change", () => {
      fileUploadDemoRenderFiles(fileUploadDemoInput.files);
    });
    
    ["dragenter", "dragover"].forEach((eventName) => {
      fileUploadDemoDropzone.addEventListener(eventName, (event) => {
        event.preventDefault();
        fileUploadDemoDropzone.classList.add("file-upload-demo__dropzone--active");
      });
    });
    
    ["dragleave", "drop"].forEach((eventName) => {
      fileUploadDemoDropzone.addEventListener(eventName, (event) => {
        event.preventDefault();
        fileUploadDemoDropzone.classList.remove("file-upload-demo__dropzone--active");
      });
    });
    
    fileUploadDemoDropzone.addEventListener("drop", (event) => {
      const files = event.dataTransfer?.files;
      if (files && files.length) {
        fileUploadDemoInput.files = files;
        fileUploadDemoRenderFiles(files);
      }
    });