21
21
//
22
22
// (Note that calls to `connected` and `disconnected` are delayed by `MutationObserver` which is what we use to track changes to the document)
23
23
//
24
- // In this version of the library, behaviours are enabled on a per-document basis.
24
+ // In this version of the library, behaviours are enabled on a per-target basis.
25
25
// Call `enableBehaviours(document)` to enable behaviour handling for that document.
26
26
// It is not necessary to call `enableBehaviours(document)` for the main document since it will be enabled automatically.
27
- // You can still call it if you want to control when the document behaviours are updated, or you can call `disableBehaviours(document)` if you
27
+ // You can still call it if you want to control when the behaviours are updated, or you can call `disableBehaviours(document)` if you
28
28
// prefer behaviours not to be enabled for the main document.
29
29
//
30
+ // You can also pass an element to `enableBehaviours` to limit the scope to that element.
31
+ //
30
32
// You can get a behaviour from an element if you know the type (or base type) of the behavior:
31
33
//
32
34
// const behaviour = getBehaviour(element, MyBehaviour);
@@ -194,11 +196,14 @@ function* chain(current) {
194
196
}
195
197
196
198
/**
197
- * Tests whether the base object is in the prototype chain of the derived object
199
+ * Tests whether the ` base` object is in the prototype chain of the ` derived` object
198
200
* @param { object } derived
199
201
* @param { object } base
200
202
*/
201
- function isBase ( derived , base ) {
203
+ function isBase ( { derived, base } ) {
204
+ if ( derived === undefined || base === undefined ) {
205
+ throw "Invalid argument to isBase" ;
206
+ }
202
207
for ( const link of chain ( derived ) ) {
203
208
if ( link === base ) {
204
209
return true ;
@@ -281,7 +286,7 @@ export function getBehaviour(element, behaviourType) {
281
286
// If we are given MyBaseBehaviour and we have stored MyDerivedBehaviour,
282
287
// we still need to find the behaviour and return it
283
288
for ( const [ behaviourType_ , behavior_ ] of instances . entries ( ) ) {
284
- if ( isBase ( behaviourType_ , behaviourType ) ) {
289
+ if ( isBase ( { derived : behaviourType_ , base : behaviourType } ) ) {
285
290
return behavior_ ;
286
291
}
287
292
}
@@ -291,7 +296,12 @@ export function getBehaviour(element, behaviourType) {
291
296
292
297
/** @typedef { { key: BehaviourKey, elementType: typeof HTMLElement, behaviourType: typeof Behaviour, existingBehaviourType: typeof Behaviour } } Warning */
293
298
294
- class BehaviourRegistry {
299
+ /**
300
+ * A `Target` is a document or element in which behaviours have been enabled
301
+ * @typedef { Node } Target
302
+ * */
303
+
304
+ export class BehaviourRegistry {
295
305
296
306
/**
297
307
* This contains the behaviours directly applied to a particular element type.
@@ -302,62 +312,70 @@ class BehaviourRegistry {
302
312
elementTypeToBehaviourRecord = new Map ( ) ;
303
313
304
314
/**
305
- * Stores documents that we're watching for changes
306
- * @type Map<Document , { registry: BehaviourRegistry, controller: AbortController } >
315
+ * Stores targets that we're watching for changes
316
+ * @type Map<Target , { registry: BehaviourRegistry, controller: AbortController } >
307
317
* */
308
- documentToRegistry = new Map ( ) ;
318
+ targetToRegistry = new Map ( ) ;
309
319
310
320
/**
311
- * Documents that need to be re-examined for behaviours
312
- * @type Document []
321
+ * Targets that need to be re-examined for behaviours
322
+ * @type Target []
313
323
*/
314
- documentQueue = [ ] ;
324
+ queue = [ ] ;
315
325
316
326
/**
317
327
* We only ensure the main document is enabled once on the first call to `register`
318
328
* so that callers can successfully disable this document without us re-enabling it
319
329
*/
320
- isMainDocumentHandled = false ;
330
+ shouldHandleMainDocument = false ;
331
+
332
+ constructor ( shouldHandleMainDocument = false ) {
333
+ this . shouldHandleMainDocument = shouldHandleMainDocument ;
334
+ }
321
335
322
336
/**
323
- * Add a document to the queue - the entire document will be examined for elements/attributes that need to be connected to behaviours.
324
- * The processing of the document happens in a microtask.
325
- * @param { Document } document
337
+ * Add a target to the queue - the target node will be examined for elements/attributes that need to be connected to behaviours.
338
+ * The processing of the target happens in a microtask.
339
+ * @param { Target } target
326
340
* */
327
- addToQueue ( document ) {
328
- if ( ! this . documentQueue . includes ( document ) ) {
329
- this . documentQueue . push ( document ) ;
341
+ addToQueue ( target ) {
342
+ if ( ! this . queue . includes ( target ) ) {
343
+ this . queue . push ( target ) ;
330
344
this . needsUpdate = true ;
331
345
}
332
346
}
333
347
334
- updateDocument ( document ) {
335
- const existing = this . documentToRegistry . get ( document ) ;
348
+ /**
349
+ *
350
+ * @param { Target } target
351
+ */
352
+ updateTarget ( target ) {
353
+ const existing = this . targetToRegistry . get ( target ) ;
336
354
337
355
if ( existing === undefined ) {
338
- const controller = listenToElement ( document . body , ( mutations , observer ) => {
356
+ const controller = listenToElement ( target , ( mutations , observer ) => {
339
357
for ( const mutation of mutations ) {
340
358
this . handleMutation ( mutation ) ;
341
359
}
342
360
} ) ;
343
361
344
- this . documentToRegistry . set ( document , { registry : this , controller } ) ;
362
+ this . targetToRegistry . set ( target , { registry : this , controller } ) ;
345
363
}
346
364
347
- this . connect ( document . body . querySelectorAll ( "*" ) ) ;
365
+ this . connect ( target . querySelectorAll ( "*" ) ) ;
348
366
}
349
367
350
368
/**
351
- * Processes all the documents in the queue.
369
+ * Processes all the targets in the queue.
352
370
* Adds mutation observation if necessary and
353
- * examines all existing document content to attach behaviours if necessary.
371
+ * examines all existing target content to attach behaviours if necessary.
354
372
*/
355
373
update ( ) {
356
- const documents = this . documentQueue ;
357
- this . documentQueue = [ ] ;
374
+ const targets = this . queue ;
375
+ this . queue = [ ] ;
358
376
359
- for ( const document of documents ) {
360
- this . updateDocument ( document ) ;
377
+ for ( const target of targets ) {
378
+ this . updateTarget ( target ) ;
361
379
}
362
380
}
363
381
@@ -421,15 +439,15 @@ class BehaviourRegistry {
421
439
422
440
// For the first registration, we add the main document to the queue so that users cannot forget to call enable on it
423
441
// We only do this once so that users can choose to disable the main document
424
- if ( ! this . isMainDocumentHandled ) {
425
- this . isMainDocumentHandled = true ;
426
- this . addToQueue ( document ) ;
442
+ if ( this . shouldHandleMainDocument ) {
443
+ this . shouldHandleMainDocument = false ;
444
+ this . addToQueue ( globalThis . document ) ;
427
445
}
428
446
429
- // If behaviours get registered after a document is enabled,
430
- // we need to update existing documents
431
- for ( const document of this . documentToRegistry . keys ( ) ) {
432
- this . addToQueue ( document ) ;
447
+ // If behaviours get registered after a target is enabled,
448
+ // we need to update existing targets
449
+ for ( const target of this . targetToRegistry . keys ( ) ) {
450
+ this . addToQueue ( target ) ;
433
451
}
434
452
435
453
return { warnings } ;
@@ -487,61 +505,63 @@ class BehaviourRegistry {
487
505
}
488
506
489
507
/**
490
- * Starts watching the document and connecting behaviours to relevant elements
491
- * @param { Document } document
508
+ * Starts watching the target and connecting behaviours to relevant elements
509
+ * @param { Target } target
492
510
*/
493
- enable ( document ) {
494
- // Here we add the specified document to the queue then immediately process the queue
511
+ enable ( target ) {
512
+ // Here we add the specified target to the queue then immediately process the queue
495
513
// to ensure that all routes are going through the same code paths.
496
- this . addToQueue ( document ) ;
514
+ this . addToQueue ( target ) ;
497
515
this . update ( ) ;
498
516
}
499
517
500
518
/**
501
- * Stops watching the document . Does not disconnect already connected behaviours.
502
- * @param { Document } document
519
+ * Stops watching the target . Does not disconnect already connected behaviours.
520
+ * @param { Target } target
503
521
*/
504
- disable ( document ) {
505
- if ( document === globalThis . document ) {
522
+ disable ( target ) {
523
+ if ( target === globalThis . document ) {
506
524
// Disable main document handling by saying it's already done
507
525
// This code means that users can disable the main document before any registrations.
508
526
// Without this code, users would have to ensure that disable is called after the first registration.
509
- this . isMainDocumentHandled = true ;
527
+ this . shouldHandleMainDocument = false ;
510
528
}
511
529
512
- // Remove this document from the queue if it's there
513
- const index = this . documentQueue . indexOf ( document ) ;
530
+ // Remove this target from the queue if it's there
531
+ const index = this . queue . indexOf ( target ) ;
514
532
if ( index >= 0 ) {
515
- this . documentQueue . splice ( index , 1 ) ;
533
+ this . queue . splice ( index , 1 ) ;
516
534
}
517
535
518
- // Stop listening to this document if we've started listening
519
- const existing = this . documentToRegistry . get ( document ) ;
536
+ // Stop listening to this target if we've started listening
537
+ const existing = this . targetToRegistry . get ( target ) ;
520
538
if ( existing !== undefined ) {
521
539
existing . controller . abort ( ) ;
522
- this . documentToRegistry . delete ( document ) ;
540
+ this . targetToRegistry . delete ( target ) ;
523
541
}
524
542
}
525
543
}
526
544
527
545
addMicrotaskProperty ( BehaviourRegistry , "update" ) ;
528
546
529
- const customBehaviours = new BehaviourRegistry ( ) ;
547
+ // The global behaviour registry has main document handling turned on
548
+ // Other behaviour registries default to main document handling being off
549
+ export const customBehaviours = new BehaviourRegistry ( true ) ;
530
550
531
551
/**
532
- * Starts watching the document and connecting behaviours to relevant elements
533
- * @param { Document } document
552
+ * Starts watching the target and connecting behaviours to relevant elements
553
+ * @param { Target } target
534
554
*/
535
- export function enableBehaviours ( document ) {
536
- customBehaviours . enable ( document ) ;
555
+ export function enableBehaviours ( target ) {
556
+ customBehaviours . enable ( target ) ;
537
557
}
538
558
539
559
/**
540
- * Stops watching the document . Does not disconnect already connected behaviours.
541
- * @param { Document } document
560
+ * Stops watching the target . Does not disconnect already connected behaviours.
561
+ * @param { Target } target
542
562
*/
543
- export function disableBehaviours ( document ) {
544
- customBehaviours . disable ( document ) ;
563
+ export function disableBehaviours ( target ) {
564
+ customBehaviours . disable ( target ) ;
545
565
}
546
566
547
567
/**
0 commit comments