Drag And Drop

배고픈 징징이 ㅣ 2023. 4. 5. 13:44

1. 시작

Drag And Drop 기능을 외부 Library없이 Javascript로 개발해보자.

이 프로젝트에서는 TypeScript로 작성이 되었으며, Html을 직접 작성하지 않고 코드화 되어 있다.

 

2. Draggable 속성 추가

우선 원하는 요소에 드래그가 가능하게 draggable 속성을 추가해준다.

export class Options extends FormItem {
    panel : View;
    options : Array<any> = [];

    constructor(model : OptionModel, parent? : View) {
        super("simms-options", parent);
        const _this = this;
        new Button({
            name: I18n.get("add"),
            listener: () => {
                //실제로 드래그가 될 요소 생성
                this.createOption();
            }
        }, this);

        this.panel = new View("simms-option-list", this);
    }

    createOption(value? : string){
        const optionTag = new View("simms-option", this.panel)
        const option = new TextInput(optionTag);
        if(value) option.value = value;
        new Icon({name:"delete", listener: e => {this.delete(e);}}, optionTag);

        //draggable 속성을 주어서 드래그가 가능하게 설정
        optionTag.el.setAttribute("draggable", "true");

        this.options?.push(option);
    }

    ...
}

 

3. Drag에 따른 Event 설정

드래그의 start, end, over Event를 설정해 준다.

start와 end 이벤트는 드래그가 될 자식 요소에 작성하고, over 이벤트는 드래그가 될 요소의 부모 요소에 작성한다.

드래그시 바뀌어야될 위치는 getDragAfterElement 함수에서 계산한다.

 

...

createOption(value? : string){
    const optionTag = new View("simms-option", this.panel)
    const option = new TextInput(optionTag);
    if(value) option.value = value;
    new Icon({name:"delete", listener: e => {this.delete(e);}}, optionTag);

    optionTag.el.setAttribute("draggable", "true");
    optionTag.el.addEventListener("dragstart", () => {
        //dragging 클래스는 css opacity : 0.5로 불투명하게 설정
        optionTag.el.classList.add("dragging");
    });
    optionTag.el.addEventListener("dragend", () => {
        optionTag.el.classList.remove("dragging");
    });

    //dragover는 drag가 되는 요소들의 부모 Element에서 작성
    //여기서는 부모 요소가 this.panel 이다.
    this.panel.el.addEventListener("dragover", e => {
        //이 if문은 드래그앤드랍을 위로 했을 시 afterElement가 dragging과 같은 값으로 바뀌는 이슈 때문에 추가했다.
        if(this.dragging != this.getDragAfterElement(e.clientY)) this.afterElement = this.getDragAfterElement(e.clientY);
        const dragging = document.querySelector(".dragging");

        if(dragging) this.panel.el.insertBefore(dragging, afterElement);
    });

    this.options?.push(option);
}

getDragAfterElement(y : number){
    return this.options.reduce(
        (closest, currentValue) => {
            const child = currentValue.el.parentElement;
            const box = child.getBoundingClientRect();
            const offset = y - box.top - box.height / 2;
            if(offset < 0 && offset > closest.offset) return {offset: offset, element: child};
            else return closest;
        }
        , {offset: Number.NEGATIVE_INFINITY}
    ).element;
}
    
...

 

Reduce 함수의 자세한 설명은 아래 링크 참조

https://pinokio5600.tistory.com/69

 

Array.reduce()

1. 설명 배열의 각 요소에 Reducer 함수를 실행하고, 하나의 결과값( 누산값 )을 반환한다. arr.reduce(callback[, initialValue]); 2. 매개변수 callback : 배열의 각 요소에 대해 실행할 함수. 네 개의 인수를 받

pinokio5600.tistory.com

 

반응형

'Client Side' 카테고리의 다른 글

forEach 순차처리의 불가능 (for문, for of문의 순차처리)  (0) 2023.04.06
Array.reduce()  (0) 2023.04.05
Modal Drag  (0) 2023.02.22
Datepicker MinDate  (0) 2023.01.30
필수사항 체크  (0) 2023.01.27