Skip to content

Instantly share code, notes, and snippets.

@itzikbenh
Created October 11, 2017 17:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itzikbenh/4c46aaaa140f76644a3c7c247dd7ad9c to your computer and use it in GitHub Desktop.
Save itzikbenh/4c46aaaa140f76644a3c7c247dd7ad9c to your computer and use it in GitHub Desktop.
Picky class
class Picky {
constructor(element) {
let self = this;
this.$el = element;
this.select = this.$el.querySelector('select');
this.dropdown = this.$el.querySelector('ul');
this.input = this.$el.querySelector('input');
this._open = false;
this.$el.addEventListener('keydown', (event) => {
self.keydown(event);
});
this.input.addEventListener('click', () => {
this.open = true;
});
this.input.addEventListener('input', () => {
self.filter();
});
this.$el.querySelector('.open-select-dropdown').addEventListener('click', () => {
this.open = !this.open;
});
this.options = [];
[].forEach.call(this.select, (elem, index) => {
if (elem.value) {
self.options.push({ value: elem.value, text: elem.textContent });
let li = document.createElement('li');
li.textContent = elem.textContent;
li.setAttribute('data-value', elem.value);
self.dropdown.appendChild(li);
li.addEventListener('mouseover', () => {
self.highlight(li);
});
li.addEventListener('click', () => {
self.setSelected(li);
});
}
});
}
get open() {
return this._open;
}
set open(value) {
this._open = value;
if (value) {
this.openDropdown();
this.input.focus();
} else {
this.closeDropdown();
}
}
static addClass(el, className) {
if (! NodeList.prototype.isPrototypeOf(el)) {
el = [el];
}
[].forEach.call(el, (elem) => {
if (elem.classList) {
elem.classList.add(className)
} else {
elem.className += ' ' + className;
}
});
}
static removeClass(el, className) {
if (! NodeList.prototype.isPrototypeOf(el)) {
el = [el];
}
[].forEach.call(el, (elem) => {
if (elem.classList) {
elem.classList.remove(className)
} else {
elem.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
}
});
}
static hasClass(el, className) {
if (el.classList) {
return el.classList.contains(className);
} else {
return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className);
}
}
keydown(event) {
let highlighted = this.$el.querySelector('li.highlighted');
if (highlighted) {
if (event.keyCode === 13) {
this.open = false;
this.setSelected(highlighted);
}
if (event.keyCode === 40) {
let next = highlighted.nextElementSibling;
if (next) {
this.removeHighlightedClass();
Picky.addClass(next, 'highlighted');
} else {
return;
}
let dropdownRect = this.dropdown.getBoundingClientRect();
let nextRect = next.getBoundingClientRect();
let currentOffset = dropdownRect.top + document.body.scrollTop + this.dropdown.offsetHeight;
let nextBottom = nextRect.top + document.body.scrollTop + next.offsetHeight;
let nextOffset = this.dropdown.scrollTop + nextBottom - currentOffset;
if (nextBottom > currentOffset) {
this.dropdown.scrollTop = nextOffset;
}
}
if (event.keyCode === 38) {
let prev = highlighted.previousElementSibling;
if (Picky.hasClass(prev, 'no-results')) {
return;
}
if (prev) {
this.removeHighlightedClass();
Picky.addClass(prev, 'highlighted');
} else {
return;
}
let dropdownRect = this.dropdown.getBoundingClientRect();
let prevRect = prev.getBoundingClientRect();
let currentOffset = dropdownRect.top + document.body.scrollTop;
let prevTop = prevRect.top + document.body.scrollTop;
let prevOffset = this.dropdown.scrollTop + (prevTop - currentOffset);
if (prevTop - currentOffset < 0) {
this.dropdown.scrollTop = prevOffset;
}
}
}
}
filter() {
this.removeHighlightedClass();
this.removeSelected();
this.highlightFirst();
[].forEach.call(this.$el.querySelectorAll("li:not(.no-results)"), (elem) => {
if (elem.textContent.toLowerCase().indexOf(this.input.value.toLowerCase()) > -1) {
Picky.removeClass(elem, 'hidden')
} else {
Picky.addClass(elem, 'hidden')
}
});
if (this.$el.querySelectorAll("li:not(.no-results):not(.hidden)").length < 1) {
Picky.removeClass(this.$el.querySelector("li.no-results"), 'hidden')
} else {
Picky.addClass(this.$el.querySelector("li.no-results"), 'hidden')
}
}
highlightFirst() {
let first = this.$el.querySelector('.select-dropdown-content > li:not(.no-results):not(.hidden)');
if (first) {
this.removeHighlightedClass();
Picky.addClass(first, 'highlighted');
}
}
setSelected(el) {
this.removeSelected();
Picky.addClass(el, 'selected');
this.select.querySelector(`option[value='${el.getAttribute("data-value")}']`).setAttribute('selected', 'selected');
this.input.value = el.textContent;
this.open = false;
}
removeSelected() {
[].forEach.call(this.select.querySelectorAll('option[selected="selected"]'), (elem) => {
elem.removeAttribute('selected');
});
this.removeSelectedClass();
}
openDropdown() {
Picky.addClass(this.$el, 'open');
}
closeDropdown() {
Picky.removeClass(this.$el, 'open');
}
highlight(el) {
this.removeHighlightedClass();
Picky.addClass(el, 'highlighted');
}
removeHighlightedClass() {
Picky.removeClass(this.$el.querySelectorAll("li.highlighted"), 'highlighted');
}
removeSelectedClass() {
Picky.removeClass(this.$el.querySelectorAll("li.selected"), 'selected');
}
toggleDropdown() {
this.open = !this.open;
}
}
let elements = document.querySelectorAll('.single-picky');
elements.forEach((elem) => {
new Picky(elem);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment