@@ -6,17 +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- connectedCallback ( ) : void {
10- const listId = this . getAttribute ( 'for' )
11- if ( ! listId ) return
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+ }
1221
13- // eslint-disable-next-line custom-elements/no-dom-traversal-in-connectedcallback
14- const input = this . querySelector ( 'input' )
15- const results = document . getElementById ( listId )
16- if ( ! ( input instanceof HTMLInputElement ) || ! results ) return
17- const autoselectEnabled = this . getAttribute ( 'data-autoselect' ) === 'true'
18- state . set ( this , new Autocomplete ( this , input , results , autoselectEnabled ) )
19- results . setAttribute ( 'role' , 'listbox' )
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+
40+ connectedCallback ( ) : void {
41+ if ( ! this . isConnected ) return
42+ this . #reattachState( )
2043 }
2144
2245 disconnectedCallback ( ) : void {
@@ -27,6 +50,15 @@ export default class AutocompleteElement extends HTMLElement {
2750 }
2851 }
2952
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+
3062 get src ( ) : string {
3163 return this . getAttribute ( 'src' ) || ''
3264 }
@@ -66,7 +98,7 @@ export default class AutocompleteElement extends HTMLElement {
6698 fetchResult = fragment
6799
68100 static get observedAttributes ( ) : string [ ] {
69- return [ 'open' , 'value' ]
101+ return [ 'open' , 'value' , 'for' ]
70102 }
71103
72104 attributeChangedCallback ( name : string , oldValue : string , newValue : string ) : void {
@@ -75,6 +107,10 @@ export default class AutocompleteElement extends HTMLElement {
75107 const autocomplete = state . get ( this )
76108 if ( ! autocomplete ) return
77109
110+ if ( this . forElement !== state . get ( this ) ?. results || this . inputElement !== state . get ( this ) ?. input ) {
111+ this . #reattachState( )
112+ }
113+
78114 switch ( name ) {
79115 case 'open' :
80116 newValue === null ? autocomplete . close ( ) : autocomplete . open ( )
0 commit comments