우리는 한다, 개발을.

jQuery 드래그 이벤트

⌛ 9 mins

jQuery 드래그 이벤트

스크립트 요청 작업 중 드래그로 다중선택을 하는 기능 구현이 있었다.

간단하게 드래그 이벤트로 선택된 구역의 체크박스를 체크해주는 기능이었다.

하 지 만 jQuery로 드래그 기능 만들어 본 적 없,,,어.,,,,🥲

jQuery 이벤트는 넘모넘모 많기 때문에 어떤 이벤트가 있는지 줄줄 외우기엔 내 뇌 내 용량의 한계가 있기 때문에 늘 구글링의 도움을 얻는다.

[Events jQuery API Documentation](https://api.jquery.com/category/events/)

여기에는 다양한 jQuery 이벤트함수가 정리되어 있다.

한 번도 안 써본 이벤트가 존많문…ㅎ 당연함.. 늘 쓰던 것만 씀….

암튼~ 이렇게 많은 이벤트가 있으니까 drag.. 어적후저적후 이벤트도 있갯즤^——————^

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

왜요?

왜 업서요?ㅎ

어의가 업내;ㅋ

-

이런 나에게 떠오른 방법은 한 땀 한 땀 마우스 이벤트를 만들어 주는 것! 😀

jQuery에는 내가 사용해보았던 마우스 이벤트가 정말 많기 때문에 이걸 잘 조합하면 드래그 이벤트를 충분히 구현해 낼 수 있을 것 같았다.


일단 드래그라는 행위는 마우스 왼쪽 버튼으로 어떤 요소 위에서 mousedown 을 한 뒤 그 상태를 유지하며 이동 후 다른 요소 위에서 mouseup 을 하는 행위의 조합이다.

클릭과 동작이 비슷하지만 여러 요소에서 이벤트가 발생될 수 있다는 점에서 조금 다른 것 같다.

-

드래그 이벤트를 적용하는 스크립트는 두가지인데 하나는 하나의 열로 되어있는 테이블이고 다른 하나는 여러개의 열로 이루어진 테이블이었다.

시간대별로 드래그를 해서 선택을 해야 하는 것이기 때문에 열이 1개일 경우는 상관 없지만 열이 여러개일 때에는 같은 열에 있는 요소위에서만 이벤트가 작동되게 하고 싶었다.

var mouseActive = false;

$('div[question-name="I1X1"]').find('table td.td-radio').mousedown(function(event){
    event.preventDefault();
    $(this).find('input[type=checkbox]').click();
    mouseActive = true;
});

table 의 td요소에 mousedown 이벤트를 걸어주면 드래그를 시작 할 때 함수가 실행된다. 이 때 event.preventDefault(); 로 브라우저 기존의 마우스 이벤트를 막아주어야 한다. 안 하면 안 예뻐짐. (파랗게 텍스트 선택 이벤트가 같이 실행된다…😥)

나는 드래그 하는 요소 내부의 checkbox를 체크해주거나 체크를 해제하는 기능을 구현했는데 배경색을 바꾸거나 border 디자인을 바꿀 수도 있다.

-

mouseActive 변수는 boolean 값으로 마우스 왼쪽이 클릭되어있는 상태 (down 되어있는 상태)를 나타내며 처음 mousedown 이벤트가 실행 될 때 true가 된다.

-

$('div[question-name="I1X1"]').find('table td.td-radio').mouseenter(function(event){
	  event.preventDefault();
	  if(mouseActive){
	    $(this).click();
	  }
});

마우스 왼쪽이 눌린 채로 이동하면 마우스가 지나는 요소에 대해서도 이벤트를 걸어주어야 한다. mouseenter 이벤트는 요소 안으로 마우스가 들어오는 순간을 감지하는데 mouseover 이벤트와 비슷하다. 이 둘의 차이점은 이벤트 버블링이 일어나는지의 여부이다. mouseenter 이벤트는 이벤트 버블링이 일어나지 않아 자기 자신만이 이벤트를 받고 이벤트가 상위 요소로 전파되지 않는다. 즉 target 과 currentTarget 이 항상 일치한다. 또한 preventDefault메서드를 호출하여 이벤트의 기본동작을 취소할 수 있기 때문에 나는 mouseenter 이벤트를 사용하게 되었다.

.td-radio가 아닌 다른 요소에서 mousedown을 했을 경우 mouseActive의 상태가 false이기 때문에 클릭 이벤트가 동작하지 않는다.

-

이제 마지막으로 mouseup이벤트를 생성해야 하는데 여러가지 경우의 수가 생긴다.

1) 마우스 이벤트가 걸리는 .td-radio의 열 위에서 mouseup이벤트가 동작할 경우, 이벤트가 동작한 td까지 checkbox를 클릭해주면 된다.

2) 그 외의 경우 ( 이벤트가 걸리지 않는 td의 열 위에서 mouseup, table 밖에서 mouseup) 마지막으로 마우스가 머물렀던 .td-radio의 checkbox까지만 클릭해야 한다.

3) .td-radio가 아닌 다른 요소에서 mousedown 후 .td-radio에서 mouseup이 동작 할 경우에는 클릭이벤트가 동작하면 안된다.

1) 이 때는 mouseenter 이벤트 동작으로 인해 checkbox 의 click 이벤트가 동작하므로 mouseActive 변수의 값만 false로 바꾸어주면 된다.

