Inserting ﹤style﹥ elements into mktoText areas (without having ’em stripped out)
I recommend that all Marketo LP templates have a place for page-level, just-in-time JS and CSS:
<div hidden class="mktoText" id="pageBehaviors" mktoName="Page Behaviors JS & CSS">
That’s where you put stuff when the client makes a last-minute request and you can’t change the global template for fear of side effects, nor are you comfy modifying shared Design Studio .js and .css files linked in the template.
In theory, you’d fill that Page Behaviors JS & CSS container like so:
Ah, but that won’t work. After clicking Apply, Marketo simply discards the <style>
element!
The <script>
element, in contrast, is supported. (Yes, it’s wrapped in old-school commented-CDATA but it works just fine.) Plus there’s a wrapper <div>
added by default.[1]
So you end up with this:
That’s problematic. But there’s an easy solution. First, include this JavaScript in all your templates:
<script>
class HydratableStyle extends HTMLElement {
connectedCallback() {
if(document.readyState !== "loading"){
throw ("Element must be in the initial document: " + this.outerHTML);
}
document.addEventListener("DOMContentLoaded", (e) => {
let style = document.createElement("style");
style.textContent = this.textContent;
this.textContent = "";
this.appendChild(style);
});
}
};
customElements.define('style-hydratable', HydratableStyle);
</script>
You can put the <script>
in the <head>
or <body>
, whichever you prefer. Just don’t inject it with a tag manager. It needs to be in the initial document.
Then, you can put your same CSS styles inside a new <style-hydratable>
tag, exactly as you would in a <style>
:
Marketo will remove line breaks on Apply. But that’s fine, since CSS doesn’t care if it’s vertical or horizontal:
How it works
First, a warning: the JS above represents a break with TEKNKL tradition, as it doesn’t work in Internet Explorer. That’s right, I’ve finally conceded that — at least with something presumably non-fatal like CSS — it’s okay to exclude the group of people forced to use IE in 2022.
(This doesn’t change my position that developers must not assume a client doesn’t care about IE. Rather, confirm and document that you’re testing in version N of Chrome, v. N of Firefox, v. N of Safari, v. N of Mobile Safari, etc.)
There’s ultimately one way to turn that <style-hydratable>
into a live <style>
, which is copying its text content into a new HTMLStyleElement, constructed with document.createElement
.
So the JS above first upgrades every <style-hydratable>
into a HydratableStyle Custom Element. CEs fire a callback when each connects to the DOM. Here, each callback function (1) creates a new HTMLStyleElement, (2) copies the HydratableStyle’s inner text into it, (3) empties the original text, and (4) injects the new, real style into the DOM. Nothing really special inside the callback.
The auto-hydration means we don’t need to search for <style-hydratable>
elements using document.querySelectorAll("style-hydratable")
. They essentially find themselves.
Alternate approaches
<style-hydratable>
need not be a Custom Element. (Using CEs and class
makes it non-IE.) It could be an Unknown Element and still hold the CSS temporarily. Or, better than relying on Unknown Elements, use a data block — that is, an HTMLScriptElement with a non-executable type, like <script type="text/css">
. But I’m keeping it cutting-edge for once.😊
Notes
[1] Remember, the parent <div class="mktoText">
is set to hidden
in the template. So it doesn’t matter that it has a child <div>
as that’s hidden too.