@@ -28,6 +28,9 @@ import { getSwitch, hasSwitch, rswitch } from './switch'
2828 * @internal
2929 */
3030export class Binder {
31+ __bindDepth = 0
32+ __pendingTeleports = new Map < HTMLElement , string > ( )
33+
3134 __parser : Parser
3235 __ifBinder : IfBinder
3336 __forBinder : ForBinder
@@ -70,17 +73,63 @@ export class Binder {
7073 }
7174
7275 __bindDefault ( element : Element ) : void {
73- if (
74- element . nodeType !== Node . ELEMENT_NODE ||
75- element . hasAttribute ( this . __pre )
76- )
77- return
78- if ( this . __ifBinder . __bindAll ( element ) ) return
79- if ( this . __forBinder . __bindAll ( element ) ) return
80- if ( this . __dynamicBinder . __bindAll ( element ) ) return
81- this . __componentBinder . __bindAll ( element )
82- this . __unwrapTemplates ( element )
83- this . __bindAttributes ( element , true )
76+ ++ this . __bindDepth
77+ try {
78+ if (
79+ element . nodeType !== Node . ELEMENT_NODE ||
80+ element . hasAttribute ( this . __pre )
81+ )
82+ return
83+ if ( this . __ifBinder . __bindAll ( element ) ) return
84+ if ( this . __forBinder . __bindAll ( element ) ) return
85+ if ( this . __dynamicBinder . __bindAll ( element ) ) return
86+ this . __componentBinder . __bindAll ( element )
87+ this . __unwrapTemplates ( element )
88+ this . __bindAttributes ( element , true )
89+ } finally {
90+ -- this . __bindDepth
91+ if ( this . __bindDepth === 0 ) this . __flushPendingTeleports ( )
92+ }
93+ }
94+
95+ __resolveTeleportTarget ( el : HTMLElement , selector : string ) : Element | null {
96+ let queryRoot = document as ParentNode
97+ if ( ! queryRoot ) {
98+ let root = el . parentNode
99+ while ( root ?. parentNode ) root = root . parentNode
100+ if ( ! root ) return null
101+ queryRoot = root
102+ }
103+ return queryRoot . querySelector ( selector )
104+ }
105+
106+ __performTeleport ( el : HTMLElement , selector : string ) : boolean {
107+ const teleportTo = this . __resolveTeleportTarget ( el , selector )
108+ if ( ! teleportTo ) return false
109+ const parent = el . parentElement
110+ if ( ! parent ) return false
111+ const placeholder = new Comment ( `teleported => '${ selector } '` )
112+ parent . insertBefore ( placeholder , el )
113+ ; ( el as HTMLElement & { teleportedFrom : Node } ) . teleportedFrom = placeholder
114+ ; ( placeholder as Comment & { teleportedTo : HTMLElement } ) . teleportedTo = el
115+ addUnbinder ( placeholder , ( ) => {
116+ removeNode ( el )
117+ } )
118+ teleportTo . appendChild ( el )
119+ return true
120+ }
121+
122+ __queueTeleport ( el : HTMLElement , selector : string ) : void {
123+ this . __pendingTeleports . set ( el , selector )
124+ }
125+
126+ __flushPendingTeleports ( ) : void {
127+ const pending = this . __pendingTeleports
128+ if ( pending . size === 0 ) return
129+ this . __pendingTeleports = new Map < HTMLElement , string > ( )
130+ for ( const [ el , selector ] of pending . entries ( ) ) {
131+ this . __performTeleport ( el , selector )
132+ }
84133 }
85134
86135 __bindAttributes ( element : Element , isRecursive : boolean ) : void {
@@ -141,25 +190,9 @@ export class Binder {
141190 ) : boolean {
142191 if ( config !== teleportDirective ) return false
143192 if ( isNullOrWhitespace ( valueExpression ) ) return true
144- let queryRoot = document as ParentNode
145- if ( ! queryRoot ) {
146- let root = el . parentNode
147- while ( root ?. parentNode ) root = root . parentNode
148- if ( root ) queryRoot = root
149- else return true
193+ if ( ! this . __performTeleport ( el , valueExpression ) ) {
194+ this . __queueTeleport ( el , valueExpression )
150195 }
151- const teleportTo = queryRoot . querySelector ( valueExpression )
152- if ( ! teleportTo ) return true
153- const parent = el . parentElement
154- if ( ! parent ) return true
155- const placeholder = new Comment ( `teleported => '${ valueExpression } '` )
156- parent . insertBefore ( placeholder , el )
157- ; ( el as HTMLElement & { teleportedFrom : Node } ) . teleportedFrom = placeholder
158- ; ( placeholder as Comment & { teleportedTo : HTMLElement } ) . teleportedTo = el
159- addUnbinder ( placeholder , ( ) => {
160- removeNode ( el )
161- } )
162- teleportTo . appendChild ( el )
163196 return true
164197 }
165198
0 commit comments