Muscardinus

Dropdown Menu 본문

FrontEnd/Basic Skills

Dropdown Menu

Muscardinus 2022. 3. 13. 23:54
728x90

select의 dropdown을 구현하기 위해서는

backdrop에 selection 외부를 클릭했을때 selection container가 사라지게 하는 event listener를 넣는 것이 좋다

 

<!DOCTYPE html>
<html>
    <head>
        <title>UI Component - Dropdown List</title>
    </head>
    <body>
        <div class="container">
            <div id="dropdown" 
                class="dropdown-container">
            </div>
            <div id="userlist"
                class="table-container">
            </div>
        </div>
        <div class="back-drop">
            <div class="dropdown-item-list-box">
                <div class="dropdown-item-box selected"><span>Google</span></div>
                <div class="dropdown-item-box"><span>Yahoo</span></div>
            </div>
        </div>
    </body>
</html>

 

/*
* title: DropDownList class
* description: dropdown ui component
* configuration:
    idField: 해당 row의 id로 활용할 key
    labelField: 해당 row의 label로 활용할 key
    data: 표현될 리스트
    changeEvent: 선택된 데이터를 받을 수 있는 callback
    selector: 선택된 데이터를 표시할 라벨 영역
    backdrop: dropdown list를 출력할 영역
*/
export class DropDownList {
    constructor(configuration) {
        // 내부 변수
        this.rowHeight = 27;
        // 라벨이 없을 경우
        this.emptyLabel = '선택하세요.';
        // 현재 선택된 index -1은 선택이 안되어 있음을 의미.
        this.currentIndex = -1;

        // data list의 id 필드 명시
        this.idField = configuration.idField || 'id';
        // data list의 label 필드 명시
        this.labelField = configuration.labelField || 'label';
        // 옵션 데이터 리스트
        this.data = configuration.data;
        // change 변경에 따른 callback
        this.callback = configuration.changeEvent;
        // 선택할 때 마다 선택된 라벨을 표현해줄 element setup
        this.dropdownLabel = this.initialize(document.querySelector(configuration.selector), this.emptyLabel);
        // backdrop 영역 element setup
        this.backdrop = document.querySelector(configuration.backdrop);
        this.dropdownItem = this.displayDropdownItemList(this.backdrop, configuration.data);
        // event listen
        this.eventBinding();
    }

    /*
    * title: dropdown label display method
    * input: display 되는 element, label 없을 시 출력할 string
    * output: dropdown label 영역 element
    * description: 최초 생성할 때 dropdown 라벨 영역을 출력한다. 라벨 영역 템플릿을 관리한다.
    */
    initialize(selector, emptyLabel) {
        const dropdownLabel = document.createElement('div');
        dropdownLabel.classList.add('dropdown-select-label-container');
        let render = `
            <span class="dropdown-select-label">${emptyLabel}</span>
            <div class="dropdown-select-arrow-container">
                <div class="dropdown-select-arrow"></div>
            </div>
        `;
        dropdownLabel.insertAdjacentHTML('afterbegin', render);
        selector.appendChild(dropdownLabel);
        // 생성된 라벨 영역을 리턴해준다.
        return dropdownLabel;
    }

    /*
    * title: dropdown item list display method
    * input: display 되는 element, label 없을 시 출력할 string
    * output: dropdown label 영역 element
    * description: dropdown item 영역을 출력한다.
    */
    displayDropdownItemList(selector, data) {
        if (!selector) return;

        let render = '<div class="dropdown-item-list-box">';
        for (let i = 0; i < data.length; i++) {
            render += `
                <div class="dropdown-item-box">
                    <span>${data[i][this.labelField]}</span>
                </div>
            `
        }
        render += '</div>';
        // 리스트를 갱신해야하므로 innerHTML사용함.
        selector.innerHTML = render;

        // q1. label position에 dropdown list 영역을 출력하시오.
        // TODO: Write JS code here!'
        // label position에 dropdown list 영역을 출력
        const target = document.querySelector('.dropdown-item-list-box');
        const labelRect = this.dropdownLabel.getBoundingClientRect();
        target.style.cssText = `
            position: absolute;
            width: ${labelRect.width}px;
            top: ${labelRect.top + 5}px;
        `;

        return target;
    }

    /*
    * title: event binding method
    * description: 모든 이벤트를 처리한다.
    */
    eventBinding() {
        // backdrop 영역 클릭 시 이벤트
        this.backdrop.addEventListener('click', () => {
            // q2. backdrop 영역 클릭 시의 이벤트를 처리하시오.
            // TODO: Write JS code here!'
            this.backdrop.style.cssText = `
                display: none;
            `;
        });

        // label 영역 클릭 시 이벤트
        this.dropdownLabel.addEventListener('click', () => {
            // q3. label 영역 클릭 시의 이벤트를 처리하시오.
            // TODO: Write JS code here!'
            this.backdrop.style.cssText = `
                display: block;
            `;
        });

        document.querySelectorAll('.dropdown-item-box')
        .forEach((item, index) => {
            item.addEventListener('click', (event) => {
                const currentOption = this.retriveOptionByIndex(index);
                if (this.currentIndex > -1) {
                    this.unselectedDropdownItem(this.currentIndex);
                }
                this.currentIndex = index;

                if (this.currentIndex > -1) {
                    this.selectedDropdownItem(this.currentIndex);
                }

                // q4. 함수(dispatchEvent)를 참조하여 id, label 값을 인자로 넘겨 이벤트를 발생시키시오.
                // TODO: Write JS code here!'
                this.dispatchEvent({
                    id: currentOption[this.idField],
                    label: currentOption[this.labelField],
                })
            });
        });
    }

    /*
    * title: dropdown event execute method
    * description: dropdown 에서 발생된 이벤트를 외부로 전송
    */
    dispatchEvent(item) {
        // 선택된 아이템의 라벨로 변경하고, dropdown item 영역을 화면에서 보이지 않도록 하시오.
        this.dropdownLabel.querySelector('.dropdown-select-label').innerHTML = item[this.labelField];
        this.backdrop.style.cssText = 'display: none;';
        // 선택된 아이템을 외부로 callback 함수를 통해 전달.
        this.callback(item);
    }

    selectedDropdownItem(index) {
        document.querySelectorAll('.dropdown-item-box')[index].classList.add('selected');
    }

    unselectedDropdownItem(index) {
        document.querySelectorAll('.dropdown-item-box')[index].classList.remove('selected');
    }

    retriveOptionByIndex(index) {
        const targetOption = this.data[index];
        !targetOption.label ? this.emptyLabel : targetOption.label;
        return targetOption;
    }
}
728x90

'FrontEnd > Basic Skills' 카테고리의 다른 글

Rating UI  (0) 2022.03.19
Radio Box  (0) 2022.03.17
Auto Complete  (0) 2022.03.13
Search bar  (0) 2022.02.09
Scroll Top  (0) 2022.01.31
Comments