Retcon is a tiny Craft CMS plugin adding a series of powerful Twig filters for modifying HTML content. Here are some of the things Retcon can do:
- Add attributes (e.g.
class="foobar"
,style="color:red;"
ordata-foo
) using CSS selectors (e.g.'img'
,'div.foobar'
,'p:first-child'
etc) - Append values to existing attributes
- Remove attributes completely (e.g. removing all inline
style
attributes) - Transform inline images (it even uses Imager, if installed)
- Add srcset or lazyloading to inline images (again, using Imager if installed)
- Remove or unwrap DOM nodes
- Wrap DOM nodes (e.g. wrap all
<span>
tags in a<p>
) - Extract DOM nodes (e.g. remove everything except
<img>
tags) - Inject strings or HTML content
- Change tag names (e.g. change all occurrences of
<p>
to<span>
, or changediv.foobar
top.foobar
)
...and much more!
This plugin requires Craft CMS 5.0+
Retcon uses PHP's native DOMDocument class to parse and modify HTML. Additionally, Masterminds' HTML5 library is used for HTML5 support, and Symfony's DomCrawler and CssSelector components are used to enable the powerful jQuery-like selector syntax.
Retcon exposes a series of different methods for modifying HTML. Most methods take a selector
parameter (i.e. the selector(s) for the elements you want to modify, e.g. 'img'
, 'p'
or 'div.foobar'
), and some take additional parameters for further configuration.
Note that it doesn't matter if your HTML is from a WYSIWYG field (Redactor or CK Editor) or just a regular ol' string. If it's HTML, Retcon will eat it.
All of Retcon's methods are exposed as Twig filters, which is how Retcon is primarily designed to be used. For example, if you wanted to add a classname 'image'
to all images in a Redactor field called body
, here's how that'd look:
{{ entry.body | retconAttr('img', { class: 'image' }) }}
Note that for the Twig filters, the prefix retcon
is added to the method name – i.e. the attr
method becomes the retconAttr
filter, the transform
method becomes the retconTransform
filter, etc.
For use cases where your HTML is not in a field or variable, the apply tag pair syntax works nicely – the following example adds rel="noopener noreferrer"
to all <a>
tags with target="_blank"
:
{% apply retconAttr('a[target="_blank"]:not([rel])', { rel: 'noopener noreferrer' }) %}
{# A whole bunch of HTML in here #}
....
{% endapply %}
Being Twig filters, chaining multiple Retcon methods will of course work:
{{ entry.body | retconChange('h1,h2,h4,h5,h6', 'h3') | retconAttr('h3', { class: 'heading') }}
Another option is to use the "catch-all" filter retcon
, which takes a single array containing the names of the methods you want to run in sequence, and their parameters:
{{ entry.body | retcon([
['change', 'h1,h2,h4,h5,h6', 'h3'],
['attr', 'h3', { class: 'heading' }]
]) }}
If you want to use Retcon in a Craft plugin or module, all methods are also available through the mmikkel/retcon/Retcon::getInstance()->retcon
service (note that unlike the Twig filters, the retcon
prefix is missing from the service method names – in other words, retconAttr
is just attr()
):
use mmikkel\retcon\Retcon;
echo Retcon::getInstance()->retcon->attr($entry->body, ['class' => 'image']);
For an actual use case example; here's how the rel="noopener noreferrer"
example could look in a module (basically, the below code would add rel="noopener noreferrer"
automatically to all <a target="_blank">
tags in your templates (unless they've already got a rel
attribute set, of course):
use mmikkel\retcon;
public function init() {
Event::on(
View::class,
View::EVENT_AFTER_RENDER_TEMPLATE,
function (TemplateEvent $event) {
if (!Craft::$app->getRequest()->getIsSiteRequest()) {
return;
}
if ($event->output && Craft::$app->getPlugins()->getPlugin('retcon')) {
$event->output =
Retcon::getInstance()->retcon->attr(
$event->output,
'a[target="_blank"]:not([rel])', [
'rel' => 'noopener noreferrer',
]);
}
}
);
}
A "selector" in Retcon is the same thing as a selector in CSS; i.e. something like 'img'
, '.foo'
or h3 + p
.
Retcon's selector support is close to full parity with CSS, but not every selector will work. See the CssSelector docs for further details.
Multiple selectors can be defined as a comma-separated string (i.e. 'p, span'
) or as an array (i.e. ['p', 'span']
).
New (Retcon 3.1.0+ only):
If you need to limit your selection to top-level nodes only, the 'body'
element selector can be used in conjunction with the child combinator ('>'
). As an example:
{# Make all top level <p> tags red #}
{% apply retconAttr('body > p', { style: 'color: red;' }) %}
<p>I wanna be red</p>
<div>
<p>I wanna be left alone</p>
</div>
{% endapply %}
Result:
<p style="color: red;">I wanna be red</p>
<div>
<p>I wanna be left alone</p>
</div>
The body element selector is supported for all filters.
transform Apply a named or inline image transform to all images. If installed, Retcon uses Imager to apply the transform. New: Retcon also supports Imager's successor, Imager X.
srcset Apply an array of named or inline image transform to all images, for simple srcset support. If installed, Retcon uses Imager to apply the transforms.
lazy Replaces the src attribute of image tags with a transparent, base64 encoded SVG (retaining the original image's aspect ratio); putting the original src URL in a data-attribute
dimensions
Adds width
and height
attributes to image nodes, if they are missing (and the image referenced in the image nodes' src
attribute is a local image file). NEW
autoAlt
Adds Asset title or filename as alternative text for images missing alt
tags
attr
Adds, replaces, appends to or removes a set of attributes and attribute values – e.g. class
. Can be used to remove inline styles.
renameAttr Renames existing attributes for matching selectors, retaining the attribute values
wrap
Wraps stuff in other stuff (e.g. put all <span>
tags in <p>
tags)
unwrap Removes parent node for matching elements; retaining their content
remove Removes all elements matching the given selector(s)
removeEmpty Removes all empty elements. NEW
only Removes everything except the elements matching the given selector(s)
change Changes tag type for all elements matching the given selector(s). Can also be used to remove tags completely, but retaining their content.
inject Inject strings or HTML
Retcon is provided free of charge. The author is not responsible for data loss or any other problems resulting from the use of this plugin. Please see the Wiki page for documentation and examples. and report any bugs, feature requests or other issues here. As Retcon is a hobby project, no promises are made regarding response time, feature implementations or bug amendments.