6장 Drag and Drop
HTML5는 마우스나
기타 포인팅 장치를 이용하여 웹 브라우저 내의 문자열이나 이미지를 움직일 수 있는 끌기와 놓기(drag-and-drop)
동작을 지원합니다.
이 그 기능은 쇼핑몰과 같은 웹 페이지에서 유저로 하여금 쇼핑 카트에 구매할 상품을 끌어다 놓거나 홈페이지 내에서 특정 요소를 재배치하거나
파일 업로드 같은 기능을 구현할 때도 추가적으로 이용할 수 있는 기능입니다.
현재까지 끌기(Drag)와 놓기(Drop) 기능은
자주 업데이트 중이며 관련 명세들에 포함된 기능들을 브라우저 별로 구현이 되는 부분도 있고 구현되지 않는 부분이 있는 상태임을 알립니다.
관련 문서는 “http://dev.w3.org/html5/spec/dnd.html“에서
확인할 수 있습니다.
끌기(Drag)와 놓기(Drop)의 전체적인
흐름은 다음과 같습니다.
drag가 가능한 요소를
마우스 혹은 멀티 터치가 가능할 경우는 여러분으로 손으로 요소를 끌어다가 dropzone이라는 사전에
정의된 영역에 현재 drag 중인 요소를 내려 놓는 과정과 관련된 것이 끌기(Drag)와 놓기(Drop)의 전반적인 흐름이며 이 과정에서 이루어지는
세부적인 작업을 여기서는 간략하게 소개하고자 합니다.
1. 끌기(drag)할 요소에 draggable 속성 설정하기
HTML 요소(Element)를 마우스나 기타 포인팅 장치로
드래그(끌기)할 수 있는 속성을 설정하는 것으로 끌기(drag)가 가능한 true, 끌기(drag)가
불가능한 false, 마지막으로 기본값인 auto로 다음과
같은 형식을 사용하여 설정할 수 있습니다.
<element draggable=속성값>…</element>
여기서
마지막 속성값이 auto는 어떻게 적용되는지가 궁금할 수 있는데
draggable 속성값이 auto로 설정되면 브라우저에게 해당 요소를 끌기(drag)할 것인지 안 할 것인지를 브라우저가 알아서 하도록 합니다. 메모장이나
기타 에디터를 열어서 다음과 같이 코드를 작성해봅니다.
<!DOCTYPE HTML>
<html>
<head>
<title> Drag 가능 여부 확인 </title>
<style type="text/css">
</style>
<script type="text/javascript">
</script>
</head>
<body>
<h1>각각을 끌기(drag)해볼까요?</h1>
<img id="dragImage1" src="./images/chickenSalad.png" draggable="true"/><br/>
<img id="dragImage3" src="./images/crab.png" draggable="false" /><br />
<img id="dragImage3" src="./images/shrimp.png" draggable="auto" />
</body>
</html>위와 같이 img 요소의 draggable 속성에 속성값으로 true, false, auto 로 설정하여 사용하게 됩니다. 이제 다른 이름으로 저장을 선택하고 “dragOrNot.html”로 저장합니다. Chrome으로 열어보면 다음과 같이 나타납니다.
마우스로 끌기(drag)를 해보면 draggable 속성값이 true, auto로 설정된 처음과 마지막
이미지는 끌기(drag)가 되는 반면 draggable 속성값이 false로 설정된 두 번째 이미지는 끌기(drag)가 되지 않습니다.
2. 끌기(drag)와 관련된 이벤트들
요소(Element)에 draggable 속성값이 “true”로 설정되어 마우스나 포인팅
장치를 이용한 요소의 끌기(drag) 동작이 가능하도록 설정한 후에 끌기(drag) 동작을 해보면 기본적으로 다음과 같은 이벤트들이 상황에 따라서 발생하게 됩니다.
이벤트 설명 dragstart 요소에
처음 끌기(drag)될 때 발생 drag 요소가
끌기(drag)된 상태에서 반복적으로 발생 dragend 요소에
대한 끌기(drag) 작업이 완료되었을 때 발생. 보통
끌기(drag) 중인 요소가
놓기(drop) 동작 후 발생됨 dragenter 끌기(drag)된 요소가 놓기(drop) 동작을 수행하기 위해 dropzone 영역에 들어갔을 때 발생 dragover 끌기된
요소가 dropzone 영역에서 움직일 때 발생 dragleave 끌기된
요소를 놓기(drop) 동작을 하지 않고 dropzone 영역에서
벗어날 때 발생
이제
예제를 해볼까요? dragstart, drag 이벤트에 대한 예제입니다.
방금 전 예제 “dragOrNot.html”을 메모장으로 열고 다음과 같이 코드를 수정, 추가합니다.
<body>
<h1>각각을 끌기(drag)해볼까요?</h1>
<img id="dragImage1" src="./images/chickenSalad.png" draggable="true"
ondragstart="dragstart(event)" ondrag="drag(event)" /><br/>
<img id="dragImage3" src="./images/crab.png" draggable="false" /><br />
<img id="dragImage3" src="./images/shrimp.png" draggable="auto"/>
</body>
<script type="text/javascript">
function dragstart(e)
{
alert("drag가 시작되었음");
}
function drag(e)
{
alert("drag 중임");
}
</script>위와 같이 img 요소에 draggable 속성값이 “true”로 설정된 img 요소에 dragstart, drag 이벤트가 발생하면 실행할 자바 스크립트 함수를 설정해주고 <script> ... </script> 블록 내에 해당 자바 스크립트 함수를 작성합니다.
마지막으로 다른 이름으로 저장을 선택하여 “dragOrNotwithEvents.html”로 저장한 후 chrome로 열어서 draggable 속성값이 “true”로 설정된 img 요소를 끌기(drag)하면 다음과 같이 해당 이벤트들이 발생하여 메시지를 나타냅니다.
3. 끌기(drag)된 요소를 놓을 영역(dropzone) 설정하기
dropzone을 지정하기 위해서는 2 가지 형태가 가능한데 끌기(drag)된 요소를 놓기(drop)할 요소에 dropzone 속성을 설정하는 방법과 끌기(drag)된 요소를 놓기(drop)할 요소의 id 속성에
dropzone과 같은 이름으로 명시하고 해당 요소에 끌기(drag)에서 언급한 dragenter, dragover 같은 이벤트와 놓기(drop) 이벤트들을
설정하여 사용하는 방법이 있습니다.
여기서는 첫 번째 방법보다는 대부분의 브라우저에서 지원하는 두 번째 방법을 설명하고자 합니다. 두
번째 방법에서 class 속성에 다음과 같은 형식으로 설정합니다.
<element id=dropzone> … </element>
예제를 통해서 그 실제 동작을
확인해봅니다. 메모장으로 “dragOrNotwithEvents.html”
문서를 열고 다음과 같이 코드를 추가합니다.
<img id="dragImage3"
src="./images/shrimp.png" draggable="auto"/><br />
<div
id="dropzone"
style="background:beige;border:1px dotted;width:200px;height:100px"
ondragenter="dragenter(event)" ondragover="dragover(event)" ondragleave="dragleave(event)" />
</body>
</html>위와 같이 div 요소를 이용하여 끌기(drag)된 img 요소를 놓기(drop)할 요소의 id 속성에 dropzone과 같은 이름으로 명시한 후 끌기(drag)에서 언급한 dragenter, dragover 같은 이벤트를 명시합니다. 이제 <script> … </script> 블록을 다음과 같이 작성합니다.
<script type="text/javascript">
function dragenter(e)
{
alert("dropzone에 진입");
}
function dragover(e)
{
alert("dropzone에 있음");
}
function dragleave(e)
{
alert("dropzone에서 벗어남");
}
</script>마지막으로 다른 이름으로 저장을 선택하여 “dropzone.html”로 저장한 후 chrome로 열어보면 다음과 같이 나타납니다.
첫 번째 이미지를 끌기(drag)하여 dropzone으로 설정된 div 요소 내로 이동하면 dragenter 이벤트가 발생하여 다음과 같이 나타내게 됩니다.
dragover, dragleave 이벤트를 확인해보기 위해서 <script> … </script> 블록 내 function dragenter(e) 부분을 주석처리(/* … */)한 후 다시 실행하여 첫 번째 이미지를 끌기(drag)해서 dropzone 내로 이동한 후 dropzone을 벗어나면 다음과 같이 차례대로 관련 메시지를 나타냅니다.
4. 놓기(drop)와 관련 이벤트들
놓기(drop)이벤트는 여러분이 마우스나 기타 포인팅
장치를 이용하여 특정 요소를 끌기(drag)해서 방금 전 알아본
dropzone 영역에 놓을 때 발생합니다.
이번 예제에서는 여러분이 dropzone에 img 요소를
놓기(drop)하면 메시지창에 “요리가 예약되었습니다.”라고 표시하고 다시 dropzone에서 img 요소를 끌기(drag)하면 “요리가
취소되었습니다.”라고 표시하도록 합니다.
메모장으로 “dropzone.html”을 열고 코드를 변경하는데 먼저
<script> … </script> 블록입니다.
<script
type="text/javascript">
function allowDrop(e)
{
e.preventDefault();
}
function dragstart(e)
{
e.dataTransfer.setData("Text",e.target.id);
}
function drop(e)
{
var id = e.target.getAttribute('id');
var data=e.dataTransfer.getData("Text");
e.target.appendChild(document.getElementById(data));
if(id =='dropzone'){
alert("요리가 예약되었습니다.");
}
else
alert("요리가 취소되었습니다.");
e.preventDefault();
}
</script>위에서 사용된 preventDefault() 메서드는 기본적으로 요소(Element)는 다른 요소(Element)에 drop 될 수 없습니다. preventDefault() 메서드는 이런 기본 동작을 하지 않고 다른 요소에 drop될 수 있도록 하는데 사용됩니다.
dragstart(e) 함수는 마우스로 id가 “dragImage1”인 첫 번째 이미지 요소를 드래그(drag)하면 발생하는 이벤트에서 처리해줘야 할 코드들을 정의한 것으로
Drag and Drop에서 데이터 핸들링의 핵심 객체인 dataTrasfer 객체의 setData() 메서드를 이용하여 drop할 요소의 id를 설정하였습니다.
다음으로 drop(e)는 dropzone으로
설정된 영역에 현재 drag된 데이터를 drop하면 처리할
코드를 작성하는 부분입니다. 여기서는 dataTrasfer 객체의 getData() 메서드를 이용하여 dropzone에 추가될 요소의 id를 정보를 얻고 이를 appendChild() 메서드를 이용하여 dropzone의 자식 요소로 추가합니다.
이제 <body> 부분을 다음과 같이 수정합니다.
<body>
<h1>각각을
끌기(drag)해볼까요?</h1>
<div
id="SourceNode"
style="background:yellow;border:1px solid;width:100px;height:200px"
ondrop="drop(event)" ondragover="allowDrop(event)">
<img id="dragImage1" src="./images/chickenSalad.png" draggable="true"
ondragstart="dragstart(event)" /><br/>
<img id="dragImage2" src="./images/crab.png" draggable="false" /><br />
<img id="dragImage3" src="./images/shrimp.png" draggable="auto" /><br />
</div>
<br/><br />
<div id="dropzone" style="background:beige;border:1px dotted;width:200px;height:100px"
ondrop="drop(event)" ondragover="allowDrop(event)" />
</body>마지막으로 다른 이름으로 저장을 선택하여 “dropzonewithdrop.html”로
저장한 후 chrome로 열어보면 다음과 같이 나타납니다.
위에서 첫 번째 이미지를 끌기(drag)해서 dropzone에 놓기(drop)를 해보면 다음과 같이 나타납니다.
이제 반대로 dropzone에 있는 이미지를 끌기(drag)해서 id의 속성값이 “SourceNode”인 div에 놓기(drop)를 해보면 역시 동일한 동작이 수행되어 다음과
같은 결과를 나타냅니다.
5. 끌기(drag)와 놓기(drop)의 핵심 – dataTransfer 객체의 이해
앞서 설명한 개괄적인 drag and drop 과정에
대한 흐름을 보다 구체적으로 설명하면 다음과 같습니다.
여러분은 방금 전 “dropzonewithdrop.html” 예제를 통해 dataTransfer 라는 객체를 사용해 보았습니다. dataTransfer 객체는 drag and drop 과정에서 발생하는 데이터를 핸들링하는데 사용되는 핵심적인 객체입니다.
HTML 요소에 drag 동작을 사용하려면 drag할 요소(element)에
draggable 속성(Attribute)을 부여한 후 드래그(drag)되는 데이터를 저장하기 위한 dragstart에 대한 이벤트에
반응하는 리스너(EventListner )
혹은 이번트 핸들러를 설정합니다.
이 이벤트 리스너에는
기본적으로 해당 요소가 드래그(drag)될 때 드래그될 수 없는 텍스트를 선택한 것이 아니라는 것을
확인한 다음에 데이터를 DataTranfer 객체(object)로
저장하고 복사, 이동 등과 같은 허용된 효과를 설정하면 됩니다.
다음으로 문자열이나 이미지를 놓기(drop)가 가능하도록 하려면 현재 DataTranfer에 저장되어 있는 데이터를 받아들이는 대상은
dropzone 속성을 가져야 하고 drop 이벤트를 처리할 수 있어야 합니다. 여기서 dropzone 속성은 텍스트 문자열 형태를 받아들일 수
있도록 하는 “text/plain”이나 PNG 이미지 파일을
받아들이는 “image/png” 그리고 피드백을 주기 위한 형태로 설정합니다.
마지막으로 dragend
이벤트가 발생하면 drag된 원래의 요소를 제거를
하게 됩니다.
이제 dataTransfer 객체와 관련된 속성과
메서드에 대해서 차례대로 알아봅니다.
5.1 dataTransfer 객체(object)의 속성
데이터를 드래그(drag)한 데이터를 dropzone에 놓기(drop)하는 과정 중에서 dataTransfer 객체는 여러 가지
속성들을 이용하여 관련 데이터들을 처리하는데 현재까지 정의되어 있는 속성들을 정리하면 다음과 같습니다.
속성 |
의미 |
effectAllowed |
drag and drop 과정에서 dragenter, dragover 이벤트가 발생하는 동안 |
dropEffect |
어떤 동작이 선택되었는지를 반환하는 속성. None, copy, move. link
중 |
items |
dataTransfer 객체와 관련된 dataTransferItemList 객체를 반환 |
types |
dragstart 이벤트 설정된 형식들을
문자열로 반환. 만일 파일들일 경우 |
files |
파일들에 대한 FileList 형태로 반환 |
물론 위에서 정리된 속성과 그에 해당하는 속성값들을 모두 지원하는 웹 브라우저는 현재 없는 듯 합니다. 향후 표준화가 진행되면서 사라져야 할 속성 혹은 속성값들이 당연히 등장하겠지만 그래도 지금 현재까지 이루어진 속성과 이에 대한 속성값들에 대해서 정리해보는 것도 나름 의미가 있을 것 같아서 정리합니다.
5.1.1 effectedAllowed 속성(Property)
effectedAllowed 속성은 drag and drop 과정에서 dragenter, dragover 이벤트가 발생하는 동안
dropEffect 속성을 초기화하는데 사용되는 속성으로 다음과 같은 속성값을 가질 수 있습니다.
속성값 |
의미 |
none |
Drag된 데이터가 drop될 수 없음 |
copy |
Drag된 데이터가 복사될 수 있음 |
copyLink |
Drag된 데이터가 복사되고 링크될 수 있음 |
copyMove |
Drag된 데이터가 복사되고 이동될 수 있음 |
link |
Drag된 데이터가 링크될 수 있음 |
linkMove |
Drag된 데이터가 링크되고 이동될 수 있음 |
move |
Drag된 데이터가 이동될 수 있음 |
all |
Drag된 데이터가 복사, 이동, 링크될 수 있음 |
uninitialized |
디폴트값으로 effectedAllowed 속성값은 최기화되지 않음 |
예를 들어 이동일 경우 다음과 같이 사용할 수 있습니다.
dataTransfer.effectAllowed='move';
5.1.2 dropEffect 속성(Property)
어떤 동작이 선택되었는지를 반환하는 속성으로 다음과 같은 4 가지
속성값을 가질 수 있으며
만일 effectedAllowed 속성에서 설정된 값이 아니라면 해당 동작은 실패합니다.
속성값 |
의미 |
none |
Drag된 데이터가 drop될 수 없음 |
copy |
Drag된 데이터가 복사될 것임을 알림 |
link |
원래의 데이터와 Drag된 데이터 사이의 일종의 링크가 생성됨을 알림 |
move |
Drag된 데이터가 이동될 것임을 알림 |
5.1.3 items 속성(Property)
dataTranfer 객체의 items 속성은 dataTransfer 객체와 관련된 dataTransferItemList 객체를
반환하는 속성으로 이 dataTransferItemList는 배열의 형태로 드래그된 데이터를 포함합니다.
5.2 DataTransfer 객체(object)의 기본적인 메서드
DataTransfer 객체에 데이터를 추가, 어떤 데이터가 들어있는지 확인하거나
기존의 데이터를 삭제하기 위해서는 다음과 같이 기본적인 3 가지 메서드를 이용합니다.
메서드 |
의미 |
setData() |
데이터를 추가하거나 기존의 데이터를 바꿔 저장할 때 사용 |
getData() |
명시된 형식(Format)에 맞는 데이터를 문자열 형태로 반환 |
clearData() |
데이터를 제거할 때 사용 |
setData() 메서드는 데이터를 추가할 때 사용하고 getData() 메서드는 특정 형식의 데이터를 얻고자 할 때 사용되며 마지막으로 clearData() 메서드는 데이터를 제거할 때 사용되며 각각으로 사용 형식은 다음과 같습니다.
dataTransfer.setData(format,
data)
dataTransfer.getData(format)
dataTransfer.clearData()
위에서 setData(), getData() 메서드의 입력 파라미터로 사용되는 format은 드래그(drag)되는 데이터의 형식을 나타내는 것으로 텍스트의 경우는 “text”, URL일 경우는 “url”입니다.
5.3 Drag되는 데이터 필터링하기
Drag되는 요소들 가운데 필터링을 하려면 setData() 메서드를
사용하여 데이터를 추가할 때도 가능하겠지만 여기서는 놓기(Drop) 작업을 수행할 때 체크하여 필요한
데이터만 놓기(Drop) 작업이 가능하도록 하는 예를 소개합니다. “dropzonewithdrop.html”
파일을 이용하여 id가 “dragImage3”인 img 요소만 놓기(Drop) 작업이 가능하도록 하는 예입니다.
“dropzonewithdrop.html” 파일을 열어서 먼저 <body>…</body>
블록에서 3개의 img 요소에 대해서 현재 id가 “dragImage1”인
img 요소만 draggable 속성값이 true로 dragstart 이벤트가 설정되어 있는데 나머지 2개의 img 요소도 id가 “dragImage1”인 img 요소처럼 코드를 다음과 같이 수정합니다.
<div id="SourceNode"
style="background:yellow;border:1px solid;width:100px;height:200px"
ondrop="drop(event)"
ondragover="allowDrop(event)">
<img id="dragImage1"
src="./images/chickenSalad.png" draggable="true"
ondragstart="dragstart(event)"
/><br/>
<img id="dragImage2"
src="./images/crab.png" draggable="true"
ondragstart="dragstart(event)"/><br />
<img id="dragImage3"
src="./images/shrimp.png" draggable="true"
ondragstart="dragstart(event)" /><br />
</div>
이렇게 한 후 다른 이름으로 저장을 선택하여 “dropzonewithdropandfiltering.html”로
저장한 후 Chrome을 이용하여 연결한 후 다음과 같이 3개의 img 요소를 끌기와 놓기 작업을 해봅니다.
그러면 위와 같이 3개의 3개의 img 요소를 끌기와 놓기 작업이 가능합니다.
이제 id가 “dragImage3”인 img 요소만 놓기(Drop) 작업이 가능하도록 하기 위해서 함수
drop(e)의 코드에 다음과 같이 코드를 추가합니다.
function
drop(e)
{
var id = e.target.getAttribute('id');
var
data=e.dataTransfer.getData("Text");
if (data == "dragImage3")
{
e.preventDefault();
}
else
{
e.dataTransfer.clearData();
return;
}
e.target.appendChild(document.getElementById(data));
…
위와 같이 if문을 이용하여 id가 “dragImage3”인
img 요소만 처리하도록 코드를 작성하고 저장합니다. 다시 Chrome을 이용하여 연결하여 각 이미지들을 끌기와 놓기해보면 다음과 같이
id가 “dragImage3”인 img인 요소만
동작하는 것을 알 수 있습니다.
- 본 저작물은 본인이 2011년 상반기부터 2012년 여름 즈음까지 도서 출판을 목적으로 약 470 페이지(A4, 폰트 10)으로 작성한 원본 중 해당 부분을 그대로를 공개하는
것으로 본 저작물에 대한 모든 권리는 본인(원철연)에서 있음을
알립니다.
개인적인
학습 목적으로 사용을 허용하며 온오프라인의 베포나 펌, 상업적인 용도의 사용은 삼가해주시기
바랍니다. 끝으로 학교나 비영리 단체에서의 경우 본 저작물을 비상업적인 용도로 활용하고자 할 경우 연락처를
비밀댓글로 남겨주시면 연락드리겠습니다.
'제가 쓴 책 > HTML5, CSS3 and JavaScript' 카테고리의 다른 글
2.sessionStorage (0) | 2013.08.08 |
---|---|
7장 Web Storage (0) | 2013.08.08 |
2. Video (0) | 2013.08.07 |
1. Audio (0) | 2013.08.07 |
11.2 픽셀(pixel)에 기반한 이미지 가공(Manipulation) (0) | 2013.08.07 |