Extending the frontend UI
For UI that needs to run in the browser - status badges, comment indicators,
custom toolbar buttons - the extension exposes a small, stable facade on
window. and dispatches DOM Customs at
defined lifecycle points. Internal refactors of the underlying JavaScript do
not change these method signatures or event detail shapes - this is
the one part of the frontend JavaScript covered by a semver guarantee.
Note
Everything described here requires frontend editing to be active for the
current backend user (see Introduction). When
editing is disabled, xfe: still fires once with an empty
element map, but no elements are ever registered.
Facade methods
getElementInfo(uid)
Returns the resolved target element and payload for a content element uid, so
consumers never have to re-do DOM resolution (anchor pattern, translation
mapping). Returns null if the uid was not rendered.
const info = window.XimaFrontendEdit.getElementInfo(42);
// { uid: 42, element: HTMLElement, payload: { element: {...}, menu: {...} } }
notify({ title, message, severity })
Shows a toast notification using the extension's own notification manager.
severity is one of ok, info, warning,
error.
window.XimaFrontendEdit.notify({
title: 'Comment added',
message: 'A new comment was posted on this element.',
severity: 'info',
});
registerToolbarItem(uid, buttonSpec)
Adds a button to a content element's hover toolbar, next to the built-in
Edit/More actions buttons. button:
html- inner HTML of the button (e.g. an inline SVG icon)label- accessible label, also used as the tooltiphref- renders an<a>instead of a<button>onClick- click handler
window.XimaFrontendEdit.registerToolbarItem(42, {
html: '<svg>...</svg>',
label: 'Show comments',
onClick: () => openCommentsPanel(42),
});
registerBadge(uid, spec)
Renders a persistent, hover-independent indicator on a content element's
overlay - useful for "live annotating" a page, where a marker must be
visible at a glance rather than only on hover. spec:
htmlorelement- the badge content (HTML string or a DOM element)position- one oftop-left,top-right(default),bottom-left,bottom-rightid- when given, a later call with the same uid + id replaces this badge in place instead of adding a duplicate (useful sincexfe:element-renderedcan fire more than once for the same element)onClick- click handler; the badge only receives pointer events when this is set
Multiple badges registered for the same corner lay out in a row, in registration order, instead of overlapping - each corner is its own slot, created on first use.
window.XimaFrontendEdit.registerBadge(42, {
html: '<span class="my-badge" title="3 comments">3</span>',
position: 'top-left',
id: 'comment-count',
onClick: () => openCommentsPanel(42),
});
setBadgeMode(mode)
Sets a display-mode hook for badges: 'subtle' (default) or 'prominent',
exposed as document.'s data-
attribute. The extension ships no badge content itself (see
registerBadge above), so it has no opinion on what "subtle" vs
"prominent" should actually look like - write your own CSS against the
attribute:
window.XimaFrontendEdit.setBadgeMode('prominent');
/* Small dot only, by default */
.my-badge-label { display: none; }
/* Full label once prominent mode is active */
[data-xfe-badge-mode="prominent"] .my-badge-label { display: inline; }
openBackendView(url, options)
Opens a backend URL in the version-appropriate container: the contextual
sidebar (v14.2+, when enabled and available), the v13 iframe modal, or a new
tab as a fallback - no version checks needed in consumer code. The returnUrl
flash-message deferral mechanism used by edit forms is applied automatically.
options:
target-'tab'forces a new tab regardless of versiontitle- shown in the container's headerwidth- a CSS length (e.g.'600px','50%'); invalid values are ignoredonClose- called with{ reason }when the container closes (reasonis'close', or'saved'for the sidebar's own save flow)reloadOnClose- reload the parent page on close (defaulttrue)-
linkPolicy- a rule or array of rules{ match: string, evaluated against link clicks inside the embedded document.| Reg Exp, action } actionis one of:stay(default when nothing matches) - keep navigation inside the container, same as today's built-in behaviorclose- close the containerignore- swallow the click, do nothingexternal- open the link in a new tab
The policy only ever governs plain link clicks - the built-in save/close buttons of an actual edit form keep working exactly as before.
window.XimaFrontendEdit.openBackendView(commentsBackendUrl, {
title: 'Comments',
width: '480px',
linkPolicy: { match: '/typo3/module/web/layout', action: 'close' },
onClose: ({ reason }) => console.log('comments panel closed:', reason),
});
Lifecycle events
All events are dispatched on document.
xfe:ready
Fired once, after the initial AJAX render (or immediately, with an empty map, when frontend editing is disabled).
detail.elements- plain object keyed by content element uid, each value shaped like thegetElementInfo()return value
xfe:element-rendered
Fired once per content element, right after its overlay/toolbar is built.
detail.uid,detail.element,detail.payload- same asgetElementInfo()detail.overlay,detail.toolbar,detail.dropdown- the underlying DOM nodes (dropdownisnullwhen the element has no context menu)
xfe:dropdown-open / xfe:dropdown-close
Fired when a content element's "More actions" dropdown opens or closes.
detail. identifies the element. Only fires for an actual
open/close interaction - hovering between elements does not trigger a
spurious xfe: for a dropdown that was never open.
document.addEventListener('xfe:element-rendered', (event) => {
const { uid, payload } = event.detail;
if (payload.element.CType === 'list' && payload.element.list_type === 'news_pi1') {
window.XimaFrontendEdit.registerBadge(uid, { html: '<span>News</span>' });
}
});
Minimal integration example
document.addEventListener('xfe:ready', (event) => {
console.log('Frontend Edit ready with', Object.keys(event.detail.elements).length, 'element(s)');
});
document.addEventListener('xfe:element-rendered', (event) => {
const { uid } = event.detail;
window.XimaFrontendEdit.registerToolbarItem(uid, {
label: 'Open comments',
onClick: () => window.XimaFrontendEdit.openBackendView('/comments/' + uid),
});
});