Template requirements
The extension has exactly one requirement of your templates: every content element must be identifiable in the rendered HTML.
The content element ID
Each content element needs a "c-id" — its UID prefixed with c — on the
element that wraps it:
<div id="c10" class="frame frame-default frame-type-textpic">
...
</div>
This is how the injected JavaScript maps a DOM node to a database record. No c-id means no edit button for that element.
Nothing to do. fluid_ renders the c-id out of the box,
so the extension works immediately after Installation.
Add the UID to the wrapping element yourself:
<div id="c{data.uid}">
...
</div>
container templates do not render the c-id by default:
<div id="c{data.uid}">
<f:for each="{children_200}" as="record">
<f:format.raw>{record.renderedContent}</f:format.raw>
</f:for>
</div>
DCE elements need the c-id added to the DCE template:
<div class="dce" id="c{contentObject.uid}">
Your template goes here...
</div>
Note
Styling problems may occur with nested content elements, because the injected UI is positioned relative to the wrapping element.
Alternative: the data-frontend-edit attribute
For templates that cannot carry the c-id anchor - dynamic content element
extensions (DCE), other custom Fluid templates - a second matching channel
exists: a data- attribute on the content
element's own wrapping HTML element.
<div data-frontend-edit="tt_content:10" class="my-custom-wrapper">
...
</div>
The bundled xfe: ViewHelper renders this attribute for you:
{namespace xfe=Xima\XimaTypo3FrontendEdit\ViewHelpers}
<div class="my-custom-wrapper"{xfe:editable(record: data)}>
...
</div>
The ViewHelper renders only the attribute, so it goes inside the opening tag
in inline notation. The tag notation <div <xfe:
renders the same output, but it is not valid HTML and breaks IDEs, formatters
and linters.
Both patterns can be mixed freely on the same page; an element only needs one
of them. Unlike the c-id anchor pattern, a data- element is
always treated as the content element itself - no sibling resolution is
attempted.
Alternative: render markers (experimental)
Templates you cannot or do not want to change, e.g. Content Blocks without the
fluid_ layout, can be detected through the site setting
frontendEdit.markerBasedDetection.
While it is active, every rendered content element is wrapped in a pair of HTML
comments:
<!--xfe:b:tt_content:10-->
<div class="my-custom-wrapper">
...
</div>
<!--xfe:e:tt_content:10-->
The markers are an addition to the other two patterns. An element that
already has a c-id or a data- attribute keeps using it.
The output of the content element needs exactly one root element. With
several root elements, or with loose text next to the root element, the
markers cannot point at a single element and are ignored. The element's own
empty anchor is not counted, so the anchor pattern
<a id="c10"></ works.
Two features still rely on the c-id and do not work for elements that are only
detected through markers or the data- attribute:
- Jumping back to the element after saving
(frontendEdit.enableScrollToElement), which uses the fragment
#cof the return URL.{uid} - Drag & Drop Reordering, which collects the movable elements of a column by their c-id.
Render markers lists the operational limits, such as caching, minifiers and non-HTML output.
Editing foreign records (news, addresses, ...)
The data- attribute also works for records from any other
table - not just tt_ - by adding a table prefix:
data-. This covers the classic case of
editing foreign records displayed on a detail page, e.g. a news detail page
rendered by EXT:news:
{namespace xfe=Xima\XimaTypo3FrontendEdit\ViewHelpers}
<div class="news-detail"{xfe:editable(record: newsItem, table: 'tx_news_domain_model_news')}>
<h1>{newsItem.title}</h1>
...
</div>
record takes a record array such as {data} or an object with a
get method, such as an Extbase model or a core Record object.
Alternatively, pass the uid directly.
This is deliberately thin: the menu offers exactly edit, info and
history - no hide, delete or move, since those are meaningful only for
tables this extension understands specifically (tt_, pages).
Permissions are checked the same way as everywhere else in the extension (the
backend user's actual edit rights on that record); a table the current user
cannot edit - or that TYPO3 does not know at all - never gets a menu.
Translated records resolve to the current frontend language automatically,
the same way tt_ does.
Extend the menu the same way as for content elements, via the
FrontendEditDropdownModifyEvent - the record row carries a
_table key so a listener can tell it apart from a tt_ row.
Optional markers
Two features need additional markers in your templates. Both are opt-in — omit them and the corresponding feature simply does not appear.
| Marker | Needed for |
|---|---|
| ColumnTargetViewHelper | "Create new content" buttons per column, and Drag & Drop Reordering (drop targets cannot be resolved without it) |
| Data ViewHelper | Edit links for related records inside a plugin, e.g. single news items in a list |
What cannot be edited
Only content elements belonging to the current page receive a menu. Inherited content — a shared footer pulled in from another page, for example — cannot be edited from the inheriting page. Use the toolbar to jump to the page that owns the record.
See also
- How it works — what happens on page load
- FAQ — troubleshooting when no menu appears