Adds a slug field for human-readable anchors ("domain.com/page/#my-section") to TYPO3 content elements. By default, this anchor is rendered as the header's id attribute.
"Speaking URLs" are a must-have feature for web pages. TYPO3 v9 and newer provide the Routing feature for this.
Former TYPO3 versions needed the third-party extensions RealURL or CoolUri.
TYPO3 also provides the navigational content elements "Section index" and "Section index of subpages from selected pages",
which will build a list of pages and their included content elements.
These content elements will be linked by their unique id, e.g.:
https://www.example.org/a-sub-page/#c123
Copied!
It's working well, but it's not human-readable.
What does it do?
First of all, this extension provides human-readable URL fragments for TYPO3 content elements:
Furthermore, the extension allows to set anchor links next to the header.
An editor can activate these with a checkbox for individual content elements.
Note
In fact, you can see both features in action on this very documentation page:
Hover your mouse over a heading. A link symbol will appear. This is the anchor link.
Click on this anchor. Your browser will jump to this section. A readable fragment will be added to the URL.
There are several ways in TYPO3 to link directly to a content element:
Content elements "Section index" and
"Section index of subpages from selected pages".
These menus will automatically provide a list of links to
content elements on (selected) subpages.
Rich text editor (RTE)
You can set individual links to content elements in the RTE through
the Link Browser.
Link fields
You can e.g. add a link to the content element's header.
Whenever the subsequent requirements are fulfilled,
the human-readable URL anchor will be used in these links.
When will my URL anchor be used?
TYPO3 will use your human-readable URL anchor if the following
requirements are met:
Of course, the backend field for the URL anchor must be filled.
Your header must be visible in the frontend (not set to "Hidden").
Otherwise, links to this content element will still use the default,
which is the element's unique id.
Using the anchor fields
1.) Getting human-readable anchors
You can use the following text field to set a more readable anchor for a
TYPO3 content element.
The button on the right allows you to automatically generate an anchor from the
current contents of the header field.
You can, of course, modify the anchor afterwards.
Text field for a human-readable URL anchor
2.) Extra: Adding a new link next to the header
Please hover your mouse cursor over a heading in this tutorial.
Do you see the link symbol showing up next to it?
It allows you to navigate to this section of the page–exactly like the
menu links which were mentioned in the preface above.
If you activate this checkbox in your content element (and you filled the
URL anchor field), a link like this will be added to the header:
Checkbox to show an additional link anchor next to the header
Note
You may not see this checkbox. That would mean that your administrator has
disabled it.
Mass editing of anchors
The List module makes it possible to display the content of several
fields at once and gives you the ability to edit several records with one action.
This allows you, for example, to quickly add human-readable anchors to all content elements
on a page at once.
Click the Show columns button and enable the field "Human-readable URL #anchor".
A table with content elements in the List module. The anchor field is already enabled
and visible as the last column the right.
Select the content elements you want to update, then click the Edit columns button
Using the Edit columns button will open the header field together with every enabled column in the list.
Note
In TYPO3 v12, this button does not exist yet. Open the Single Table View by clicking on the table's title
"Page content ()" (see screenshot above) first, then select the content elements.
The middle "Edit" button in the table will provide the same functionality.
Selecting records for editing in TYPO3 v13 and newer
Edit the anchor fields of multiple content elements
Mass editing of anchors using the buttons on the right, with manual adjustments as needed
Note
The buttons to generate an anchor from the header's contents will only work if the header field
is visible in the editing form. Otherwise, you can only manually edit the human-readable anchor.
Correct syntax for URL anchors
Please be aware that only a limited set of characters can be used for
such an URL anchor (officially called a "fragment identifier").
These are technical limitations.
Also, all URL anchors on the same page must be unique! If you use the
same URL anchor in two or more content elements on the same page, they will
be automatically appended with increasing numbering when saving.
If you write a non-supported character in the URL anchor field, it will be
replaced when you leave the field or save the content element.
Tip
For your convenience, you can e.g. fill the URL anchor field with
"Learn all about Product X".
When you leave the field or save the content element, it will be converted to
"learn-all-about-product-x".
The following characters are allowed in this field:
ASCII characters (a–z)
digits (0–9)
underscores (_)
hyphens (-)
periods (.)
As soon as you leave the field (or the content element is saved) …
… all characters are converted to lowercase.
… HTML elements are removed completely.
… space characters are converted to the hyphen character.
… special characters (e.g. äöü߀) are converted to ASCII equivalents.
Tip
Readability: The URL anchor doesn't have to match your header exactly.
But it will be more understandable for your website's visitor if it
reflects the content of this text section.
The extension needs to be installed as any other extension of TYPO3 CMS.
Perform the following steps:
Get the extension
Use composer: Use composer require sebkln/content-slug
Use the Extension Manager: Select "Get extensions". Press the
"Update now" button and search for the extension key content_slug.
Download the latest version by using the Import button, or click on the
extension's title to download a version of your choice.
Activate the extension in the TYPO3 backend module
Admin Tools > Extensions.
If you're using TYPO3 11.4 (or later) and composer, all extensions are
automatically considered as active.
Include the static template
The extension ships some TypoScript code which needs to be included.
Switch to the root page of your website.
Open the Template record.
Switch to the Includes tab of the template record.
Select "Speaking URL fragments (anchors) (content_slug)" in the field
Include static (from extensions). It must be loaded after the static
template "Fluid Content Elements (fluid_styled_content)"!
Include the static template
Customize configuration and templates
This extension extends EXT:fluid_styled_content and therefore provides
customized Fluid templates.
Important
If you already customized the same Fluid templates for your website,
you'll need to extend your version with some new variables and ViewHelpers.
This has changed.fragmentIdentifier is still used as the variable name
in the Header/Header.html partial. But instead of the raw contents of the
tx_content_slug_fragment field, it now contains the complete URL fragment
configured with TypoScript:
fragmentIdentifier: fragmentIdentifier,
Copied!
This should only be a breaking change if you use one of the Header
partials directly from this extension and customized the other one in your
sitepackage.
Should be non-breaking: "Section Index" templates
Both "Section Index" templates were simplified, using the new TypoScript
variable fragmentIdentifier.
The default configuration provides the same URL fragment as before
(a combination of UID and human-readable fragment).
If you customized these templates in your sitepackage, this should not be
a breaking change – the tx_content_slug_fragment database field is still
available, after all. And if you adjusted the prefix, this will remain the same.
Attention
Nonetheless, I advise that you update all templates with the new
TypoScript variable. This ensures that the URL fragment is configured at a
central place.
By default, this extension will generate the following HTML markup for
TYPO3 content element headers:
<header><h1id="c35-section-of-interest"class="">
This is the header of an interesting part of my article
<!-- This link is only rendered if 'Set link to #anchor' is activated --><aclass="headline-anchor"href="#c35-section-of-interest">#</a></h1></header>
Copied!
As you can see, the fragment identifier is used as an id attribute in HTML.
This id can then be referenced in a link.
Potential issue
Warning
A URL fragment could accidentally match the id of an element in the
website's HTML template (e.g. "#main-navigation").
This template-related identifier could be styled with CSS, or be accessed with
JavaScript. Most likely, this would end in unexpected side effects.
By adding the uid of the current TYPO3 content element, we'll circumvent this
potential issue.
Tip
You can remove or adapt the default prefix or configure a suffix instead.
The extension provides TypoScript configuration
with stdWrap support for this.
You can customize the suffix in plugin.tx_contentslug.urlFragmentSuffix
Default
0
settings.replaceFragmentInPageLinks
Property
settings.replaceFragmentInPageLinks
Data type
boolean
Description
When activated, fragment links set in the RTE or TCA fields of type
inputLink are replaced with the human-readable fragment identifier.
Default
1
settings.checkForHiddenHeaders
Property
settings.checkForHiddenHeaders
Data type
boolean
Description
If disabled, fragment links are replaced even if the content element's
header is hidden. Use this with care!
See TypoScript Setup Reference for details.
By default, this extension renders the human-readable fragment as an id attribute
on the content element's header. Therefore, a given fragment will not be used
for content elements that have a hidden (non-rendered) header.
If you disable this setting, fragment links are replaced even if the content element's
header is hidden. You will then need to render this attribute on a different HTML tag.
You will also need to hide the checkbox "Set link to #anchor",
or migrate the corresponding HTML markup as well.
Recommendation: add the id attribute to the Fluid layout of content elements.
<divid="c{data.uid}"class="frame frame-{data.frame_class} frame-type-{data.CType} frame-layout-{data.layout}{f:if(condition: data.space_before_class, then: ' frame-space-before-{data.space_before_class}')}{f:if(condition: data.space_after_class, then: ' frame-space-after-{data.space_after_class}')}"><f:ifcondition="{data._LOCALIZED_UID}"><aid="c{data._LOCALIZED_UID}"></a></f:if><!-- Add these lines: --><f:ifcondition="{fragmentIdentifier}"><divid="{fragmentIdentifier}"></div></f:if><!-- etc. --></div>
Copied!
Default
1 (via TypoScript constant)
Assemble the fragmentIdentifier variable
This variable is available in all Fluid templates of EXT:fluid_styled_content
and allows to configure the complete URL fragment at a central place.
Attention
This variable is also processed in the following classes:
The custom DataProcessor FragmentIdentifierProcessor, which will
process the URL fragments for the "Section Index" content elements.
The ModifyFragment event listener, which allows to overwrite fragments for
links set in the rich text editor or in TCA fields with renderType
inputLink.
lib.contentElement {
// Override default templates of fluid_styled_content:
partialRootPaths.101 = EXT:content_slug/Resources/Private/Overrides/fluid_styled_content/Partials/
templateRootPaths.101 = EXT:content_slug/Resources/Private/Overrides/fluid_styled_content/Templates/// Build a complete fragment identifier with possible prefix and suffix:
variables {
fragmentIdentifier = COA
fragmentIdentifier {
if.isTrue.field = tx_content_slug_fragment
10 =< plugin.tx_contentslug.urlFragmentPrefix
20 = TEXT20.field = tx_content_slug_fragment
30 =< plugin.tx_contentslug.urlFragmentSuffix
stdWrap.trim = 1
}
}
}
Copied!
Use FragmentIdentifierProcessor for "Section Index" menus
The menu content elements of type "Section Index" are built with DataProcessors.
To get the configured fragmentIdentifier variable for each of the
linked content elements in these menus, the custom FragmentIdentifierProcessor
is needed.
// Process 'fragmentIdentifier' variable in section menus:
tt_content.menu_section.dataProcessing.10.dataProcessing.20.dataProcessing.5 = fragment-identifier
tt_content.menu_section_pages.dataProcessing.10.dataProcessing.20.dataProcessing.5 = fragment-identifier
Copied!
Sanitize custom data with postUserFunc
In case you append or prepend some custom strings, you can use the fragment
evaluation to sanitize the completed URL fragment again:
This extension enhances the existing TYPO3 content elements, which are commonly
rendered with EXT:fluid_styled_content. Therefore, customized Fluid templates
have to be provided by this extension.
It also works well with the Bootstrap Package.
In both cases, be sure to load the configuration of this extension afterEXT:fluid_styled_content or EXT:bootstrap_package.
TypoScript setup
You can configure the fragment identifier with TypoScript.
If you customize the templates, override the template paths of the content elements.
Note the spelling: with# in the anchor link, without# in
the id attribute!
Also important: if header_layout is set to default, the Header.html
partial is called a second time. Therefore, we need to transfer our additional
variables again:
The TYPO3 content elements of CType"Section Index" and
"Section Index of subpages from selected pages" both build a list of pages and
their included content elements.
By default, the content elements will be linked by their unique id, e.g.
https://www.example.org/a-sub-page/#c123.
The new Fluid condition will check if a fragment identifier is given for the
content element.
Note
The human-readable fragment can only rendered if the header is not hidden.
Therefore, we also need to check if the header_layout is set to 100.
This is taken care of in the custom DataProcessor,
which is added to both Section Index menus.
If available, the configured fragment identifier is then rendered
(identical to the anchor link in Header.html).
Otherwise, the default anchor to the content element is rendered (#c123).
1. Adding fragment fields to additional content elements
The custom fields of EXT:content_slug are added to the TCA palette headers.
This palette is used by most of the content elements in the TYPO3 core.
Some extensions might not use this palette, though. Instead, a reduced or
custom palette is used to display the header field with some selected
fields in the backend.
A good example is EXT:beautyofcode.
This useful extension by developer Felix Nagel allows to render code examples
with syntax highlighting and line numbers.
It uses the reduced palette header (mind the missing s at the end).
Solution A: Replace the palette
One possible way to add the fragment-related fields is to replace the header palette for its CType:
Solution C: Add the fragment fields to another palette
Some custom content element might use a completely new palette to render the
header field with some extras. In this case, you can use the TYPO3 API to
add the fields to this palette.
You need to adjust the name of the custom palette (line 3) and set the
position to before or after an existing field in this palette (line 5).
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addFieldsToPalette('tt_content', // Table for TYPO3 content elements'custom_palette', // The palette which should contain the fragment fields'--linebreak--, tx_content_slug_fragment, tx_content_slug_link', // The fields, rendered in a new line'after:some_field'// Position of the fragment fields);
Copied!
Warning
While you could add the fragment fields to the existing header palette,
be aware of the consequences: this would also add them to CTypes like
shortcut and html, which won't render the header in the frontend
(by default).
2. Removing the uniqueInPid evaluation
What does the uniqueInPid evaluation even do?
It's a mechanism that prevents duplicate field values on the same page.
This is very helpful for our URL fragments, as they need to be
unique on each page (otherwise the browser can't distinguish the anchor links).
Why would I even consider to remove it then!?
Imagine a website with translated content on the same page.
The uniqueInPid evaluation doesn't distinguish between languages.
That means you can't have identical anchor links in e.g. English and German.
In practice this seldom is an issue. But it might become one
if you want to use technical terms or brand names as anchors
(e.g. https://www.example.org/info/#typo3).
How to remove it from the list of eval functions
Caution
I strongly advise to only remove this evaluation on websites with few and
well-trained editors that fully understand the consequences.
The following code removes uniqueInPid from the list of eval functions.
You mustn't remove the other two evaluations!
// Original:
$GLOBALS['TCA']['tt_content']['columns']['tx_content_slug_fragment']['config']['eval'] = 'trim,Sebkln\\ContentSlug\\Evaluation\\FragmentEvaluation,uniqueInPid';
// Remove the evaluation for all content elements:
$GLOBALS['TCA']['tt_content']['columns']['tx_content_slug_fragment']['config']['eval'] = 'trim,Sebkln\\ContentSlug\\Evaluation\\FragmentEvaluation';
// Or, maybe better: only remove it for selected content elements:
$GLOBALS['TCA']['tt_content']['types']['beautyofcode_contentrenderer']['columnsOverrides']['tx_content_slug_fragment']['config']['eval'] = 'trim,Sebkln\\ContentSlug\\Evaluation\\FragmentEvaluation';
$GLOBALS['TCA']['tt_content']['types']['textpic']['columnsOverrides']['tx_content_slug_fragment']['config']['eval'] = 'trim,Sebkln\\ContentSlug\\Evaluation\\FragmentEvaluation';
While the DataProcessor can process TypoScript references (=<)
to other cObjects insidefragmentIdentifier,
lib.contentElement.variables.fragmentIdentifier mustn't be a
reference by itself.
// This would work:
lib.contentElement {
variables.fragmentIdentifier < lib.yourCustomFragment
}
// This would work:
lib.contentElement {
variables.fragmentIdentifier = COA
variables.fragmentIdentifier {
10 =< lib.yourCustomFragment
}
}
// This won't work:
lib.contentElement {
variables.fragmentIdentifier =< lib.yourCustomFragment
}