JavaScriptで画像をドロップして画面に表示する(ondrop, ondragover)

JavaScript

JavaScripでondropondragoverを使用した時にちょっとハマったので、今回はこの2つのイベントについてまとめてみました。

最初にJavaScriptのondropondragoverのイベントの説明を簡単にします。
画像をドロップして、画面に表示する簡単なサンプルコードを作成したので、それを元にコードの解説しています。

ondrop, ondragoverイベントとは?

ondropイベントは要素に指定すると、その要素に対してファイルなどをドロップしたときに発生するイベントです。

ondragoverイベントは指定した要素の上に、ファイルなどをドラッグした時に発生するイベントです。
ドラッグ中は数百ミリ秒間隔で、ずっと発生します。

それぞれのイベントについては、MDNのこちらに詳しく書いてあります。
HTML ドラッグ&ドロップ API

注意する点として、この2つのイベントは一緒に使わないと、どちらも動きません。
`ondrop`だけで動くかと思っていましたが、使ってみると要素に対して、`ondragover`も設定していないとイベントがおきませんでした。

画像をドロップして表示するサンプルコード

画像ファイルを画面の特定の位置にドロップして、そのまま画面の下部に表示するようなものを作成してみました。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>drag on drop</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script>
      function fileDragOver(e) {
        e.preventDefault()
      }
      function fileDrop(e) {
        e.preventDefault()
        const files = e.dataTransfer.files
        for (var i = 0; i < files.length; i++) {
          const file = files[i]

          const reader = new FileReader()
          reader.onload = (event) => {
            const base64Text = event.currentTarget.result

            document.querySelector('#uploadImageArea').innerHTML += `<img src="${base64Text}" width="20%" />`
          }
          reader.readAsDataURL(file)
        }
      }
    </script>
  </head>
  <body>
    <nav class="navbar navbar-dark bg-dark mb-3">
      <a class="navbar-brand ms-3" href="#">Drag On Drop</a>
    </nav>
    <div class="container">
      <form>
        <div ondrop="fileDrop(event)" ondragover="fileDragOver(event)" class="mb-3 d-flex justify-content-center border rounded">
          <div class="p-5">
            ここにドラッグ&ドロップ
          </div>
        </div>
        <div id="uploadImageArea" class="d-flex flex-wrap"></div>
      </form>
    </div>
  </body>
</html>

解説

HTMLタグから見ていきます。
divタグに対して、ondropondragoverを設定して、ドラッグ&ドロップするエリアを下記のように作成しています。

<div ondrop="fileDrop(event)" ondragover="fileDragOver(event)" class="mb-3 d-flex justify-content-center border rounded">
  <div class="p-5">
    ここにドラッグ&ドロップ
  </div>
</div>

このエリアにファイルをドロップすると、ondropondragoverのイベントが発生します。
ファイルをエリアに持っていった時に、マウスポインタがエリアにある間はondragoverがずっと発生して、ファイルを落とした時にondropが発生します。

ondropイベントの時にはfileDrop関数を呼び、ondragoverの時にはfileDragOver関数を呼んでいます。
それぞれの関数が呼ばれた時には、引数としてDragEventオブジェクトが渡されます。

次にスクリプトを見ていきます。
まず最初に、それぞれの関数でpreventDefaultメソッドを呼ぶようにしています。

e.preventDefault()

これを行うことで、画像ファイルをドラッグ&ドロップした場合に、ブラウザ標準の画像をそのまま開くという挙動を抑えています。

fileDrop関数はファイルをドロップした時に動きます。
preventDefaultを呼んだ後は、ファイルを取得して、画面に表示するということを行っています。

DragEventオブジェクトのdataTransfer.filesで、ドロップされたファイルがFileListオブジェクト形式で取得できます。
内容をfiles変数に入れています。

const files = e.dataTransfer.files

ドロップされたファイルの数はlengthを調べるとわかります。
for文を使用して、下記のようにすることで、全てのファイルを取得することが可能です。
1個だけしか処理しない場合は、e.dataTransfer.files[0]として、取得しても良いかもしれません。

for (var i = 0; i < files.length; i++) {
  const file = files[i]

  const reader = new FileReader()
  reader.onload = (event) => {
    const base64Text = event.currentTarget.result

    document.querySelector('#uploadImageArea').innerHTML += `<img src="${base64Text}" width="20%" />`
  }
  reader.readAsDataURL(file)
}

for文の中では、最初にfilesの要素番号を指定して、ファイルを取得しています。
その後に、FileReaderを使用して、ファイルの内容を読み込んでいます。

reader.readAsDataURLは非同期でファイルの内容を読みます。
読み終わると、reader.onloadに設定した関数が動きます。

base64の画像文字列形式で内容を取得して、画像タグを#uploadImageAreaがついているdivタグ内に展開しています。
base64で画像を取得するところについては、下記に詳しく書いたので確認してみてください。
JavaScriptで画像ファイルをbase64文字列に変換する方法!
今回はWebフォームで画像ファイルを選択した後に、JavaScriptを使用してBase64形式に変換する方法を書いています。 実際に画像をフォームに設定して変換する、簡単なサンプルを作ってみました。 JavaScriptで画像をbase6...

動作確認をする

実際に作ったものを動かすと、下記のようになります。
JavaScriptでondrop / ondragoverの検証
画像を選択して、ドロップすると画面に表示されることが確認できました。

ドロップした後にアップロードしたい

ドロップした後にファイルをアップロードしたい場合は、今回取得したFileListオブジェクトを下記のようなinputタグに設定して、送信するといいです。

<input type="file" name="file[]" multiple />

jQueryを使っていますが、アップロードまで検証した記事が下記になります。
ドラッグ&ドロップでマルチファイルアップロードのサンプルと懸念事項

ajaxを使用して、アップロードしたい場合には、今回取得したbase64文字列の画像をjsonに載せて送信すると良いです。

コメント

タイトルとURLをコピーしました