select

Better select widgets in vanilla javascript.  https://p.ce9e.org/select/demo/
git clone https://git.ce9e.org/select.git

commit
d5e4dda73b8de33c3b0e9a3db30222662cb45aef
parent
0eec7eb6e26a21eba9c06cf25b8ad90ff8a77a94
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2021-03-28 05:58
optgroup support

Diffstat

M README.md 1 -
M select.css 16 ++++++++++++----
M select.js 60 ++++++++++++++++++++++++++++++++++++++++++++----------------

3 files changed, 56 insertions, 21 deletions


diff --git a/README.md b/README.md

@@ -17,7 +17,6 @@ The code is intentionally very simple and close to browser defaults.
   17    17 ## Roadmap
   18    18 
   19    19 -	refactor constructor args
   20    -1 -	optgroups
   21    20 -	dynamic option creation
   22    21 -	localization
   23    22 -	allowClear

diff --git a/select.css b/select.css

@@ -29,22 +29,30 @@
   29    29 	display: none;
   30    30 }
   31    31 
   32    -1 .select__dropdown li {
   -1    32 .select__dropdown [role="option"],
   -1    33 .select__dropdown strong {
   33    34 	padding: 0.2em 0.4em;
   34    35 	white-space: nowrap;
   35    36 	overflow: hidden;
   36    37 	text-overflow: ellipsis;
   37    38 	cursor: default;
   38    39 }
   39    -1 .select__dropdown li.select--selected,
   40    -1 .select__dropdown li:hover {
   -1    40 .select__dropdown [role="option"].select--selected,
   -1    41 .select__dropdown [role="option"]:hover {
   41    42 	background: rgba(128,128,128,.2);
   42    43 	color: inherit;
   43    44 }
   44    -1 .select__dropdown li.select--has-focus {
   -1    45 .select__dropdown [role="option"].select--has-focus {
   45    46 	background: Highlight;
   46    47 	color: HighlightText;
   47    48 }
   -1    49 .select__dropdown ul {
   -1    50 	margin: 0;
   -1    51 	padding: 0;
   -1    52 }
   -1    53 .select__dropdown ul [role="option"] {
   -1    54 	padding-left: 2em;
   -1    55 }
   48    56 
   49    57 .select__input > ul {
   50    58 	display: inline;

diff --git a/select.js b/select.js

@@ -83,8 +83,9 @@ export class Select {
   83    83 	}
   84    84 
   85    85 	update() {
   86    -1 		if (this.focus !== -1 && this.dropdown.children.length) {
   87    -1 			Array.from(this.dropdown.children).forEach((li, i) => {
   -1    86 		var options = this.dropdown.querySelectorAll('[role="option"]');
   -1    87 		if (this.focus !== -1 && options.length) {
   -1    88 			Array.from(options).forEach((li, i) => {
   88    89 				var op = this.original.options[this.indexMap[i]];
   89    90 				li.classList.toggle('select--has-focus', i === this.focus);
   90    91 				li.classList.toggle('select--selected', this.original.multiple && op.selected);
@@ -92,7 +93,7 @@ export class Select {
   92    93 			});
   93    94 			this.wrapper.setAttribute('aria-expanded', 'true');
   94    95 			this.input.setAttribute('aria-activedescendant', this.id + '_option_' + this.indexMap[this.focus]);
   95    -1 			this.dropdown.children[this.focus].scrollIntoView({block: 'nearest'});
   -1    96 			options[this.focus].scrollIntoView({block: 'nearest'});
   96    97 		} else {
   97    98 			this.wrapper.setAttribute('aria-expanded', 'false');
   98    99 			this.input.setAttribute('aria-activedescendant', '');
@@ -121,22 +122,48 @@ export class Select {
  121   122 		}
  122   123 	}
  123   124 
   -1   125 	createOption(op, i) {
   -1   126 		var li = document.createElement('li');
   -1   127 		li.id = this.id + '_option_' + i;
   -1   128 		li.textContent = op.label;
   -1   129 		li.setAttribute('role', 'option');
   -1   130 		li.onclick = () => {
   -1   131 			this.setValue(i, this.original.multiple);
   -1   132 			this.input.focus();
   -1   133 		};
   -1   134 		this.indexMap.push(i);
   -1   135 		return li;
   -1   136 	}
   -1   137 
  124   138 	open(complete) {
  125   139 		this.focus = 0;
  126   140 		this.dropdown.innerHTML = '';
  127   141 		this.indexMap = [];
  128    -1 		Array.from(this.original.options).forEach((op, i) => {
  129    -1 			if (op.label && (complete || this.isMatch(op.label))) {
  130    -1 				var li = document.createElement('li');
  131    -1 				li.id = this.id + '_option_' + i;
  132    -1 				li.textContent = op.label;
  133    -1 				li.setAttribute('role', 'option');
  134    -1 				li.onclick = () => {
  135    -1 					this.setValue(i, this.original.multiple);
  136    -1 					this.input.focus();
  137    -1 				};
  138    -1 				this.dropdown.append(li);
  139    -1 				this.indexMap.push(i);
   -1   142 		var i = 0;
   -1   143 		Array.from(this.original.children).forEach(child => {
   -1   144 			if (child.tagName === 'OPTION') {
   -1   145 				if (child.label && (complete || this.isMatch(child.label))) {
   -1   146 					this.dropdown.append(this.createOption(child, i));
   -1   147 				}
   -1   148 				i += 1;
   -1   149 			} else {
   -1   150 				var group = document.createElement('li');
   -1   151 				var label = document.createElement('strong');
   -1   152 				var ul = document.createElement('ul');
   -1   153 				group.setAttribute('role', 'group');
   -1   154 				label.textContent = child.label;
   -1   155 				ul.setAttribute('role', 'none');
   -1   156 				group.append(label);
   -1   157 				group.append(ul);
   -1   158 				Array.from(child.children).forEach(c => {
   -1   159 					if (c.label && (complete || this.isMatch(c.label))) {
   -1   160 						ul.append(this.createOption(c, i));
   -1   161 					}
   -1   162 					i += 1;
   -1   163 				});
   -1   164 				if (ul.children.length) {
   -1   165 					this.dropdown.append(group);
   -1   166 				}
  140   167 			}
  141   168 		});
  142   169 		this.update();
@@ -150,9 +177,10 @@ export class Select {
  150   177 	}
  151   178 
  152   179 	moveFocus(k) {
   -1   180 		var options = this.dropdown.querySelectorAll('[role="option"]');
  153   181 		this.focus += k;
  154   182 		this.focus = Math.max(this.focus, 0);
  155    -1 		this.focus = Math.min(this.focus, this.dropdown.children.length - 1);
   -1   183 		this.focus = Math.min(this.focus, options.length - 1);
  156   184 		this.update();
  157   185 	}
  158   186