2) 이 경우는 .td-radio 요소에서 마우스가 이탈하는 순간이기 때문에 mouseleave이벤트를 활용하여 mouseActive 변수의 값을 false로 바꾸어준다.

3) 다른 요소에서 mousedown이 일어나게 되면 mouseActive 변수의 값이 false이기 때문에 checkbox 의 클릭 이벤트가 동작하지 않는다.

$(document).mouseup(function(event){
    if(mouseActive){
        mouseActive = false;
    }
});
$('div[question-name="I1X1"]').find('table').mouseleave(function(event){
    if(mouseActive){
        mouseActive = false;
    }
});

-

-

그런데 이 코드에는 치명적인 에러가 있었다.

단일의 체크박스 선택이 안된다는 것이었다….^^ 왜이러는데…ㅎ

단일 선택을 하게 되면 mousedown이벤트와 mouseenter이벤트의 click이 중복으로 먹혀서 그런 거 같다…

그래서 .td-radio 요소 단일 선택 경우를 생각하여 처음 mousedown이벤트로 체크박스가 클릭 된 요소의 id값을 변수에 저장해두고 mouseup했을 때 요소의 id값과 비교하는 로직을 추가했다.

 
var mouseActive = false;
**var tdId = '';**

$('div[question-name="I1X1"]').find('table td.td-radio').mousedown(function(event){
    event.preventDefault();
    **tdId = $(this).find('input[type=checkbox]').attr('id');**
    $(this).find('input[type=checkbox]').click();
    mouseActive = true;
});
$('div[question-name="I1X1"]').find('table td.td-radio').mouseenter(function(event){
    event.preventDefault();
    if(mouseActive){
			$(this).click();
    }
})**.mouseup(function(event){
    event.preventDefault();
    var $chk = $(this).find('input[type=checkbox]');    
    if ($chk.attr('id') === tdId) {
        $(this).find('input[type=checkbox]').click();
    }
});**
$(document).mouseup(function(event){
    if(mouseActive){
        tdId = '';
        mouseActive = false;
    }
});
$('div[question-name="I1X1"]').find('table').mouseleave(function(event){
    if(mouseActive){
        tdId = '';
        mouseActive = false;
    }
});

잘 되는군😆

-

열이 여러개인 경우는 mousedown이벤트 내부에서 해당 요소의 hkey, vkey값을 변수에 저장해둔 후 mouseenter이벤트 내부에서 요소의 hkey와 vkey를 비교해준다.

같은 열만 이벤트를 걸기 위해서 hkey는 같고 vkey는 다른 조건에서만 체크박스의 클릭 이벤트를 실행시킨다. 만약 hkey가 다른 요소로 마우스가 옮겨갈 경우 mouseleave로 보고 저장해둔 hkey, vkey 변수값을 초기화하고 mouseActive를 false로 변경한다.

var mouseActive = false;
**var clickHkey = '';
var clickVkey = '';**

$('div[question-name="I3X1"]').find('table td.td-radio').mousedown(function(event){
    event.preventDefault();
    **clickHkey = $(this).attr('hkey');
    clickVkey = $(this).attr('vkey');**

    $(this).find('input[type=checkbox]').click();
    mouseActive = true;
});
$('div[question-name="I3X1"]').find('table td.td-radio').mouseenter(function(event){
    event.preventDefault();
    var tdHkey = $(this).attr('hkey');
    var tdVkey = $(this).attr('vkey');
    if(mouseActive){
        **if(tdHkey === clickHkey && clickVkey !==tdVkey){
            $(this).click();
        }else{
            clickHkey = '';
            clickVkey = '';
            mouseActive = false;
        }**
    }
}).mouseup(function (event) {
    event.preventDefault();
    var $chk = $(this).find('input[type=checkbox]');
    if ($chk.attr('vkey') === clickVkey) {
        $(this).find('input[type=checkbox]').click();
    }
});
$(document).mouseup(function(event){
    if(mouseActive){
        clickHkey = '';
        clickVkey = '';
        mouseActive = false;
    }
});
$('div[question-name="I3X1"]').find('table').mouseleave(function(event){
    if(mouseActive){
        clickHkey = '';
        clickVkey = '';
        mouseActive = false;
    }
});


작업을 마무리하며 좀 더 찾아보니 on()메소드를 사용해서 드래그 이벤트 리스너를 만들어주면 된다고 한다. 이 방법은 드래그 앤 드롭에 더 적합한 느낌이다. 드래그해서 원하는 곳에 드랍하는 기능을 만들 때 쓰면 유용할 것 같다.


드래그 앤 드랍을 쉽게 하는 방법으로 .draggable() 이라는 것도 있다.

var $tdRadio = $('div[question-name="I1_1"]').find('table td.td-radio');
$.each($tdRadio, function(i, td){
    $(td).draggable();
});

요소에 draggable 관련된 클래스가 부여되고

간편한 코드로 이렇게 요소 자체를 드래그 앤 드랍 할 수 있다.



이렇게 jQuery로 드래그 이벤트를 만들어보았다. 간단하고 어렵지 않은 내용이지만 작업하면서 재미를 크게 느꼈고 역시 프론트엔드 작업은 승질나면서도 재미가 있는 묘한 매력이 있다….😋