/** * actulus selectbox */ const selectBox = { init: function (selectBox) { // js random key generator function randomKey() { return Math.random().toString(36).substr(2, 9); } if (selectBox.getAttribute('data-id') === null) { this.createSelectBox(selectBox, randomKey()); } else { this.createSelectBox(selectBox, selectBox.getAttribute('data-id')); } }, /** * first of all, find all select boxes */ /** * create custom select box */ createSelectBox: function (selectBox, key) { // hide selectBox selectBox.style.display = "none"; // get data-id attribute const last_id = selectBox.getAttribute("data-id"); if (document.querySelector('.select-container[data-key="' + last_id + '"]')) { document.querySelector('.select-container[data-key="' + last_id + '"]').remove(); } selectBox.setAttribute('data-id', key); const selectContainer = document.createElement("div"); selectContainer.classList.add("select-container"); selectContainer.setAttribute('data-key', key) selectBox.parentNode.insertBefore(selectContainer, selectBox); const selectButton = document.createElement("button"); selectButton.classList.add("select-button"); selectButton.type = "button"; selectContainer.appendChild(selectButton); const selectedText = document.createElement("span"); selectedText.classList.add("selected-text"); selectButton.appendChild(selectedText); selectedText.innerHTML = (selectBox.options[selectBox.selectedIndex]) ? selectBox.options[selectBox.selectedIndex].text : " "; const selectArrow = document.createElement("span"); selectArrow.classList.add("select-arrow"); selectArrow.innerHTML = '' selectButton.appendChild(selectArrow); const selectOptionsWrapper = document.createElement("div"); selectOptionsWrapper.classList.add("select-options-wrapper"); selectContainer.appendChild(selectOptionsWrapper); if (selectBox.hasAttribute("data-search")) { const searchContainer = document.createElement("div"); searchContainer.classList.add("search-container"); selectOptionsWrapper.appendChild(searchContainer); const searchInput = document.createElement("input"); searchInput.classList.add("search-input"); searchInput.type = "text"; searchInput.placeholder = "Search..."; searchContainer.appendChild(searchInput); this.search(selectBox, selectContainer, key, searchInput, selectOptionsWrapper); } const selectOptions = document.createElement("div"); selectOptions.classList.add("select-options"); selectOptionsWrapper.appendChild(selectOptions); // prepare content this.prepareContent(selectBox, selectContainer, selectOptions); selectBox.addEventListener('DOMSubtreeModified', () => { setTimeout(() => { if (!selectContainer.classList.contains('acting')) { this.prepareContent(selectBox, selectContainer, selectOptions); } }, 100); }); // addEventListeners this.addEventListeners(selectBox, selectContainer, selectOptions, key); // search }, prepareContent: function (selectBox, selectContainer, selectOptions,) { const selectedText = selectContainer.querySelector('.selected-text'); const options = selectBox.querySelectorAll("option"); selectOptions.innerHTML = ''; let i = 0; options.forEach((option) => { option.setAttribute("data-index", i); const optionElement = document.createElement("button"); optionElement.classList.add("select-option"); optionElement.type = "button"; optionElement.innerHTML = option.text; optionElement.setAttribute("data-value", option.value); optionElement.setAttribute("data-index", i); selectedText.innerHTML = (selectBox.options[selectBox.selectedIndex]) ? selectBox.options[selectBox.selectedIndex].text : " "; if (option.selected) optionElement.classList.add("active"); selectOptions.appendChild(optionElement); optionElement.addEventListener("click", (e) => { selectContainer.classList.add('acting'); selectedText.innerHTML = e.target.innerHTML; selectBox.value = e.target.getAttribute("data-value"); // add active class to selected option const options = selectContainer.querySelectorAll(".select-option"); options.forEach((option) => { option.classList.remove("active"); }); e.target.classList.add("active"); // fix for same value's option // remove selectbox selected attribute if (selectBox.querySelector('[selected]')) selectBox.querySelector('[selected]').removeAttribute('selected'); selectBox.querySelector("option[data-index='" + e.target.getAttribute("data-index") + "']").setAttribute("selected", "selected"); selectBox.selectedIndex = e.target.getAttribute("data-index"); var event = document.createEvent('HTMLEvents'); event.initEvent('change', true, false); selectBox.dispatchEvent(event); this.switchBox(selectContainer, 'close'); setTimeout(() => { selectContainer.classList.remove('acting'); }, 100); }); i++; }); }, /** * add event listeners to select options * @param {HTMLElement} selectBox * @param {HTMLElement} selectOptions **/ addEventListeners: function (selectBox, selectContainer, selectOptions, key) { const selectButton = selectContainer.querySelector(".select-button"); selectButton.addEventListener("click", (e) => { if (selectContainer.classList.contains("open")) { this.switchBox(selectContainer, 'close'); } else { this.switchBox(selectContainer, 'open'); } }); document.addEventListener("click", (e) => { if (!e.target.closest(".select-container[data-key='" + key + "']")) { this.switchBox(selectContainer, 'close'); } }); }, search: function (selectBox, selectContainer, key, searchInput, selectOptionsWrapper) { const selectOptions = selectContainer.querySelector(".select-options"); searchInput.addEventListener("keyup", (e) => { const filter = searchInput.value.toUpperCase(); const options = selectContainer.querySelectorAll(".select-option"); let i = 0; for (i = 0; i < options.length; i++) { let option = options[i]; let txtValue = option.textContent || option.innerText; if (txtValue.toUpperCase().indexOf(filter) > -1) { option.style.display = ""; } else { option.style.display = "none"; } } }); }, switchBox: function (selectContainer, type) { const optionsWrapper = selectContainer.querySelector(".select-options-wrapper"); if (type === "open") { optionsWrapper.style.transform = "translateY(-10px)"; optionsWrapper.style.opacity = "0"; optionsWrapper.style.transition = "all .14s ease-in-out"; selectContainer.classList.add("open"); let optHeight = optionsWrapper.querySelector('.select-options').offsetHeight; let top = selectContainer.offsetTop; let height = selectContainer.offsetHeight; let windowHeight = window.innerHeight; let scrollTop = window.scrollY; let elementPos = top - scrollTop + height; if (elementPos > windowHeight - optHeight && top - scrollTop < windowHeight - optHeight + top - scrollTop && top > optHeight) { optionsWrapper.style.bottom = `${height + 10}px`; optionsWrapper.style.top = 'initial'; } setTimeout(() => { optionsWrapper.style.transform = "translateY(0)"; optionsWrapper.style.opacity = "1"; }, 1); } else if (type === "close") { setTimeout(() => { optionsWrapper.style.transform = "translateY(-10px)"; optionsWrapper.style.opacity = "0"; }, 1); setTimeout(() => { optionsWrapper.style = ''; selectContainer.classList.remove("open"); }, 142); } } }; const selectBoxes = document.querySelectorAll("select"); selectBoxes.forEach((sb) => { setTimeout(() => { selectBox.init(sb); }, 100); });