Chrome ドラックドロップでファイル選択
以前、スマホからPCへファイルを送信するWebアプリを作成しました。その際は、スマホ操作がメインでドラッグ&ドロップは考えていませんでしたが、PCでファイルを選択するにはドラッグ&ドロップでファイルが選択できると楽なので対応してみることにしました。
しかし、近年のGoogleChromeではデスクトップからブラウザへファイルをドラッグ&ドロップすると、新しいタブでファイルをオープンする挙動になっていると思います。
おそらくそのせいだと思いますが、筆者の環境(Windows11+Chrome106.0.5249.103)では、タグにondropイベントを設定してもイベントが反応しません。
同環境で回避策はその方法はないか考えてみました。
fileを指定したinput要素なら意図した反応をします
よく考えれば当たり前なのかもしれませんが、input type="file"の領域にドロップすると、ファイルはオープンされずに選択されます。また、この時ondropイベントも反応がありました。
sample1.html
<div style="width: 98%; border: 1px solid blue; padding: 10px" draggable="true" ondrop="console.log('dorp to div'); event.preventDefault();">
<-- 本来ドラッグ&ドロップ用の領域 -->
</div>
sample2.html
<input type="file" multiple ondrop="console.log('drop to input');" style="background-color: #ccf; display: block; width:98%; height: 3rem;"/>
typeがfileのinput要素の上ならドロップがファイル選択として機能し、ondropイベントも稼働します。(ボタンの上か青色の場所)
装飾
通常のinput type="file"の場合でも、そのデザイン性が微妙だという理由から、hiddenで隠しておいて、別の要素からclickイベントを起こすというような使い方がされます。
筆者はデザインが苦手なので見た目の微妙さは変わらないかもしれませんが、元のボタンや表示を隠す方法を考えてみました。
といっても、方法はinput type="file"を透明にして、その上に文字を出力するようにするだけです。
関連するセキュリティホールが発見されたりすると使えなくなったりするのかもしれませんが、執筆時点の環境では意図したように稼働させることができました。
sample3.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ドラック&ドロップのサンプル</title>
</head>
<body>
<div style="width: 100%; height: 100px; border: 1px solid blue; padding: 0; position: relative;">
<div style="position: absolute; text-align: center; width: 100%">
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48">
<path
d="M22.6 37.9h3V27.85l4.1 4.1 2.1-2.1-7.8-7.6-7.7 7.7 2.1 2.1 4.2-4.2ZM11 44q-1.2 0-2.1-.9Q8 42.2 8 41V7q0-1.2.9-2.1Q9.8 4 11 4h18.05L40 14.95V41q0 1.2-.9 2.1-.9.9-2.1.9Zm16.55-27.7V7H11v34h26V16.3ZM11 7v9.3V7v34V7Z"
/>
</svg>
<br />
<span>ここにファイルをドロップしてください</span>
<ul style="list-style: none" id="file-list">
<li>ファイルが選択されていません</li>
</ul>
</div>
<input
id="file"
type="file"
multiple
ondrop="console.log('drop to input')"
style="
background-color: #ccf;
display: block;
width: 100%;
height: 100%;
opacity: 0;
"
/>
</div>
<script>
const file = document.getElementById("file");
const list = document.getElementById("file-list");
const adjustArea = () => {
// ドラッグドロップの範囲の調整
const last = list.querySelectorAll("li");
if (0 < last.length) {
const liBottom = last[last.length - 1].getBoundingClientRect().bottom;
const fileRect = file.getBoundingClientRect();
let targetHeight =
fileRect.bottom - fileRect.top + (liBottom - fileRect.bottom) + 5;
file.style.height = targetHeight + "px";
file.parentNode.style.height = targetHeight + "px";
}
};
window.addEventListener("load", () => {
file.addEventListener("change", () => {
// リストクリア
while (list.hasChildNodes()) {
list.removeChild(list.firstChild);
}
if (file.files.length == 0) {
const li = document.createElement("li");
li.textContent = "ファイルが選択されていません";
list.appendChild(li);
} else {
for (const f of file.files) {
const li = document.createElement("li");
li.textContent = f.name;
list.appendChild(li);
}
}
// 高さの調整
setTimeout(adjustArea(), 100);
});
// 高さの調整
setTimeout(adjustArea(), 100);
});
</script>
</body>
</html>
上記のコードを稼働させると次のようになります。
ここにファイルをドロップしてください
- ファイルが選択されていません