0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WebAPIのDOM操作 FileList・DataTransfer

0
Posted at

背景

ファイルアップロードのUIを触っていて、<input type="file"> から取れる files の中身が配列っぽく見えるのに配列メソッドが使えなくてハマった。この files の正体である FileList と、その制約を回避するための DataTransfer について整理しておく

FileList

FileList は HTML の <input> 要素の files プロパティが返すオブジェクトで、選択されたファイルの一覧を表す。

<input type="file" id="fileInput" multiple>

(ここにファイル選択UIのスクリーンショット)

このinputで選択したファイルの一覧が FileList として返ってくる。

FileList {0: File, 1: File, 2: File, length: 3}
  0: File { name: 'dev.jpg',         size: 59665,   lastModified: 1764643177551, ... }
  1: File { name: 'football.jpg',    size: 1012021, lastModified: 1764642516495, ... }
  2: File { name: 'sample-gif.webp', size: 471912,  lastModified: 1764656221369, ... }
  length: 3
  [[Prototype]]: FileList

できること

  • length でファイル件数を取得できる
  • files[0], files[1] のようにインデックスで指定のファイルを参照できる(※ ただし配列ではない)

できないこと

  • 配列ではないので push(), pop() などの配列メソッドは使えない
input.files.push is not a function
    at HTMLInputElement.<anonymous>

FileList は「配列のように見えるが配列ではない」オブジェクト(Array-likeなオブジェクト)だと押さえておくとよい。

DataTransfer

DataTransfer は、コンテキスト間で転送されるデータ(ドラッグ&ドロップ操作やクリップボードの読み書きなど)を保持するためのオブジェクト。データを一時的に入れて運ぶ「箱」のような役割をする。

主な用途は次の2つ。

  • ドラッグ&ドロップ: デスクトップの画像ファイルをWeb画面上にマウスで引っ張ってきて(ドラッグ)、離して(ドロップ)アップロードする時
  • コピー&ペースト: ある場所でテキストや画像をコピーして、別の入力欄に貼り付ける時

FileListの制約をDataTransferで回避する

FileList 単体ではできなかった「ファイルの追加・削除」が、DataTransfer を経由するとできる。DataTransferitems に対して add() でファイルを積み、その結果できあがる dt.files(これも FileList)を input.files に代入し直す、という流れになる。

<input type="file" id="fileInput" multiple>
<script>
  document.getElementById('fileInput').addEventListener('change', function () {
    const input = this

    console.log('--- 選択直後の FileList ---')
    console.log('件数:', input.files.length)
    for (let i = 0; i < input.files.length; i++) {
      console.log(i + '番目:', input.files[i].name)
    }

    console.log('--- DataTransfer で作り直す ---')
    const dt = new DataTransfer()
    dt.items.add(input.files[0])
    dt.items.add(input.files[1])
    for (let i = 0; i < dt.items.length; i++) {
      console.log(dt.items[i].getAsFile().name)
    }

    // input.files に代入し直すと、選択状態そのものを差し替えられる
    // input.files = dt.files
    // console.log('--- 差し替え後の FileList ---')
    // console.log('件数:', input.files.length)
    // console.log('残ったファイル:', input.files[0].name)
  })
</script>

DataTransfer を使うと配列操作的なことができるが、dt.files 自体はあくまで FileList なので、length は使えても forEach などの配列メソッドは使えない点は変わらない。

まとめ

項目 FileList DataTransfer
正体 input.files が返すファイル一覧オブジェクト データを一時的に運ぶ「箱」
配列か 配列ではない(Array-like) items / files を持つ
length 使える items.length が使える
インデックス参照 files[0] で可 items[0].getAsFile() で可
ファイルの追加・削除 ❌ できない items.add() で可
forEach などの配列メソッド ❌ 使えない dt.files も FileList なので使えない

ファイルの追加・削除をしたい時は、DataTransfer で新しい FileList を組み立てて input.files に代入し直す、というのが定石。

感想

  • ドラックアンドドロップ、コピペをよく考えたら保存する場所必要だよね
  • DOM操作まだまだ弱いなぁ
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?