Component Communication
WakaPAC components communicate through desktop-style messaging for all inter-component communication. All messages — whether between siblings, parent to child, or child to parent — flow through msgProc, providing a single, consistent entry point for every component.
Component Hierarchies
Parent-child relationships are determined entirely by DOM nesting, not by the order in which components are created. When WakaPAC initialises a component, it walks the DOM tree to check whether that element is nested inside another component's element. If it is, the outer component becomes the parent.
<div id="app"> <!-- Root component -->
<div id="sidebar"> <!-- Child of app -->
<div id="menu"> <!-- Child of sidebar (grandchild of app) -->
</div>
</div>
<div id="content"> <!-- Child of app -->
</div>
</div>
<script>
// Creation order is irrelevant — hierarchy is resolved from DOM structure
wakaPAC('#menu', { /* ... */ });
wakaPAC('#app', { /* ... */ });
wakaPAC('#sidebar', { /* ... */ });
wakaPAC('#content', { /* ... */ });
</script>
This produces two hierarchy chains: app → sidebar → menu and app → content. Each component has at most one parent, but can have any number of children.
Desktop-Style Messaging
The following sections cover how components are addressed, how messages are defined, sent, and received — including targeted, broadcast, and hierarchy-aware delivery.
Component Identification
Every component has a pacId, resolved in this order: the data-pac-id attribute, the element's id attribute, or an auto-generated identifier. This is the address you use when sending a message:
wakaPAC('#sidebar', {
ready() {
console.log(this.pacId); // "sidebar"
}
});
Defining Custom Message Types
All custom message IDs must start at wakaPAC.MSG_USER (0x1000) to avoid collisions with built-in messages. Define them as named constants:
const MSG_ITEM_SELECTED = wakaPAC.MSG_USER + 1;
const MSG_REFRESH_DATA = wakaPAC.MSG_USER + 2;
const MSG_THEME_CHANGED = wakaPAC.MSG_USER + 3;
Sending Messages
Any component can send a message to any other component. The sender calls a function on wakaPAC,
specifies the target, and the message arrives in that component's msgProc. Messages can also be
broadcast to all components at once, or to all descendants of a given component.
Every send function shares the same signature: a message ID constant, integer wParam,
integer lParam, and optional extended payload object. Two axes determine which function to use:
Scope — targeted (by pacId), hierarchy-aware (resolved from the component tree),
broadcast (all components or a subtree), or broadcastMessageGlobal (all tabs/windows at the same origin).
Timing — synchronous variants (sendMessage, sendMessageToParent) invoke msgProc
immediately; asynchronous variants (postMessage, postMessageToParent) queue through
the DOM event system and return right away. Prefer async unless the caller depends on the target having already
processed the message.
Receiving Messages
msgProc receives all messages delivered to a component — including built-in events like mouse moves and key presses. Components that only handle custom messages should guard at the top to avoid unnecessary processing:
wakaPAC('#sidebar', {
darkMode: false,
msgProc(event) {
// Skip all built-in toolkit messages
if (event.message < wakaPAC.MSG_USER) {
return;
}
switch (event.message) {
case MSG_REFRESH_DATA:
this.loadData();
break;
case MSG_THEME_CHANGED:
this.darkMode = event.wParam === 1;
break;
}
}
});
Return false to cancel default toolkit handling for cancellable events (clicks, key events, form submission). For custom messages this has no effect.
Messages
Every message delivered to a component arrives as an event object in msgProc. The properties below are always present regardless of how the message was sent.
Message Event Object
| Property | Type | Description |
|---|---|---|
message | number | The message type constant, e.g. wakaPAC.MSG_USER + 1. Use this in switch statements to dispatch to the correct handler. |
wParam | number | First integer parameter — flags, IDs, or small values. Meaning is defined by the message type. |
lParam | number | Second integer parameter — flags, IDs, or small values. Meaning is defined by the message type. |
detail | object | The extended object passed to the send function. Empty object {} if none was provided. |
timestamp | number | Time the message was created, in milliseconds since the page loaded (performance.now() epoch). |
pacId | string | The pacId of the component receiving the message. |
target | Element|null | The DOM element that originated the event. Present for DOM-originated messages (clicks, key presses, etc.); null for programmatically sent messages. |
originalEvent | Event|null | The native browser event that triggered this message. Present for DOM-originated messages; null for programmatically sent messages. |
API
wakaPAC.postMessage(pacId, messageId, wParam, lParam, extended?)
Queues a message for asynchronous delivery to the target component. The target's msgProc runs later in the event loop; calling code continues immediately. If the target does not exist or is no longer connected to the DOM, the message is dropped. Preferred over sendMessage in most cases.
| Parameter | Type | Description |
|---|---|---|
pacId | string | Target component's pacId. |
messageId | number | Message type constant. Custom messages must be ≥ wakaPAC.MSG_USER. |
wParam | number | First integer parameter — flags, IDs, or small values. |
lParam | number | Second integer parameter — flags, IDs, or small values. |
extended | object | Optional. Arbitrary data, available as event.detail in the receiver. |
Returns void | ||
wakaPAC.sendMessage(pacId, messageId, wParam, lParam, extended?)
Delivers a message synchronously to the target component. The target's msgProc runs immediately, before the next line of calling code executes. If the target does not exist, the message is dropped. Use only when you genuinely need the target to have processed the message before continuing.
| Parameter | Type | Description |
|---|---|---|
pacId | string | Target component's pacId. |
messageId | number | Message type constant. Custom messages must be ≥ wakaPAC.MSG_USER. |
wParam | number | First integer parameter — flags, IDs, or small values. |
lParam | number | Second integer parameter — flags, IDs, or small values. |
extended | object | Optional. Arbitrary data, available as event.detail in the receiver. |
Returns void | ||
wakaPAC.broadcastMessage(messageId, wParam, lParam, extended?)
Delivers a message synchronously to every registered component in the application. Use for genuinely global state changes. If only a few components care, target them directly — broadcast hits everything.
| Parameter | Type | Description |
|---|---|---|
messageId | number | Message type constant. Custom messages must be ≥ wakaPAC.MSG_USER. |
wParam | number | First integer parameter — flags, IDs, or small values. |
lParam | number | Second integer parameter — flags, IDs, or small values. |
extended | object | Optional. Arbitrary data, available as event.detail in the receiver. |
Returns void | ||
wakaPAC.broadcastMessageGlobal(messageId, wParam, lParam, extended?)
Delivers a message to every registered component in the current page and every other tab or window of the same origin. The local dispatch happens first, synchronously. Cross-tab dispatch is handled by the browser's BroadcastChannel API; WakaPAC automatically fans the message out to all components in every receiving tab.
Cross-tab broadcast is opt-in. It is only active when a <meta name="wakapac-channel"> tag is present in the <head>. If the tag is absent, broadcastMessageGlobal falls back silently to local-only dispatch. The content attribute sets the channel name — all tabs must use the same name to form one broadcast group. Because BroadcastChannel uses structured clone for serialisation, the extended payload must contain only plain, serialisable values — strings, numbers, booleans, plain objects, and arrays. DOM nodes, component references, functions, and class instances will not survive the clone and must not be included.
const MSG_CART_UPDATED = wakaPAC.MSG_USER + 1;
// In any component — updates cart components in every open tab
wakaPAC.broadcastMessageGlobal(MSG_CART_UPDATED, 0, 0, {
itemId: 42,
quantity: 3
});
| Parameter | Type | Description |
|---|---|---|
messageId | number | Message type constant. Custom messages must be ≥ wakaPAC.MSG_USER. |
wParam | number | First integer parameter — flags, IDs, or small values. |
lParam | number | Second integer parameter — flags, IDs, or small values. |
extended | object | Optional. Arbitrary data, available as event.detail in the receiver. Must be structured-clone safe — no DOM nodes, functions, or class instances. |
Returns void | ||
wakaPAC.getParentPacId(pacId)
Returns the pacId of the parent component, or null if the component has no parent. Use this when you need the parent's address for purposes other than sending a message directly. sendMessageToParent and postMessageToParent call this internally.
| Parameter | Type | Description |
|---|---|---|
pacId | string | The child component whose parent to resolve. |
Returns string|null | The parent's pacId, or null if the component has no parent. | |
wakaPAC.sendMessageToParent(pacId, messageId, wParam, lParam, extended?)
Delivers a message synchronously to the parent of the given component. If the component has no parent, the message is silently dropped. Equivalent to calling getParentPacId() and then sendMessage().
| Parameter | Type | Description |
|---|---|---|
pacId | string | The child component sending the message. |
messageId | number | Message type constant. Custom messages must be ≥ wakaPAC.MSG_USER. |
wParam | number | First integer parameter — flags, IDs, or small values. |
lParam | number | Second integer parameter — flags, IDs, or small values. |
extended | object | Optional. Arbitrary data, available as event.detail in the receiver. |
Returns void | ||
wakaPAC.postMessageToParent(pacId, messageId, wParam, lParam, extended?)
Queues a message for asynchronous delivery to the parent of the given component. If the component has no parent, the message is silently dropped. Asynchronous equivalent of sendMessageToParent — same parameters, queued delivery.
| Parameter | Type | Description |
|---|---|---|
pacId | string | The child component sending the message. |
messageId | number | Message type constant. Custom messages must be ≥ wakaPAC.MSG_USER. |
wParam | number | First integer parameter — flags, IDs, or small values. |
lParam | number | Second integer parameter — flags, IDs, or small values. |
extended | object | Optional. Arbitrary data, available as event.detail in the receiver. |
Returns void | ||
wakaPAC.broadcastToChildren(pacId, messageId, wParam, lParam, extended?)
Delivers a message to all descendants of the given component. The root component itself does not receive the message — only its children, grandchildren, and so on. Traversal is depth-first. The same event object is reused across all dispatches; if a child's msgProc mutates event.detail, subsequent siblings will see the mutation.
| Parameter | Type | Description |
|---|---|---|
pacId | string | The root component whose full subtree receives the message. |
messageId | number | Message type constant. Custom messages must be ≥ wakaPAC.MSG_USER. |
wParam | number | First integer parameter — flags, IDs, or small values. |
lParam | number | Second integer parameter — flags, IDs, or small values. |
extended | object | Optional. Arbitrary data, available as event.detail in the receiver. |
Returns void | ||
Best Practices
- Guard against built-in messages. Add
if (event.message < wakaPAC.MSG_USER) return;at the top of anymsgProcthat only handles custom messages. Without it, every mouse move and keypress calls your handler unnecessarily. - Use named constants. Define
const MSG_ITEM_SELECTED = wakaPAC.MSG_USER + 1at the top of your script. Inline numbers are not self-documenting and break when you insert a new message between existing ones. - Include senderPacId in the payload. When sending from a child to a parent, include
{ senderPacId: this.pacId, ... }in the extended data. The parent can then respond directly by pacId without coupling to any specific child implementation. - Send only what's needed. Avoid passing entire component state through a message. Narrow payloads make contracts easier to understand and debug.
- Broadcast sparingly.
broadcastMessagehits every component. If only two or three components care, target them directly. - Keep cross-tab payloads serialisable.
broadcastMessageGlobalpasses theextendedobject through structured clone. Use only plain values — strings, numbers, booleans, plain objects, and arrays. - Use a specific channel name. The
contentattribute of thewakapac-channelmeta tag should identify your application — for example"my-app". A generic name risks interference if multiple WakaPAC applications run on the same origin. - Watch for loops. A sends to B, B sends back to A — this is the easiest way to create an infinite message cycle. Add a guard or a flag to prevent re-entrancy.