본문 바로가기
JS

요소를 움직여? 드래그 이벤트

by 개발자doc 2024. 5. 11.
목차
1. ondragstart
2. ondragend
3. ondragenter
4. ondragleave
5. ondragover
6. ondrop
7. 드래그 앤 드롭 구현하기(리스트 위치 교체)
1. ondragstart
  • 어떤 요소에서 드래그를 하게 되면 발생하는 이벤트

2. ondragend
  • 드래그를 진행하다가 마우스를 땠을 때 발생하는 이벤트

3. ondragenter
  • 드래그 하던 요소가 다른 요소 위에 위치했을 때 발생하는 이벤트

4. ondragleave
  • 드래그 하던 요소가 다른 요소 위에 위치했다가 떠나면 발생하는 이벤트

5. ondragover
  • 드래그 하던 요소가 다른 요소 위에 위치했을 때 발생하는 이벤트

dragenter와 유사하게 다른 요소 위에 위치했을 때 이벤트가 발생하지만 다른 점은 enter의 경우 다른 요소 위에 위치하면 이벤트가 한 번 발생하지만 over의 경우에는 다른 요소 위에서 움직일때 마다 발생한다.

해당 이벤트의 주요 목적은 기본적으로 브라우저에서는 drop을 허용하지 않는데 허용하기 위해 사용된다.

<script>
document.ondragover= (e) => {
    e.preventDefault();
}
</script>

 

그렇다면 왜 브라우저는 drop을 허용하지 않을까?

대부분의 웹 페이지들은 사용자 경험에 의해 각 요소들을 접근하기 편한 곳에 위치시킨다. 하지만 마구잡이로 드랍을 허용하면 이런 편의성을 해칠 수가 있다.

 

6. ondrop
  • 드래그를 진행하다가 유효한 다른 요소 위에서 마우스를 땠을 때 발생

end와 비슷해보이지만 drop은 마우스를 땠을 때 드래그 한 요소를 해당 위치에 떨어뜨리기 위해 사용되며 유효한 요소 위에 떨어뜨렸을 때 이벤트가 발생한다. 

7. 드래그 앤 드랍 구현하기(리스트 위치 교체)

1. 먼저 드래그 앤 드랍을 할 요소를 만들기

위치를 변경할 때 인덱스로 참조하기 위해 dataset을 지정해준다.

미리 지정해도 되고 js를 통해 동적으로 지정해도 된다.

  <div class="container">
        <div class="item" data-index='0' draggable="true">첫번째 요소</div>
        <div class="item" data-index='1' draggable="true">두번째 요소</div>
        <div class="item" data-index='2' draggable="true">세번째 요소</div>
        <div class="item" data-index='3' draggable="true">네번째 요소</div>
        <div class="item" data-index='4' draggable="true">다섯번째 요소</div>
    </div>

2. 드래그할 요소를 선택할 변수 만들기

드래그한 요소를 저장할 변수를 만들고 아직은 선택을 하지 않았으니 null로 초기화한다.

<script>
    let _target = null;
</script>

 

3. 드랍허용하기 

브라우저에서는 기본적으로 드랍을 허용하지 않는다고 했다. 드랍으로 위치를 변경하기 위해서는 드랍을 허용해야만 한다. 따라서 preventDefault함수를 이용하여 기본동작을 막아야한다.

<script>
    let _target = null;
    document.ondragover = (e) => {
    	if(e.target.classList.contains("item")&&_target!==null){
            e.preventDefault();
        }
    }
</script>

 

4. 드래그 했을 때 요소를 선택하기

요소를 선택 후 움직였을 때 해당 요소를 선택하여 저장한다. 이렇게 선택된 요소는 나중에 다른 요소 위에서 드랍을 했을 때 위치가 서로 바뀌게 될 것이다.

<script>
    let _target = null;

    document.ondragover = (e) => {
    	if(e.target.classList.contains("item")&&_target!==null){
            e.preventDefault();
        }
    }
    
    document.ondragstart = function (e) {
        if (e.target.classList.contains("item") === true) {
            _target = e.target;
        }
    }
</script>

5. 드랍시 리스트의 위치가 바뀌게 설정

querySelector를 사용해서 해당 nodeList를 가져온다고 해도 배열이 아닌 유사배열 객체이기 때문에  바로 순서를 바꿀 수는 없다. 따라서 배열로 바꾸어야 하는데 스프레드 연산자를 사용하여 nodeList를 배열로 변환해준다

<script>
    let _target = null;

    document.ondragover = (e) => {
    	if(e.target.classList.contains("item")&&_target!==null){
            e.preventDefault();
        }
    }
    
    document.ondragstart = function (e) {
        if (e.target.classList.contains("item") === true) {
            _target = e.target;
        }
    }
    
      document.ondrop = function (e) {
        if (e.target.classList.contains("item") === true && _target !== null) {
            const elList = document.querySelectorAll('.item');
            const elArr = [...elList];

            [elArr[parseInt(e.target.dataset.index)], elArr[parseInt(_target.dataset.index)]] = [elArr[parseInt(_target.dataset.index)], elArr[parseInt(e.target.dataset.index)]];
        }
    }
</script>

첫번째 요소를 두번째 요소에 올리고 드랍을 했을 때 배열의 위치가 바뀌었다. 하지만 코드를 보면 각 요소의 dataset의 값을 배열의 index로 사용하고 있다. 위치가 변경이 되어도 dataset은 그대로이기 나중에 문제가 생길 수 있기 때문에 dataset을 위치에 맞게 변경해주어야 한다.

 [e.target.dataset.index, _target.dataset.index] = [_target.dataset.index, e.target.dataset.index]

 

 

6. 순서가 변경된 리스트 렌더

기존에 있던 리스트의 순서가 변경되었으니 화면으로 보여주기 위해서는 다시 렌더를 해주어야 한다.

<script>
    let _target = null;

    document.ondragover = (e) => {
    	if(e.target.classList.contains("item")&&_target!==null){
            e.preventDefault();
        }
    }
    
    document.ondragstart = function (e) {
        if (e.target.classList.contains("item") === true) {
            _target = e.target;
        }
    }
    
 document.ondrop = function (e) {
        if (e.target.classList.contains("item") === true && _target !== null) {
            const listForm = document.querySelector(".container"); //리스트를 담는 컨테이너
            const elList = document.querySelectorAll('.item');
            const elArr = [...elList];

            [elArr[parseInt(e.target.dataset.index)], elArr[parseInt(_target.dataset.index)]] = [elArr[parseInt(_target.dataset.index)], elArr[parseInt(e.target.dataset.index)]];
            [e.target.dataset.index, _target.dataset.index] = [_target.dataset.index, e.target.dataset.index]
            
            //기존 리스트를 지움
            listForm.innerHTML = '';
            //배열을 통해 변경된 리스트 다시 렌더
            elArr.map((el) => {
                listForm.append(el);
            })

        }
    }
</script>

브라우저 화면에서 드래그해서 순서를 바꾸었을 때 성공적으로 동작하는 것을 볼 수 있다. 

'JS' 카테고리의 다른 글

FETCH API  (0) 2024.06.23
동기 VS 비동기  (0) 2024.05.17
이벤트 등록하기  (0) 2024.05.10
배열일까? 아닐까? 유사 배열 객체  (0) 2024.05.09
클로저  (0) 2024.05.07