diff --git a/src/index.ts b/src/index.ts index 35efe6e..e1a55e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,6 +31,7 @@ export default class Combobox { tabInsertsSuggestions: boolean firstOptionSelectionMode: FirstOptionSelectionMode scrollIntoViewOptions?: boolean | ScrollIntoViewOptions + didAutoAssignLastSelectedId: boolean constructor( input: HTMLTextAreaElement | HTMLInputElement, @@ -44,6 +45,7 @@ export default class Combobox { this.scrollIntoViewOptions = scrollIntoViewOptions ?? {block: 'nearest', inline: 'nearest'} this.isComposing = false + this.didAutoAssignLastSelectedId = false if (!list.id) { list.id = `combobox-${Math.random().toString().slice(2, 6)}` @@ -126,11 +128,19 @@ export default class Combobox { el.removeAttribute('data-combobox-option-default') if (target === el) { + if (!target.id) { + target.id = `${this.list.id}-selected` + this.didAutoAssignLastSelectedId = true + } this.input.setAttribute('aria-activedescendant', target.id) target.setAttribute('aria-selected', 'true') fireSelectEvent(target) target.scrollIntoView(this.scrollIntoViewOptions) } else { + if (el.id === `${this.list.id}-selected` && this.didAutoAssignLastSelectedId) { + el.removeAttribute('id') + this.didAutoAssignLastSelectedId = false + } el.removeAttribute('aria-selected') } } @@ -141,6 +151,10 @@ export default class Combobox { for (const el of this.list.querySelectorAll('[aria-selected="true"], [data-combobox-option-default="true"]')) { el.removeAttribute('aria-selected') el.removeAttribute('data-combobox-option-default') + if (el.id === `${this.list.id}-selected` && this.didAutoAssignLastSelectedId) { + el.removeAttribute('id') + this.didAutoAssignLastSelectedId = false + } } } diff --git a/test/test.js b/test/test.js index a39d783..3102972 100644 --- a/test/test.js +++ b/test/test.js @@ -410,4 +410,55 @@ describe('combobox-nav', function () { }) }) }) + + describe('with missing IDs on options', function () { + let input + let list + beforeEach(function () { + document.body.innerHTML = ` + +