jstarted.com
JavaScript/HTML/CSSのノウハウや覚書を掲載するブログ
2013.10. 8

ローカルの画像ファイルをブラウザにドラッグ・アンド・ドロップしてinputに入力する(IE非対応)

Category:

ローカルにある画像ファイルをブラウザにドロップしてアップロードする、というのをやってみました。
File APIの対応/非対応さえ判断できれば案外チョロいんじゃないか、とたかをくくっていましたが、とんでもなかったです。かなり手こずりました。

今回手を付けたのは、inputにローカルの画像ファイルを読みこませるところまで。
実際に送信するところまでは手をつけていません。inputはtype="file"を使用しています。inputを透過してドラッグ領域全体に広げ、ドロップの挙動はjsで書かずにinputの標準機能にまかせています。(firefoxとchromeでは、input type="file"にドロップするとファイルを選択した状態になってくれます。)

コードは以下のページのものを参考にしました。 http://www.html5rocks.com/ja/tutorials/file/dndfiles/

var	box = document.getElementById('fileBox'),
	elm = document.getElementById('receiveFile'),
	input = document.getElementById('inputFile'),
	ua = navigator.userAgent,
	ua_ie = false;

// IEではinput type="file"にファイルをドロップできないのでinputを表示する
if( ua.indexOf('MSIE') > -1 || ua.indexOf('Trident/7.0') > -1 ){
	ua_ie = true;
	elm.innerHTML = '<p>IEは10も11もinput type="file"にドロップできません!!!</p>';
	input.className = input.className + 'nodrop';
}

// ブラウザがドラッグイベントに対応しているかを確認
if ('ondragover' in window && 'ondragleave' in window && 'ondrop' in window) {
	// ブラウザがFile APIに対応しているかを確認
	if (window.File && window.FileReader && window.FileList && window.Blob) {
		var	elm_className_base = elm.className;

		var file_select = function(evnt){
			var	files = evnt.target.files,
				img_num = 0;

			elm.className = elm_className_base;
			elm.innerHTML = '';

			for ( var i = 0; i < files.length; i++ ){
				var	indx = i;
					file = files[indx],
					reader = new FileReader();

				if( !file.type.match('image.*')){
					continue;
				}

				reader.onerror = function() {
					this.innerHTML = '<p>読み取り時にエラーが発生しました。</p>';
				};

				img_num += 1;

				reader.onload = (function(theFile){
					return function(e){
						var	item = document.createElement('div'),
							item_txt = '';

						item.className = 'item';
						item_txt = theFile.name;
						item.innerHTML = '<div class="image"><div class="imageInner"><img src="' + e.target.result + '" title="' + item_txt + '" alt="' + item_txt + '"></div></div><div class="text">' + item_txt + '</div>';
						elm.insertBefore(item, null);
					};
				})(file);
				
				reader.readAsDataURL(file);
			}
			if( img_num < 1 ){
				elm.innerHTML = '<p>画像ファイルが含まれていません。<br>画像ファイルをドロップしてください。</p>';
			}
		};

		if( ua_ie === false ){
			box.ondragover = function(evnt){
				evnt.stopPropagation();
				evnt.preventDefault();
				elm.className = elm_className_base + 'draggle';
			};

			box.ondragleave = function(evnt){
				evnt.stopPropagation();
				evnt.preventDefault();
				elm.className = elm_className_base;
			};
		}

		input.addEventListener('change', file_select, false);
	} else {
		elm.innerHTML = '<p>File API非対応ブラウザ!!!!!!</p>';
		box.className = box.className + 'unsupported';
	}
} else {
	elm.innerHTML = '<p>ドラッグ&ドロップ用のイベントに非対応!!!!!!</p>';
	box.className = box.className + 'unsupported';
}

ブラウザがFile APIに対応しているかどうか、ondragoverやondragleaveといったイベントに対応しているかどうか、各ブラウザによってinput type="file"の挙動が異なるのをどこまで動作保証するか、といった点で各所分岐を設けていたら、かなり長いコードになってしまいました。input type="file"はIE以外では標準でドロップに対応していますが、IEでは最新の11でもそれに対応していません。なので結局IEは全面的にドロップ非対応という残念な結果になりました・・・

あと、ファイルを取得する際、複数のファイルを一括で読み込ませるようにしていますが、使用したfor文内にfunctionを使っているので、jslintに怒られます。これは回避策を見つけることができませんでした。

動作のサンプルはこちら
ローカルの画像ファイルをブラウザにドラッグ・アンド・ドロップしてinputに入力する(IE非対応) - jsdo.it

IEをすべて切り捨てるというのはさすがにみっともないですね。送信後の処理との兼ね合いにもよりますが、input type="file"ではなく、type="hidden"を使用する方法を考えたほうがいいのかもしれません。

ちなみにfacebookでは、同様の機能をIEでも動作させているようです・・・

jstarted.comはamazon.co.jpを宣伝しリンクすることによってサイトが紹介料を
獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、
Amazonアソシエイト・プログラムの参加者です。

クリエイティブ・コモンズ・ライセンス
jstarted.com by yoichi kobayashi is licensed under a Creative Commons 表示 3.0 非移植 License.