Skip to content

Commit 2f90318

Browse files
authored
Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop を更新 (#30622)
2025/10/02 時点の英語版に基づき更新
1 parent 5226b05 commit 2f90318

File tree

1 file changed

+153
-59
lines changed
  • files/ja/web/api/html_drag_and_drop_api/file_drag_and_drop

1 file changed

+153
-59
lines changed

files/ja/web/api/html_drag_and_drop_api/file_drag_and_drop/index.md

Lines changed: 153 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,99 +2,193 @@
22
title: ファイルのドラッグ & ドロップ
33
slug: Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
44
l10n:
5-
sourceCommit: 0230ecc4418a1e52bca6b4d03c4eb794f90d04f1
5+
sourceCommit: 8285d415db211ae9efe04752d9dab1b574450ee8
66
---
77

88
{{DefaultAPISidebar("HTML Drag and Drop API")}}
99

10-
HTML ドラッグ & ドロップインターフェイスは、ウェブアプリケーションでファイルをウェブページにドラッグ & ドロップできるようにするものです。この記事では、基礎となるプラットフォームのファイルマネージャーからドラッグされ、ウェブページにドロップされた 1 つまたは複数のファイルを、アプリケーションで受け入れる方法について記述しています
10+
[ランディングページ](/ja/docs/Web/API/HTML_Drag_and_Drop_API#概念と使用法)で述べたように、ドラッグ&ドロップ API は、ページ内での要素のドラッグ、ページからのデータのドラッグ、ページへのデータのドラッグの 3 つの用途を同時にモデル化します。このチュートリアルでは、 3 つ目の用途である「ページへのデータのドラッグ」を実演します。ユーザーがオペレーティングシステムのファイルエクスプローラーから画像ファイルをドロップし、ページ上に表示できる基本的なドロップゾーンを実装します。ドラッグ&ドロップが利用できない、または利用したくないユーザー向けに、`<input>` 要素によるファイル選択の代替機能も提供します
1111

12-
ドラッグ & ドロップの主な手順は、ドロップゾーン(ファイルドロップの対象要素)を定義することと、 {{domxref("HTMLElement/drop_event", "drop")}} および {{domxref("HTMLElement/dragover_event", "dragover")}} イベントのイベントハンドラーを定義することです。これらの手順は、コード例を含め、下記で記述します。完全なソースコードは [MDN のドラッグ & ドロップリポジトリー](https://github.com/mdn/dom-examples/tree/main/drag-and-drop)で利用できます(プルリクエストや issue を歓迎します)。
12+
## 基本的なページレイアウト
1313

14-
なお、 {{domxref("HTML_Drag_and_Drop_API","HTML ドラッグ & ドロップ", "", 1)}}では、ファイルのドラッグ & ドロップに対応するために 2 つの異なる形の API を定義しています。一方の API は {{domxref("DataTransfer")}} インターフェイスで、もう一方の API は {{domxref("DataTransferItem")}} と {{domxref("DataTransferItemList")}} インターフェイスです。この例では、両方の API の使用方法を説明します (そして、Gecko 固有のインターフェイスは一切使用しません)
14+
通常の `<input>` によるファイル選択も許可したいので、ドロップゾーンは `<input>` 要素で実装するのが合理的です。これにより、ドラッグでの投入とクリック操作を同時に行うことができます。一般的な手法として、`<input>` を非表示にし、代わりにスタイル設定が容易な {{HTMLElement("label")}} 要素と連動させます。また、ドロップされた画像のプレビュー表示用要素も追加します
1515

16-
## ドロップゾーンの定義
16+
```html live-sample___file-dnd
17+
<label id="drop-zone">
18+
ここに画像をドロップ、またはクリックしてアップロードしてください。
19+
<input type="file" id="file-input" multiple accept="image/*" />
20+
</label>
21+
<ul id="preview"></ul>
22+
<button id="clear-btn">クリア</button>
23+
```
1724

18-
{{domxref("HTMLElement/drop_event", "drop")}} イベントの対象となる要素には、 `ondrop` イベントハンドラーが必要です。以下のコードでは、 {{HTMLelement("div")}} 要素を使用してこの処理を行う方法を示しています
25+
ラベル要素をスタイル設定し、視覚的にドロップゾーンであることを示し、ファイル入力は非表示にします
1926

20-
```html
21-
<div id="drop_zone" ondrop="dropHandler(event);">
22-
<p>Drag one or more files to this <i>drop zone</i>.</p>
23-
</div>
24-
```
27+
```css live-sample___file-dnd
28+
body {
29+
font-family: "Arial", sans-serif;
30+
}
2531

26-
通常、アプリケーションでは、ドロップ対象の要素に {{domxref("HTMLElement/dragover_event", "dragover")}} イベントハンドラーを記述し、そのハンドラーによって、ブラウザーの既定のドラッグ動作をオフにします。このハンドラーを追加するには、 {{domxref("HTMLElement.dragover_event","ondragover")}} イベントハンドラーを記載する必要があります。
32+
#drop-zone {
33+
display: flex;
34+
align-items: center;
35+
justify-content: center;
36+
width: 500px;
37+
max-width: 100%;
38+
height: 200px;
39+
padding: 1em;
40+
border: 1px solid #cccccc;
41+
border-radius: 4px;
42+
color: slategray;
43+
cursor: pointer;
44+
}
2745

28-
```html
29-
<div
30-
id="drop_zone"
31-
ondrop="dropHandler(event);"
32-
ondragover="dragOverHandler(event);">
33-
<p>Drag one or more files to this <i>drop zone</i>.</p>
34-
</div>
35-
```
46+
#file-input {
47+
display: none;
48+
}
49+
50+
#preview {
51+
width: 500px;
52+
max-width: 100%;
53+
display: flex;
54+
flex-direction: column;
55+
gap: 0.5em;
56+
list-style: none;
57+
padding: 0;
58+
}
3659

37-
最後に、アプリケーションはドロップ先の要素のスタイルを設定して、要素がドロップゾーンであることを視覚的に示したい場合があります。この例では、 drop ドロップ先の要素は以下のスタイル設定を使用します。
60+
#preview li {
61+
display: flex;
62+
align-items: center;
63+
gap: 0.5em;
64+
margin: 0;
65+
width: 100%;
66+
height: 100px;
67+
}
3868

39-
```css
40-
#drop_zone {
41-
border: 5px solid blue;
42-
width: 200px;
69+
#preview img {
70+
width: 100px;
4371
height: 100px;
72+
object-fit: cover;
4473
}
4574
```
4675

47-
> [!NOTE]
48-
> {{domxref("HTMLElement/dragstart_event", "dragstart")}} および {{domxref("HTMLElement/dragend_event", "dragend")}} イベントは、 OS からブラウザーへファイルをドラッグしているときには発生しません。 OS のファイルがブラウザーへドラッグされてきたことを検出するには、 {{domxref("HTMLElement/dragenter_event", "dragenter")}} および {{domxref("HTMLElement/dragleave_event", "dragleave")}} を使用してください。
76+
`<label>` 要素と `<input>` 要素を使用することで、ファイル選択 UX の実装に追加の JavaScript は不要になります。次に、ファイルのドロップ操作と、ドロップされたファイルのその後の処理に焦点を当てます。
4977

50-
## ドロップの処理
78+
## ドロップターゲットの宣言
5179

52-
{{domxref("HTMLElement/drop_event", "drop")}} イベントは、ユーザーがファイルをドロップしたときに発行されます。以下のドロップハンドラーでは、ブラウザーが {{domxref("DataTransferItemList")}} インターフェイスを使用している場合は {{domxref("DataTransferItem.getAsFile","getAsFile()")}} メソッドを使用して各ファイルにアクセスし、そうでない場合は {{domxref("DataTransfer")}} インターフェイスの {{domxref("DataTransfer.files","files")}} プロパティを使用して各ファイルにアクセスしています
80+
ドロップターゲットは `<label>` 要素です。ターゲット要素として、 {{domxref("HTMLElement/drop_event", "drop")}} イベントを監視し、ドロップされたファイルを処理します
5381

54-
この例では、ドラッグしたそれぞれのファイル名をコンソールに書き出す方法を説明します。実際のアプリケーションでは、 {{domxref("File", "File API")}} を使用してファイルを処理したいかもしれません。
82+
```js live-sample___file-dnd
83+
const dropZone = document.getElementById("drop-zone");
5584

56-
この例では、ファイルでないドラッグアイテムは無視されることに注意してください。
85+
dropZone.addEventListener("drop", dropHandler);
86+
```
5787

58-
```js
59-
function dropHandler(ev) {
60-
console.log("File(s) dropped");
88+
ファイルドロップの場合、ファイルが有効なドロップ先へドロップされなくても、ブラウザーがデフォルトで処理(ファイルの開く/ダウンロードなど)を行うことがあります。この動作を防ぐため、`window``drop` イベントを監視し、キャンセルする必要があります。ファイルがドラッグされている場合のみイベントを処理するように注意します。リンクなど他の要素の場合は、デフォルトの動作をそのまま使用します。ドラッグされたアイテムが画像以外のファイルの場合、イベントは処理しますが、許可されていないことをユーザーにフィードバックします。
6189

62-
// 既定の動作で防ぐ(ファイルが開かれないようにする)
63-
ev.preventDefault();
90+
```js live-sample___file-dnd
91+
window.addEventListener("drop", (e) => {
92+
if ([...e.dataTransfer.items].some((item) => item.kind === "file")) {
93+
e.preventDefault();
94+
}
95+
});
96+
```
6497

65-
if (ev.dataTransfer.items) {
66-
// DataTransferItemList インターフェイスを使用して、ファイルにアクセスする
67-
[...ev.dataTransfer.items].forEach((item, i) => {
68-
// ドロップしたものがファイルでない場合は拒否する
69-
if (item.kind === "file") {
70-
const file = item.getAsFile();
71-
console.log(`… file[${i}].name = ${file.name}`);
72-
}
73-
});
74-
} else {
75-
// DataTransfer インターフェイスを使用してファイルにアクセスする
76-
[...ev.dataTransfer.files].forEach((file, i) => {
77-
console.log(`… file[${i}].name = ${file.name}`);
78-
});
98+
`drop` イベントを発生させるには、要素が {{domxref("HTMLElement/dragover_event", "dragover")}} イベントもキャンセルする必要があります。`window` 上で `drop` を監視しているため、`window` 全体に対する `dragover` イベントもキャンセルする必要があります。また、ファイルが画像でない場合や正しい場所にドラッグされていない場合、 {{domxref("DataTransfer.dropEffect")}} を `none` に設定します。
99+
100+
```js live-sample___file-dnd
101+
dropZone.addEventListener("dragover", (e) => {
102+
const fileItems = [...e.dataTransfer.items].filter(
103+
(item) => item.kind === "file",
104+
);
105+
if (fileItems.length > 0) {
106+
e.preventDefault();
107+
if (fileItems.some((item) => item.type.startsWith("image/"))) {
108+
e.dataTransfer.dropEffect = "copy";
109+
} else {
110+
e.dataTransfer.dropEffect = "none";
111+
}
79112
}
80-
}
113+
});
114+
115+
window.addEventListener("dragover", (e) => {
116+
const fileItems = [...e.dataTransfer.items].filter(
117+
(item) => item.kind === "file",
118+
);
119+
if (fileItems.length > 0) {
120+
e.preventDefault();
121+
if (!dropZone.contains(e.target)) {
122+
e.dataTransfer.dropEffect = "none";
123+
}
124+
}
125+
});
81126
```
82127

83-
## ブラウザー既定のドラッグ動作を阻止する
128+
> [!NOTE]
129+
> {{domxref("HTMLElement/dragstart_event", "dragstart")}} および {{domxref("HTMLElement/dragend_event", "dragend")}} イベントは、OS からブラウザーへファイルをドラッグする際に発生しません。OS のファイルがブラウザーにドラッグされたことを検出するには、 {{domxref("HTMLElement/dragenter_event", "dragenter")}} および {{domxref("HTMLElement/dragleave_event", "dragleave")}} を使用してください。
130+
> つまり、OS からファイルをドラッグする際には、{{domxref("DataTransfer.setDragImage","setDragImage()")}} を使用してカスタムのドラッグ画像やカーソルオーバーレイを適用することはできません。これは、ドラッグデータストアが {{domxref("HTMLElement/dragstart_event", "dragstart")}} イベント内でのみ変更可能であるためです。この制限は {{domxref("DataTransfer.setData","setData()")}} にも適用されます。
84131
85-
以下の {{domxref("HTMLElement/dragover_event", "dragover")}} イベントハンドラーは {{domxref("Event.preventDefault", "preventDefault()")}} を呼び出して、ブラウザーの既定のドラッグ & ドロップハンドラーをオフにしています。
132+
## ドロップの処理
86133

87-
```js
88-
function dragOverHandler(ev) {
89-
console.log("File(s) in drop zone");
134+
次に、各ファイルにアクセスするために {{domxref("DataTransferItem.getAsFile","getAsFile()")}} メソッドを使用して `dropHandler` を実装します。その後、アプリケーションは[ファイル API](/ja/docs/Web/API/File_API) を使用してこのファイルをどのように処理するかを決定できます。ここでは単にページ上に表示していますが、実際には最終的にサーバーへアップロードすることも必要になるでしょう。
135+
136+
```js live-sample___file-dnd
137+
const preview = document.getElementById("preview");
138+
139+
function displayImages(files) {
140+
for (const file of files) {
141+
if (file.type.startsWith("image/")) {
142+
const li = document.createElement("li");
143+
const img = document.createElement("img");
144+
img.src = URL.createObjectURL(file);
145+
img.alt = file.name;
146+
li.appendChild(img);
147+
li.appendChild(document.createTextNode(file.name));
148+
preview.appendChild(li);
149+
}
150+
}
151+
}
90152

91-
// 既定の動作で防ぐ(ファイルが開かれないようにする)
153+
function dropHandler(ev) {
92154
ev.preventDefault();
155+
const files = [...ev.dataTransfer.items]
156+
.map((item) => item.getAsFile())
157+
.filter((file) => file);
158+
displayImages(files);
93159
}
94160
```
95161

162+
## input に同じ動作を追加
163+
164+
上記がドラッグ&ドロップの全データフローです。次に、`displayImages()` 関数をファイル入力欄にも接続する必要があります。
165+
166+
```js live-sample___file-dnd
167+
const fileInput = document.getElementById("file-input");
168+
fileInput.addEventListener("change", (e) => {
169+
displayImages(e.target.files);
170+
});
171+
```
172+
173+
## クリアボタン
174+
175+
最後にプレビュー領域をクリアする方法を追加します。 {{domxref("URL.revokeObjectURL_static","URL.revokeObjectURL()")}} を使用して画像オブジェクトが使用していたメモリを解放します。
176+
177+
```js live-sample___file-dnd
178+
const clearBtn = document.getElementById("clear-btn");
179+
clearBtn.addEventListener("click", () => {
180+
for (const img of preview.querySelectorAll("img")) {
181+
URL.revokeObjectURL(img.src);
182+
}
183+
preview.textContent = "";
184+
});
185+
```
186+
187+
## 結果
188+
189+
{{EmbedLiveSample("file-dnd", "", 500)}}
190+
96191
## 関連情報
97192

98-
- [HTML ドラッグ & ドロップ API (概要)](/ja/docs/Web/API/HTML_Drag_and_Drop_API)
193+
- [HTML ドラッグドロップ API](/ja/docs/Web/API/HTML_Drag_and_Drop_API)
99194
- [ドラッグ操作](/ja/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations)
100-
- [HTML Living Standard: Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd)

0 commit comments

Comments
 (0)