jquery drag & drop
페이지 정보
본문
순수하게 자바스크립트, 제이쿼리로 드래그 앤 드롭 기능을 만들었는데 많은 공부가 되어서 정리한다.
나의 경우 2가지 div 박스에 여러 개의 li를 드래그 앤 드롭했다.
▼ 간단한 HTML 예시
<div id="div1" class="box">
<ul id="ul1" class="ul">
<li id="li1" class="list">li 1</li>
<li id="li2" class="list">li 2</li>
<li id="li3" class="list">li 3</li>
</ul>
</div>
<div id="div2" class="box">
<ul id="ul2" class="ul">
</ul>
</div>
1. 드래그 앤 드롭은 '드래그할 대상'과 '드롭될 대상' 2가지가 필요하다.
'드래그할 대상'과 대상'에는 자바스크립트를 이용해 각각 아이디로 이벤트 리스너를 걸어준다.
클래스 네임으로는 이벤트가 안 걸려서 선택됐을 때 각각 아이디가 있어야 한다.
또한, 드래그할 대상에는 draggable을 추가해주어야 한다.
▼ 이벤트 리스너 추가 및 draggable 토글 되는 자바스크립트
$(document).ready(function(){
// li에 이벤트 추가
for(var i = 1 ; i <= 3 ; i++){
var id = 'li'+i;
document.getElementById(id).addEventListener('dragstart', dragstart);
document.getElementById(id).addEventListener('drag', drag);
document.getElementById(id).addEventListener('dragend', dragend);
}
// div에 이벤트 추가
for(var i = 1 ; i <= 2 ; i++){
var id = 'div'+i;
document.getElementById('div1').addEventListener('dragenter', dragenter);
document.getElementById('div1').addEventListener('dragover', dragover);
document.getElementById('div1').addEventListener('drop', drop);
document.getElementById('div1').addEventListener('dragleave', dragleave);
}
// 선택된 li에 draggable = true & 선택 해제 된 li에 draggable = false
$('ul').on('click','li',function(){
$(this).toggleClass('selected');
if($(this).is('.selected')){
$(this).attr('draggable',true);
} else {
$(this).attr('draggable',false);
}
});
});
2. 드래그할 대상을 선택해서 드롭이 될 때까지 여러 가지 순서를 거친다.
2번째 코드 블록에도 적어놨듯이 각 이벤트는 드래그할 대상(li) 또는 드롭될 대상(div)에 구분해서 걸어야 한다.
이렇게 구분해야 하는 이유는 각 이벤트가 발생할 때 타깃의 위치 등 파라미터의 기준이 다르기 때문이다.
A. 드래그할 대상
(1) dragstart : 드래그할 대상을 잡고 움직이기 시작할 때 이벤트가 발생한다.
(4) drag : 마우스가 드래그 할 대상을 놓지 않고 잡고 있을 때 계속 발생한다.
(7) dragend : 모든 드래그와 드롭이 끝났을 때 발생한다.
B. 드롭될 대상
(2) dragenter : 마우스가 드롭될 대상 경계에 진입될 때 이벤트가 발생한다.
(3) dragover : 마우스가 드롭될 대상 위에 머무를 때 계속 발생한다.
(5) drop : 마우스가 드래그할 대상을 드롭했을 때 발생한다.
(6) dragleave : 마우스가 드롭될 대상을 벗어났을 때 발생한다.
이 외에도 관련 이벤트가 많은데 나는 저 7개만 사용했다.
3. 각 이벤트에 대해
(1) dragstart
드래그를 하기 위해 마우스가 눌린 상태에서 움직이기 시작할 때 발생하는 이벤트로 드래그될 대상의 html을 저장했다.
나의 경우 크롬과 IE에서 모두 다 잘 돌아가야 했다.
크롬의 경우, 드롭할 데이터를 저장해 두는 dataTransfer.setData의 형식이 다양하고 드래그 시 반투명하게 보이는 미리 보기 이미지(ghost image)와 같이 지원해주는 기능이 많았다.
반면, IE는 안 되는 것이 많았고 기능도 단순했기 때문에 IE에 맞춰야만 했다.
드래그 시작 시 드래그될 대상의 정보를 dataTransfer에 담고 해당 대상들은 숨겨지도록 했다.
function dragstart(e){
var html = '';
$('.selected').each(function(){
html += '<li class="list" id = "' + $(this).attr('id') + '">' + $(this).html() + '</li>';
});
// 드래그 될 li들의 html을 텍스트 형태로 dataTransfer에 세팅
e.dataTransfer.setData('text',html);
}
(2) dragenter
드롭될 대상의 경계에 진입하는 순간 발생하는 이벤트로 주로 시작적인 효과를 나타내도록 했다.
또한, 드래그할 때 특정 위치에서 스크롤을 적용하기 위해 위치 값을 구했다.
드롭하는 과정에서 원치 않는 이벤트를 막고 순수하게 드롭만 할 수 있도록 e.preventDefault()를 작성하였다.
var targetBox = ''; // 현재 선택된 div
var topPos = ''; // top 위치
var bottomPos = ''; // bottom 위치
var leftPos = ''; // left 위치
var rightPos = ''; // right 위치
function dragenter(e){
// 브라우저 표준동작 취소
e.preventDefault();
// 선택된 div 표시
targetBox = $(e.target).closest('.box').attr('id');
$('#'+targetBox).addClass('selectedBox');
// div 위치
topPos = document.getElementById(targetBox).getBoundingClientRect().top;
bottomPos = topPos + $('#'+targetBox).height();
leftPos = document.getElementById(targetBox).getBoundingClientRect().left;
rightPos = leftPos + $('#'+targetBox).width();
}
(3) dragover
드롭될 대상의 위에 머무르는 동안 계속 발생하는 이벤트로 스크롤과 위치 미리보기를 작성했다.
function scroll(target, step){
// 현재 스크롤의 세로 위치
var scrollY = $('#' + target).scrollTop();
$('#' + target).scrollTop(scrollY + step);
if(!scrollStop){
setTimeout(function(){ scroll(step) },3);
}
}
function dragover(e){
e.preventDefault();
//top border의 ±10px안에 있을 때 위로 스크롤
if(e.clientY < topPos + 10 && e.clientY > topPos - 10){
scrollStop = false;
scroll(targetBox, -10);
}
// bottom border의 ±10px안에 있을 때 아래로 스크롤
else if(e.clientY < bottomPox + 10 && e.clientY > bottomPos - 10){
scrollStop = false;
scroll(targetBox, 10);
}
// li 사이에 있을 때 위치 미리보기
else if($(e.target).is('.list, .preview')){
var html = '<div class="preview"></div>' + $(e.target).closest('.list').html();
$(e.target).closest('.list').html(html);
}
}
(4) drag
드래그 시작 후 드롭되기 전까지 계속 발생하는 이벤트로 드래그할 대상을 안 보이게 했다.
function drag(e){
$('.selected').hide();
scrollStop = true;
}
(5) drop
마우스를 누르고 있던 것을 떼고 드롭했을 때 발생하는 이벤트다.
단, 제대로 된 대상이 아니더라도 마우스를 떼는 순간 드롭이 되기 때문에 드롭되는 대상을 구분해야 한다.
또한 li가 아니더라도 drop 이벤트를 걸어줬기 때문에 li 외에 드롭되지 못하도록 해야 한다.
function drop(e){
e.preventDefault();
// 드래그 시작 시 저장했던 li html을 가져온다.
var html = e.dataTransfer.getData('text');
// li만 드롭
if(html.match('selected')){
if($(e.target).is('.box')){ // li 맨 아래에
$('#'+targetBox).find('.ul').append(html);
$('.selected').each(function(){ // 기존의 li 삭제
$(this).remove();
});
} else if($(e.target).is('.preview')){ // li 사이에
$('.preview').parent().before(html);
$('.selected').each(function(){ // 기존의 li 삭제
$(this).remove();
});
} else { // 잘못 드롭 됐을 때
$('.selected').trigger('click');
$('.selected').show();
}
} else {
$('.preview').remove();
$('.selectedBox').removeClass('selectedBox');
return;
}
}
(6) dragleave
드롭될 대상 밖으로 벗어났을 때 발생하는 이벤트다.
dragenter에서 구했던 div의 위치를 기준으로 벗어났는지 벗어나지 않았는지 체크했다.
function dragleave(e){
$('.preview').remove(); // 미리보기 지우기
if(e.clientY > bottomPos || e.clientY < topPos || e.clientX < leftPos || e.clientX > rightPos){
$('.selectedBox').removeClass('selectedBox');
}
}
(7) dragend
모든 드래그 앤 드롭이 끝났을 때 발생하는 이벤트로 초기화한다.
function dragend(e){
scrollStop = true;
$('.selected').show();
$('.preview').remove();
$('.selectedBox').removeClass('selectedBox');
}
4. 마치며
정리본으로 옮겨 쓰면서 변수명을 모두 다 바꾸고 생략한 것이 많기 때문에 직접 코드로 돌렸을 때 돌아가지 않을 수도 있고 엉성한 것도 많을 것입니다. 참고용으로만 보시길 바라며 비난 외의 모든 댓글은 환영합니다. :)
관련링크
- 다음글json 불러와서 적용하기2 23.08.21
댓글목록
등록된 댓글이 없습니다.