Custom select

Dependencies

Html

                
<select name="custom-selector" data-custom-select>
    <option selected disabled>Se de fede valgmuligheder i denne vælger</option>
    <option value="test7">Test 7</option>
    <option disabled value="test8">Test 8</option>
    <option value="test9">Test 9</option>
    <option value="test10">Test 10</option>
    <option value="test11">Test 11</option>
    <option value="test12">Test 12</option>
</select>
    

JavaScript - CustomSelectModule.ts

        
const dropdownHeader: string = "[data-dropdown-header]";
const selectItem: string = "[data-custom-select]";

export class customSelectModule {
	activeClass: string = "is-active";
	hiddenClass: string = "is-hidden";
    constructor() {
        this.createDropdown();
        this.toggleDropdownContent();
        this.addEventListeners();
    }

    private createDropdown = () => {
        let selectItems = document.querySelectorAll(selectItem)

        for (let i = 0; i < selectItems.length; i++) {
            let selectHtmlItem: HTMLElement = < HTMLElement>selectItems[i];

            // creating dropdown container
            let dropdown: HTMLElement = document.createElement("div");
            dropdown.classList.add("dropdown");
            dropdown.setAttribute("data-dropdown", "");
            dropdown.setAttribute("data-dropdown-close-on-click", "");
            dropdown.setAttribute("data-dropdown-set-header", "");
            dropdown.setAttribute("data-custom-selector", "");
            dropdown.setAttribute("data-close-attr", "");

            // creating header container
            let dropdownHeader: HTMLElement = document.createElement("div");
            dropdownHeader.classList.add("dropdown__header");
            dropdownHeader.setAttribute("data-dropdown-header", "");

            // creating header element
            let dropdownHeaderSpan: HTMLElement = document.createElement("span");
            dropdownHeaderSpan.classList.add("dropdown__header-text");

            // creating content container
            let dropdownContent: HTMLElement = document.createElement("div");
            dropdownContent.classList.add("dropdown__content");
            dropdownContent.setAttribute("data-dropdown-content", "");

            // creating UL element for list
            let dropdownContentList: HTMLElement = document.createElement("ul");
            dropdownContentList.classList.add("dropdown__content-list", "reset-ul");

            // getting option elements from select element in the DOM
            let select: HTMLCollection = selectHtmlItem.children;

            // adding scroll, if more than 12 items in option list
            if (select.length > 12) {
                dropdownContent.style.overflowY = "scroll";
            }

            // setting first element as initial header
            dropdownHeaderSpan.innerHTML = select[0].innerHTML;

            // looping select elements, and adding them to content list
            for (let i = 1; i < select.length; i++) {
                let option: HTMLOptionElement = < HTMLOptionElement>select[i];
                let content: string = option.innerHTML;
                let value: string = option.value;

                let dropdownContentListItem = document.createElement("li");
                dropdownContentListItem.classList.add("dropdown__content-list-item");
                dropdownContentListItem.setAttribute("data-custom-selector-value", value);
                dropdownContentListItem.setAttribute("data-custom-selector-item", "");
                dropdownContentListItem.innerHTML = content;

                if (select[i].hasAttribute("disabled")) {
                    dropdownContentListItem.setAttribute("data-custom-selector-disabled", "disabled");
                }
                else {
                    dropdownContentListItem.setAttribute("tabindex", "0");
                }

                if (select[i].hasAttribute("selected")) {
                    dropdownHeaderSpan.innerHTML = select[i].innerHTML;
                }

                dropdownContentList.appendChild(dropdownContentListItem);
            }

            // adding elements together
            dropdownHeader.appendChild(dropdownHeaderSpan);
            dropdown.appendChild(dropdownHeader);
            dropdownContent.appendChild(dropdownContentList);
            dropdown.appendChild(dropdownContent);

			// hiding old select element, and adding new to DOM
			selectHtmlItem.classList.add(this.hiddenClass);
            selectHtmlItem.parentNode.insertBefore(dropdown, selectHtmlItem)
        }

    };

    private addEventListeners = () => {

        let allSelectors = document.querySelectorAll("[data-custom-selector]");

        for (let i = 0; i < allSelectors.length; i++) {

            let singleSelector = allSelectors[i];

            let parentIndex = i;

            let allSelectorItems = singleSelector.querySelectorAll("[data-custom-selector-item]")
            for (let i = 0; i < allSelectorItems.length; i++) {
                let element: HTMLElement = < HTMLElement>allSelectorItems[i];
                
                element.addEventListener("click", () => { customSelectModule.onClickHandler(element, parentIndex) });
                element.addEventListener("keypress", (e) => {
                    if (e.key === "Enter") {
                        customSelectModule.onClickHandler(element, parentIndex)
                    }
                });
            }
        }
    }

    static onClickHandler = (item: HTMLElement, parentIndex: number) => {
        let oldSelect = document.querySelectorAll(selectItem)[parentIndex];
        if (item.hasAttribute("data-custom-selector-disabled") == false) {
            // get element from old select to be set to "selected"
            let selectedOption: HTMLOptionElement = oldSelect.querySelector(
                "[value='" + item.getAttribute("data-custom-selector-value") + "']"
            );

			// removing any selected attributes from old select
            for (let i = 0; i < oldSelect.children.length; i++) {
                if (oldSelect.children[i].hasAttribute("selected")) {
                    oldSelect.children[i].removeAttribute("selected");
                }
            }

			// setting selected attribute on the selected option
            selectedOption.setAttribute("selected", "selected");

            // setting clicked value as the dropdown header
            let value: string = item.innerText;
            item.offsetParent.parentNode.querySelector(dropdownHeader).firstChild.textContent = value;

            // removing active class from dropdown element
            let itemDropdownParent: HTMLElement = < HTMLElement>item.offsetParent.parentNode

            itemDropdownParent.classList.remove("is-active");
        }
    };

    private toggleDropdownContent = () => {
		let allDropdowns = document.querySelectorAll("[data-dropdown]");

        for (let i = 0; i < allDropdowns.length; i++)
        {
            let dropdownElement: HTMLElement = < HTMLElement>allDropdowns[i];
            let index = i;

            dropdownElement.querySelector(dropdownHeader).addEventListener("click", () => {

				let allDropdownElements = document.querySelectorAll("[data-dropdown]");

                for (let i = 0; i < allDropdownElements.length; i++) {
                    if (i != index) {
                        allDropdownElements[i].classList.remove(this.activeClass);
                    }
                }
                let target: HTMLElement = < HTMLElement>event.currentTarget;
                target.parentElement.classList.toggle(this.activeClass);
            })
        }
    };
}