postMessage communication
Communication between the WebKatalog and the parent page uses the HTML postMessage API.
Overview
The WebKatalog reports events to the parent page and accepts commands from it; both only when it runs in an iFrame.
How it works
sequenceDiagram
participant Parent as Parent page
participant WK as WebKatalog
WK->>Parent: webkatalog-loaded (on load)
WK->>Parent: USER_TRACKING (on actions)
WK->>Parent: ARTICLE_ADDED
WK->>Parent: BASKET_UPDATED
WK->>Parent: CURRENT_ARTICLE (on request)
WK->>Parent: ERROR (on errors)
Parent->>WK: PING_WEBKATALOG (health check)
Parent->>WK: SET_CONFIG (live updates)
Parent->>WK: SET_ARTICLE (set article)
Parent->>WK: REQUEST_CURRENT_ARTICLE (query current article)
Parent->>WK: SET_THEME (sync theme)Event types
Outbound events (WebKatalog → parent page)
| Event | Description |
|---|---|
webkatalog-loaded | App has loaded |
USER_TRACKING | User interactions |
CONTACT_REQUEST | Contact request / lead (planned) |
ARTICLE_ADDED | Article added to the planning |
ARTICLE_REMOVED | Article removed from the planning (planned) |
BASKET_UPDATED | Shopping cart updated |
PROPERTY_UPDATE_STARTED | Configuration update started |
PROPERTY_UPDATED | Configuration updated |
PROPERTY_UPDATE_FAILED | Configuration update failed |
CONFIGURATION_CHANGED | Configuration changes (planned) |
CURRENT_ARTICLE | Current article (response) |
ERROR | Message could not be processed |
Inbound events (parent page → WebKatalog)
| Event | Description |
|---|---|
PING_WEBKATALOG | Health check / readiness ping |
SET_CONFIG | Live updates (price display, discount) |
SET_ARTICLE | Set an external article |
PROPERTY_UPDATED | Alias for SET_ARTICLE (configuration update) |
REQUEST_CURRENT_ARTICLE | Query the current article |
SET_THEME | Sync theme |
Receiving events (parent page)
Basic setup
window.addEventListener('message', (event) => {
// Check origin (recommended)
const allowedOrigin = 'https://web.3doffice.de';
if (event.origin !== allowedOrigin) {
return;
}
const data = event.data;
if (!data || typeof data.type !== 'string') {
return;
}
switch (data.type) {
case 'webkatalog-loaded':
console.log('WebKatalog loaded');
break;
case 'USER_TRACKING':
console.log('Tracking:', data.action, data);
break;
case 'CONTACT_REQUEST':
handleContactRequest(data);
break;
case 'ERROR':
console.error('Error:', data.errorMessage);
break;
}
});Event reference
webkatalog-loaded
Sent as soon as the WebKatalog has fully loaded and is ready.
{
type: 'webkatalog-loaded',
timestamp: '2024-01-15T10:30:00.000Z'
}USER_TRACKING
Sends user interactions for analytics purposes.
Event structure:
{
type: 'USER_TRACKING',
action: 'article_select',
articleId: '12345',
articleName: 'Rechtecktisch 160x80',
timestamp: '2024-01-15T10:30:00.000Z'
}For article_add_to_cart, value (the price) is added.
Actions sent:
| Action | Description | Additional data |
|---|---|---|
article_select | Article selected | articleId, articleName |
article_add_to_cart | Added to the shopping cart | articleId, articleName, value |
article_add_to_wishlist | Added to wishlist | articleId, articleName |
Planned (not currently sent): page_view, article_configure, property_change, view_3d, view_ar, download_pdf, download_cad, share_article, search, filter, sort, navigation.
CONTACT_REQUEST
Planned
Not currently sent.
Intended for contact requests from the WebKatalog (lead generation).
Event structure:
{
type: 'CONTACT_REQUEST',
article: {
id: '12345',
name: 'Rechtecktisch 160x80',
manufacturer: 'Hersteller',
series: 'Serie A',
configuration: 'Breite: 1600mm, Tiefe: 800mm, Farbe: Weiß',
price: 599.00,
currency: 'EUR'
},
user: {
name: 'Max Mustermann',
email: 'max@example.com',
phone: '+49 123 456789'
},
message: 'Ich hätte gerne ein Angebot für diesen Tisch.',
timestamp: '2024-01-15T10:30:00.000Z'
}Handling a contact request
function handleContactRequest(event) {
const { article, user, message } = event;
// Article data
console.log('Requested article:', article.name);
console.log('Configuration:', article.configuration);
console.log('Price:', article.price, article.currency);
// Customer data
console.log('Customer:', user.name);
console.log('Email:', user.email);
console.log('Phone:', user.phone);
// Message
console.log('Message:', message);
// Here: implement your own contact form logic,
// e.g. forward to a CRM system
}Note
The WebKatalog does not include a contact form of its own. You need to implement the form on your website and prefill it with the data from the postMessage event.
ARTICLE_ADDED / ARTICLE_REMOVED
Notifies about changes in the planning.
ARTICLE_ADDED:
{
type: 'ARTICLE_ADDED',
article: {
id: '12345',
name: 'Rechtecktisch 160x80',
manufacturer: 'Hersteller',
series: 'Serie A',
configuration: 'Breite: 1600mm, Tiefe: 800mm'
},
timestamp: '2024-01-15T10:30:00.000Z'
}ARTICLE_REMOVED:
Planned
Not currently sent.
{
type: 'ARTICLE_REMOVED',
article: {
id: '12345',
name: 'Rechtecktisch 160x80',
manufacturer: 'Hersteller',
series: 'Serie A'
},
timestamp: '2024-01-15T10:30:00.000Z'
}BASKET_UPDATED
Sent on every click on "Add to cart" and contains only that article.
{
type: 'BASKET_UPDATED',
items: [
{
id: '12345',
name: 'Rechtecktisch 160x80',
quantity: 1,
price: 599.00
}
],
totalItems: 1,
timestamp: '2024-01-15T10:30:00.000Z'
}PROPERTY_UPDATE / PROPERTY_UPDATE_STARTED / PROPERTY_UPDATE_FAILED
Notifies about configuration changes.
PROPERTY_UPDATE_STARTED:
{
type: 'PROPERTY_UPDATE_STARTED',
timestamp: '2024-01-15T10:30:00.000Z'
}PROPERTY_UPDATED:
{
type: 'PROPERTY_UPDATED',
article: {
id: '12345',
// ... complete article
},
timestamp: '2024-01-15T10:30:00.000Z'
}PROPERTY_UPDATE_FAILED:
{
type: 'PROPERTY_UPDATE_FAILED',
reason: 'Beim Aktualisieren des Artikels ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.',
errorMessage: 'Beim Aktualisieren des Artikels ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.',
timestamp: '2024-01-15T10:30:00.000Z'
}reason (and the identical errorMessage) is one of:
- Text in the language of the WebKatalog (property change failed)
'Missing article id'(SET_ARTICLEwithoutarticle.id)'Failed to apply external update'(SET_ARTICLEfailed)'The article was replaced by a newer article load'withcontext: 'ARTICLE_REPLACED'(see below)
Every PROPERTY_UPDATE_STARTED is followed by exactly one PROPERTY_UPDATED or one PROPERTY_UPDATE_FAILED. A loading indicator on the parent page between the two therefore always ends.
context: 'ARTICLE_REPLACED': While the change was still running, a different article was loaded (catalog click, SET_ARTICLE, reload after SET_CONFIG). The change is discarded and no longer applied to the new article. This is not a server error; the parent page should not show an error message but wait for the ARTICLE_ADDED of the new article.
{
type: 'PROPERTY_UPDATE_FAILED',
reason: 'The article was replaced by a newer article load',
context: 'ARTICLE_REPLACED',
errorMessage: 'The article was replaced by a newer article load',
timestamp: '2024-01-15T10:30:00.000Z'
}CONFIGURATION_CHANGED
Planned
Not currently sent.
{
type: 'CONFIGURATION_CHANGED',
articleId: '12345',
changes: [
{
property: 'Breite',
oldValue: '1400mm',
newValue: '1600mm'
},
{
property: 'Farbe',
oldValue: 'Weiß',
newValue: 'Eiche'
}
],
timestamp: '2024-01-15T10:30:00.000Z'
}CURRENT_ARTICLE
{
type: 'CURRENT_ARTICLE',
article: {
id: '12345',
// ... complete article
},
timestamp: '2024-01-15T10:30:00.000Z'
}ERROR
Sent when an error occurs while processing a message from the parent page. Unknown or incomplete messages are ignored without a reply.
{
type: 'ERROR',
errorCode: 'MSG_ERROR',
errorMessage: 'Failed to process message',
context: 'SET_ARTICLE', // Type of the message
timestamp: '2024-01-15T10:30:00.000Z'
}Messages to the WebKatalog
SET_CONFIG
Live updates of settings:
iframe.contentWindow.postMessage(
{
type: 'SET_CONFIG',
showPrice: false,
discount: 10
},
'https://web.3doffice.de'
);Supported configuration options:
| Option | Type | Description |
|---|---|---|
showPrice | boolean | string | Price display on/off. Accepts true/false or "true"/"false" |
discount | number | string | Discount in percent (0-100). Accepts a number or a string |
Behavior:
showPrice: true(previously off): The current article is reloaded in its current configuration, because the price only comes from the server with an article request. The camera position is kept. A property change in progress is completed first.showPrice: falsehides the price immediately, without reloading.discounttakes effect immediately: the price tab shows the list price struck through, with "Your price · −X %" below it.
Security
Always use the full domain (https://web.3doffice.de) for the origin check instead of *.
PING_WEBKATALOG
Health check / readiness ping. The WebKatalog responds with webkatalog-loaded.
iframe.contentWindow.postMessage('PING_WEBKATALOG', 'https://web.3doffice.de');Response: The WebKatalog sends back webkatalog-loaded.
SET_ARTICLE
Applies the article object sent with the message (e.g. from CURRENT_ARTICLE or PROPERTY_UPDATED) and shows it in the 3D view. The article data is not fetched again from the server; article.id is required.
iframe.contentWindow.postMessage(
{
type: 'SET_ARTICLE',
article: {
id: '12345',
manufacturer: 'Hersteller',
program: 'Serie A',
basearticlenumber: 'ABC-123',
// ... complete IArticle
}
},
'https://web.3doffice.de'
);Note: PROPERTY_UPDATED is an alias for SET_ARTICLE and can be used interchangeably.
REQUEST_CURRENT_ARTICLE
Queries the currently selected article from the WebKatalog.
iframe.contentWindow.postMessage(
{
type: 'REQUEST_CURRENT_ARTICLE'
},
'https://web.3doffice.de'
);Response: The WebKatalog responds with CURRENT_ARTICLE.
SET_THEME
Syncs the theme of the parent page with the WebKatalog.
iframe.contentWindow.postMessage(
{
type: 'SET_THEME',
theme: 'dark' // or 'light'
},
'https://web.3doffice.de'
);Alternatively with the darkmode boolean:
iframe.contentWindow.postMessage(
{
type: 'SET_THEME',
darkmode: true
},
'https://web.3doffice.de'
);Security
- The WebKatalog sends its messages to the parent window with target origin
*. That is why you should always checkevent.originon the parent page. - The WebKatalog only processes messages from its direct parent window (
event.source === window.parent); there is no origin check. - Messages contain article and price data, but never the token.
Receiver-side check (parent page)
window.addEventListener('message', (event) => {
// Check origin
if (event.origin !== 'https://web.3doffice.de') {
return;
}
// Process message
// ...
});Behavior
webkatalog-loadedarrives once, as soon as the view is ready – not only once an article has loaded. The WebKatalog answersPING_WEBKATALOGwithwebkatalog-loadedat any time.- Send commands only after
webkatalog-loaded. Messages that arrive earlier may be lost. - Outside an iFrame, the WebKatalog sends nothing.
- After
SET_ARTICLE, the WebKatalog sends backPROPERTY_UPDATEDwith the new article.PROPERTY_UPDATEDis also accepted as input (likeSET_ARTICLE) – do not send it back, or you will create a loop. BASKET_UPDATEDarrives per click on "Add to cart" and contains only that article (quantity 1);totalItemscounts the line items of this article including children.REQUEST_CURRENT_ARTICLEwithout a loaded article gets no response.