class SearchBox { container box = document.createElement('div') list = document.createElement('div') listItems = document.createElement('div') selected = document.createElement('div') searchField = document.createElement('input') cloneListItems = this.listItems callback dynamic = {} constructor(selector) { if (typeof selector === "string") { this.container = document.querySelector(selector) } else { this.container = selector; } if(this.container) { if (this.container.nodeName === 'SELECT') { this.dataRetrieve() } else { alert('Html element SELECT is required') } addEventListener('keydown', ({keyCode}) => { if (keyCode === 27) { this.clear() } }) } } clear() { this.box.classList.remove('open') this.searchField.value = '' this.listItems.querySelectorAll('.list-item').forEach(e => { e.classList.add('visible') e.classList.remove('hover') e.style.display = 'block' }) } getNextSibling(elem, selector) { var sibling = elem.nextElementSibling; while (sibling) { if (sibling.matches(selector)) return sibling; sibling = sibling.nextElementSibling } } getPreviousSibling(elem, selector) { var sibling = elem.previousElementSibling; if (!selector) return sibling; while (sibling) { if (sibling.matches(selector)) return sibling; sibling = sibling.previousElementSibling; } } keyDown() { this.searchField.addEventListener('keydown', ({keyCode}) => { if (keyCode === 38 || keyCode === 40) { event.preventDefault() const visibleList = this.listItems.querySelectorAll('.visible:not(.hover)'); const firstItem = visibleList[0]; const lastItem = this.listItems.querySelectorAll('.visible:not(.hover)')[visibleList.length - 1]; if (this.listItems.querySelectorAll('.visible.hover').length === 0) { if (keyCode === 38 && lastItem) lastItem.classList.add('hover') if (keyCode === 40 && firstItem) firstItem.classList.add('hover') } else { const hoverElement = this.listItems.querySelector('.visible.hover') hoverElement.classList.remove('hover') //up if (keyCode === 38) { const prev = this.getPreviousSibling(hoverElement, '.visible') if (prev && prev.classList.contains('visible')) { prev.classList.add('hover') console.log(prev.offsetTop) this.listItems.scrollTo({ top: prev.offsetTop - 160, }) } else { lastItem.classList.add('hover') this.listItems.scrollTo({ top: lastItem.offsetTop - 160, }) } } //down if (keyCode === 40) { const next = this.getNextSibling(hoverElement, '.visible') if (next && next.classList.contains('visible')) { next.classList.add('hover') this.listItems.scrollTo({ top: next.offsetTop - 160 }) } else { firstItem.classList.add('hover') this.listItems.scrollTo({ top: firstItem.offsetTop - 160, }) } } } } if (keyCode === 13) { event.preventDefault() const currentSelected = this.listItems.querySelector('.hover'); if (currentSelected) { const selected = this.listItems.querySelector('.selected'); if (!this.container.hasAttribute('multiple')) { if (selected) { selected.classList.remove('selected') } currentSelected.classList.add('selected') this.selected.innerText = currentSelected.innerHTML this.container.value = currentSelected.dataset.select if (this.dynamic.change) { this.dynamic.change(this.container.value) } } else { this.multipleSelected(currentSelected, this.container.querySelector(`option[value="${currentSelected.dataset.select}"]`)) } this.container.parentNode.classList.remove('form-error') this.clear() } } }) } multipleSelected(listItem, e) { if (!this.selected.querySelector(`.selected-box[data-id="${e.value}"]`)) { const sb = document.createElement('div') const close = document.createElement('div') sb.className = 'selected-box' close.className = 'close' sb.innerText = e.innerText sb.setAttribute('data-id', e.value) const sbExists = this.selected.querySelector('.selected-box') if (!sbExists) { this.selected.innerHTML = ''; } sb.appendChild(close) this.selected.appendChild(sb) listItem.classList.add('selected') e.setAttribute('selected', true) sb.addEventListener('click', () => { event.preventDefault() e.removeAttribute('selected') const listItemSelected = this.listItems.querySelector(`.list-item[data-select="${e.value}"]`) listItemSelected.classList.remove('selected') sb.remove() const sbExists = this.selected.querySelector('.selected-box') if (!sbExists) { var s = this.container.querySelector('option:first-child') if (s) this.selected.innerHTML = s.innerText; } }) } } dataRetrieve() { this.box.classList.add('search-box') this.list.classList.add('list') this.selected.classList.add('selected') this.listItems.classList.add('list-items') this.list.appendChild(this.searchField) this.list.appendChild(this.listItems) this.box.appendChild(this.selected) this.box.appendChild(this.list) if (this.container.hasClass('disabled')) { this.box.classList.add('disabled') } this.box.addEventListener('click', e => { if ( !event.target.classList.contains('selected-box') && !event.target.classList.contains('close') ) { if (this.box.classList.contains('open')) { this.clear() } else { document.querySelectorAll('.search-box').forEach(b => { b.classList.remove('open') }) this.box.classList.add('open') this.searchField.focus() } } }) this.keyDown() this.updateFromOptionList() this.searchField.addEventListener('keyup', e => { this.listItems.querySelectorAll('.list-item').forEach((e, i) => { if (e.innerText.toLowerCase().indexOf(this.searchField.value.toLowerCase()) !== -1 || this.searchField.value === '') { e.style.display = 'block' e.classList.add('visible') } else { e.style.display = 'none' e.classList.remove('visible') e.classList.remove('hover') } }) }) this.container.parentNode.appendChild(this.box); } updateFromOptionList() { this.listItems.innerHTML = ''; this.container.querySelectorAll('option').forEach((e, i) => { if (i === 0) { //if (this.selected.innerText !== '') this.selected.innerText = e.innerText } if (e.value) { const listItem = document.createElement('div'); listItem.className = 'list-item visible' listItem.setAttribute('data-select', e.value) listItem.innerText = e.innerText; this.listItems.appendChild(listItem) if (e.hasAttribute('selected') && !this.container.hasAttribute('multiple')) { this.selected.innerText = e.innerText listItem.classList.add('selected') this.listItems.scrollTo({ top: listItem.offsetTop - 160, }) } if (e.hasAttribute('selected') && this.container.hasAttribute('multiple')) { this.multipleSelected(listItem, e) } listItem.addEventListener('click', l => { if (!this.container.hasAttribute('multiple')) { this.container.value = e.value this.selected.innerText = e.innerText if (this.dynamic.change) { this.dynamic.change(this.container.value) } } else { this.multipleSelected(listItem, e) } this.container.parentNode.classList.remove('form-error') }) } }) } updateFromObject(object) { const $this = this; object.searchBox.dynamic.change = function (value) { $this.request(object.url, `${object.queryId}=${value}`, data => { //console.log(data); $this.container.innerHTML = $this.container.querySelector('option:first-child').outerHTML data.forEach(o => { if (o[object.data.id] && o[object.data.name]) { const option = document.createElement('option') option.value = o[object.data.id]; option.innerText = o[object.data.name] $this.container.appendChild(option) } }) if (data.length > 0) { $this.updateFromOptionList() $this.container.classList.remove('disabled') $this.box.classList.remove('disabled') } }) } } request(url, query, callback) { const xhr = new XMLHttpRequest() xhr.responseType = 'json' xhr.open('get', `${url}?${query}`) xhr.onload = () => { callback(xhr.response) } xhr.send() } }