2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Javascript】フレームワークを使わずにドラッグアンドドロップでjsonを読み込む

Last updated at Posted at 2020-07-12

TL;DR

記事末のソースを見てね!

やりたいこと

Github Pagesで、静的ページだけどドラッグアンドドロップでjsonファイルを読み込みたかった。

ポイント

1. ブラウザデフォルトのドラッグアンドドロップ関係のイベント処理を止める

ブラウザにファイルをドラッグアンドドロップすると、ドロップしたファイルを読み込んでブラウザの機能としてプレビューする、またはダウンロードしてしまいます。まずはこれを止めます。

Javascript
const drop_area = document.getElementById("drop_area");

// リストのイベントすべてに同じ処理をセットする
["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave"].forEach(event => {
  drop_area.addEventListener(event, (e) => {
    // ブラウザデフォルトのイベント処理を停止
    e.preventDefault();
    e.stopPropagation();
  });
});

2. ドロップイベントがあったとき非同期通信でGETする

Github PagesはPOSTメソッドを受け付けていないので(ステータスコード405が帰ってくる)、GETメソッドで行きます。

Javascript
function handleDropAction(e) {
  // ブラウザデフォルトのイベント処理を停止
  e.preventDefault();
  e.stopPropagation();

  const droppedFiles = e.dataTransfer.files; // ドロップしたファイルを取得

  // ファイルが一つ以上ドロップされたら
  if (droppedFiles.length > 0) {
    const ajaxData = new FormData();
    const ajax = new XMLHttpRequest();
    const file = droppedFiles[0]; // 2つ目以降のファイルを今回は無視する

    ajaxData.append("file", file); // 非同期通信で送信するデータにドロップしたファイル情報を追記
    ajax.open("GET", "index.html");  // 非同期通信の準備。普通のサーバならPOSTでも可
    
    // 送信が終わったときの処理
    ajax.onload = (e) => {
      if (ajax.status >= 200 && ajax.status < 400) { // 送信に成功したら読み込む
        const fr = new FileReader();

        // ファイルの読み込みが終わったときの処理
        fr.onload = () => {
          document.getElementById("viewer").innerText = fr.result;
        };
        fr.readAsText(file); // ドロップしたファイルをテキストとして読み込み
      }
    }
    ajax.send(ajaxData); // ドロップしたファイルを表示中のページに送信する
  }
}

// あらかじめ定義しておく
// const drop_area = document.getElementById("drop_area");

// id="drop_area"要素にドロップしたとき呼び出す関数を関連付け
drop_area.addEventListener("drop", handleDropAction); 

注意

外部ファイルの読み込みになるので、file://で表されるURLではオリジン間リソース共有(CORS)扱いになってエラーが出て、うまく処理されません。

ローカルで試す場合は、PHPやPython、Rubyなどで簡易的にHTTPサーバを立てるとよいでしょう。

shell
# PHPでHTTPサーバを立てる例
cd /path/to/src/ # index.html のあるディレクトリに移動しておく
php -S localhost:8080  # http://localhost:8080/ にアクセスするとうまく動く

ソースコードまとめ

XMLHttpRequest版

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sample</title>
</head>
<body>
  <div id="drop_area" style="width:100%; height:100%;">drop area</div>
  <div id="viewer"></div>
</body>
<script>
  function handleDropAction(e) {
    // ブラウザデフォルトのイベント処理を停止
    e.preventDefault();
    e.stopPropagation();

    const droppedFiles = e.dataTransfer.files; // the files that were dropped

    if (droppedFiles.length > 0) {
      const ajaxData = new FormData();
      const ajax = new XMLHttpRequest();
      const file = droppedFiles[0];

      ajaxData.append("file", file);
      ajax.open("GET", "index.html");  // POSTでも可
      ajax.onload = (e) => {
        if (ajax.status >= 200 && ajax.status < 400) {
          const fr = new FileReader();
          fr.onload = () => {
            document.getElementById("viewer").innerText = fr.result;
          };
          fr.readAsText(file);
        }
      }
      ajax.send(ajaxData);    
    }
  }

  const drop_area = document.getElementById("drop_area");
  ["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave"].forEach(event => {
    drop_area.addEventListener(event, (e) => {
      // ブラウザデフォルトのイベント処理を停止
      e.preventDefault();
      e.stopPropagation();
    });
  });
  drop_area.addEventListener("drop", handleDropAction);
</script>
</html>

fetch版

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sample</title>
</head>
<body>
  <div id="drop_area" style="width:100%; height:100%;">drop area</div>
  <div id="viewer"></div>
</body>
<script>
  function handleDropAction(e) {
    // ブラウザデフォルトのイベント処理を停止
    e.preventDefault();
    e.stopPropagation();

    const droppedFiles = e.dataTransfer.files; // the files that were dropped

    if (droppedFiles.length > 0) {
      const ajaxData = new FormData();
      const file = droppedFiles[0];

      ajaxData.append("file", file);
      fetch("index.html", {

      })
      .then(response => {
        const fr = new FileReader();
        fr.onload = () => {
          document.getElementById("viewer").innerText = fr.result;
        };
        fr.readAsText(file);
      })
      .catch(err => {
        document.getElementById("viewer").innerText = JSON.stringify(err);
      });
    }
  }

  const drop_area = document.getElementById("drop_area");
  ["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave"].forEach(event => {
    drop_area.addEventListener(event, (e) => {
      // ブラウザデフォルトのイベント処理を停止
      e.preventDefault();
      e.stopPropagation();
    });
  });
  drop_area.addEventListener("drop", handleDropAction);
</script>
</html>
2
4
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
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?