본문 바로가기

Publishing

HTML5 & Javasciprt : Drag&Drop 구현 (예제)

Drag&Drop : File API

파일을 다루기 위해선 HTML의 <input type="file"> 태그나, 드래그 앤 드롭을 사용해 사용자에게 파일을 받고, File API를 이용한다.

아래 움짤은 드래그 앤 드롭으로 파일을 받는 모습인데,
본 포스팅에서는 이미지를 받되 한 단계 더 거쳐서 해당 이미지를 띄워주는 것까지 구현해본다.
(서버상에서 이미지를 받아 경로를 리턴해주는 것이 아니라, 앞단에서 파일을 다룸)

기본구조

기능구현을 위한 기본 구조는 아래와 같다.

HTML

이미지를 받아낼 <div> 단 하나!

<p>drag and drop your image!</p>
<div class="content">
</div>
cs

css

굳이 이렇게 안해도 됩니다.. 
.content{
    outline: 2px dashed #92b0b3 ;
    outline-offset:-10px;  
    text-align: center;
    transition: all .15s ease-in-out;
    width: 300px;
    height: 300px;
    background-color: gray;
}
cs

구현

본격적으로 기능구현에 앞서, jQuery를 사용했음을 알림.
바닐라 자바스크립트로 짜도 됩니다.

해당 엘리먼트에 dragover, dragleave, drop 이벤트를 바인딩한다.
$('.content')
  .on("dragover", dragOver)
  .on("dragleave", dragOver)
  .on("drop", uploadFiles);
 
function dragOver(e){
  e.stopPropagation();
  e.preventDefault();
}
 
function uploadFiles(e){
  e.stopPropagation();
  e.preventDefault();
}
cs

e.stopPropagation()e.preventDefault() 설명은 
이분이 잘해놓았다. 참조.

dragover/dragleave

dragoverdragleave 이벤트가 하나의 function에 묶여 있다.
event의 타입을 확인 후, 조건문으로 약간의 시각적 효과를 줬다.
function dragOver(e) {
    e.stopPropagation();
    e.preventDefault();
    if (e.type == "dragover") {
        $(e.target).css({
            "background-color""black",
            "outline-offset""-20px"
        });
    } else {
        $(e.target).css({
            "background-color""gray",
            "outline-offset""-10px"
        });
    }
}
cs

이벤트가 잘 바인딩되었으면 아래와 같이 파일을 갖다대고 떠날 때 반응한다.

drop

파일이 타겟에 떨어졌을 때 발생하는 이벤트다. 

아래와 같이 작성해보자.

function uploadFiles(e) {
    e.stopPropagation();
    e.preventDefault();
    dragOver(e); //1
 
    e.dataTransfer = e.originalEvent.dataTransfer; //2
    var files = e.target.files || e.dataTransfer.files;
 
    if (files.length > 1) {
        alert('하나만 올려라.');
        return;
    }
}
cs
  1. 드랍이벤트가 발생했을 때 dragover 이벤트에서 까만색으로 전환시켰던 색깔이 돌아오지 않기 때문에 추가해줬다. (dragleave 이벤트가 발생하지 않기 때문이다.)
  2. e.dataTransfer가 undefined로 뜰 때가 있는데 (아마 jQuery를 사용할 때) 명시적으로 재정의해주면 된다. 

파일을 드랍하면 files안에 아래와 같은 객체가 담긴다.


우리는 하나의 타겟에 하나의 이미지를 박을 것이기 때문에 2개 이상의 파일을 한번에 드롭하면 리턴시켰다. 


타입을 판별하고, 이미지가 아닐 경우에도 리턴시키자.

if (files[0].type.match(/image.*/)) {
        
}else{
    alert('이미지가 아닙니다.');
    return;
}
cs


이제 이미지를 뿌려주면 된다.

아래 코드를 추가하자.

if (files[0].type.match(/image.*/)) {
    $(e.target).css({
        "background-image""url(" + window.URL.createObjectURL(files[0]) + ")",
        "outline""none",
        "background-size""100% 100%"
    });
}else{
  alert('이미지가 아닙니다.');
  return;
}
cs


위 코드가 가능한 이유는 아래와 같다. 파일 객체가 Blob으로써 다뤄질 수 있다는 것이다.

File object is a specific kind of a Blob, and can be used in any context that a Blob can. In particular, FileReaderURL.createObjectURL()createImageBitmap(), and XMLHttpRequest.send() accept both Blobs and Files.


이제 이미지를 떨구면 타겟에 해당 이미지를 띄워줄 수 있다.

코드

포스팅에 사용한 코드. 직접 이미지를 올려보자.


아래는 조금 다르게 구현한 것.


사실 포스팅을 하게 된 건 이미지 용량을 줄여주는 사이트인 https://tinypng.com/ 을 보고 구현해본 건데, 한번 접속해보면 위 예제의 활용가능성을 볼 수 있지 않을까 싶다.