Custom Modules
Element vs Global Modules
Choose whether your custom module should attach to objects or run as a global runtime participant.
Element vs Global Modules
Before writing code, decide whether your module is object-driven or runtime-driven.
Element modules
Element modules connect to StringObject instances discovered in the DOM.
Use an element module when:
- behavior is declared in markup
- you need per-element attributes
- you write output to one element and its mirrors
- you want
onObjectConnected(...),objects, andobject.events
Typical shape:
export class StringMyModule extends StringModule {
constructor(context: StringContext) {
super(context);
this.htmlKey = 'my-module';
}
}
<div string="my-module" string-my-value="24"></div>
Global modules
Global modules participate in the runtime without requiring string="..." on elements.
Use a global module when:
- behavior is app-wide
- no per-object connection step is needed
- you mainly react to scroll, pointer, viewport, or events
- the module manages its own DOM querying or global classes
Typical shape:
export class StringMyGlobalModule extends StringModule {
override onSubscribe(): void {
this.events.on('scroll:start', this.onScrollStartEvent);
}
override onUnsubscribe(): void {
this.events.off('scroll:start', this.onScrollStartEvent);
}
}
stringTune.use(StringMyGlobalModule);
How connection actually works
For element modules, the runtime flow is:
ObjectManagercreates aStringObjectfrom a DOM element.- The module is considered connectable when
canConnect(object)returnstrue. - By default,
canConnect(...)checks whetherobject.keyscontainshtmlKey. - The runtime calls
initializeObject(...). - The runtime calls
calculatePositions(...). - The runtime calls
connectObject(...), which triggersonObjectConnected(...)on first connection. - The module keeps that object in
objectsOnPageandobjectscollections.
Global modules skip the object connection path unless they override canConnect(...) or manually manage objects.
Core module vs UI module
StringModule also has an internal type bucket:
type = 1means core moduletype = 2means UI module
Built-ins like StringMagnetic switch to type = 2.
For most custom modules, keep the default unless you have a concrete reason to separate the module into the UI queue. The runtime still dispatches the same lifecycle names to both buckets.
Practical rule
Start with an element module unless you are sure the feature is global.
That keeps the contract clearer:
string="..."- mapped attributes
- object-local output
Once that shape stops fitting the feature, move to a global module intentionally rather than by accident.