@@ -6,18 +6,40 @@ const state = new WeakMap()
66
77// eslint-disable-next-line custom-elements/file-name-matches-element
88export default class AutocompleteElement extends HTMLElement {
9+ #forElement: HTMLElement | null = null
10+ get forElement ( ) : HTMLElement | null {
11+ if ( this . #forElement?. isConnected ) {
12+ return this . #forElement
13+ }
14+ const id = this . getAttribute ( 'for' )
15+ const root = this . getRootNode ( )
16+ if ( id && ( root instanceof Document || root instanceof ShadowRoot ) ) {
17+ return root . getElementById ( id )
18+ }
19+ return null
20+ }
21+
22+ set forElement ( element : HTMLElement | null ) {
23+ this . #forElement = element
24+ this . setAttribute ( 'for' , '' )
25+ }
26+
27+ #inputElement: HTMLInputElement | null = null
28+ get inputElement ( ) : HTMLInputElement | null {
29+ if ( this . #inputElement?. isConnected ) {
30+ return this . #inputElement
31+ }
32+ return this . querySelector < HTMLInputElement > ( 'input' )
33+ }
34+
35+ set inputElement ( input : HTMLInputElement | null ) {
36+ this . #inputElement = input
37+ this . #reattachState( )
38+ }
39+
940 connectedCallback ( ) : void {
10- const listId = this . getAttribute ( 'for' )
1141 if ( ! this . isConnected ) return
12- if ( ! listId ) return
13-
14- // eslint-disable-next-line custom-elements/no-dom-traversal-in-connectedcallback
15- const input = this . querySelector ( 'input' )
16- const results = ( this . getRootNode ( ) as Document ) . getElementById ( listId )
17- if ( ! ( input instanceof HTMLInputElement ) || ! results ) return
18- const autoselectEnabled = this . getAttribute ( 'data-autoselect' ) === 'true'
19- state . set ( this , new Autocomplete ( this , input , results , autoselectEnabled ) )
20- results . setAttribute ( 'role' , 'listbox' )
42+ this . #reattachState( )
2143 }
2244
2345 disconnectedCallback ( ) : void {
@@ -28,6 +50,15 @@ export default class AutocompleteElement extends HTMLElement {
2850 }
2951 }
3052
53+ #reattachState( ) {
54+ state . get ( this ) ?. destroy ( )
55+ const { forElement, inputElement} = this
56+ if ( ! forElement || ! inputElement ) return
57+ const autoselectEnabled = this . getAttribute ( 'data-autoselect' ) === 'true'
58+ state . set ( this , new Autocomplete ( this , inputElement , forElement , autoselectEnabled ) )
59+ forElement . setAttribute ( 'role' , 'listbox' )
60+ }
61+
3162 get src ( ) : string {
3263 return this . getAttribute ( 'src' ) || ''
3364 }
@@ -67,7 +98,7 @@ export default class AutocompleteElement extends HTMLElement {
6798 fetchResult = fragment
6899
69100 static get observedAttributes ( ) : string [ ] {
70- return [ 'open' , 'value' ]
101+ return [ 'open' , 'value' , 'for' ]
71102 }
72103
73104 attributeChangedCallback ( name : string , oldValue : string , newValue : string ) : void {
@@ -76,6 +107,10 @@ export default class AutocompleteElement extends HTMLElement {
76107 const autocomplete = state . get ( this )
77108 if ( ! autocomplete ) return
78109
110+ if ( this . forElement !== state . get ( this ) ?. results || this . inputElement !== state . get ( this ) ?. input ) {
111+ this . #reattachState( )
112+ }
113+
79114 switch ( name ) {
80115 case 'open' :
81116 newValue === null ? autocomplete . close ( ) : autocomplete . open ( )
0 commit comments