DEV Community

James Moberg
James Moberg

Posted on

Embed a Full HTML Document Inline Using Shadow DOM

We use ColdFusion/CFML to generate valid HTML documents for PDF generation using jsoup & WKHTMLTOPDF. If the generated HTML content is simply outputted onto an existing webpage, the webpage becomes invalid (due to double DOCTYPE "inception") and the website's global CSS styles will polluting the preview.

In order to view the final result correctly, the HTML document would have to be viewed independently within a new tab (using window.open or target="_blank") or embedded in an iFrame.

Another approach that we've been experimenting with uses the browser's shadow DOM. This has worked well for our needs so far, but is pretty specific... it generates a standalone shadow DOM and populates it with the content within an existing webpage so it can be more faithfully rendered without any interactivity. Our PDF & email previews are now rendered inline and are much easier to review.

Source Code

https://gist.github.com/JamoCA/0774f707e8b18b61bc95b5f2c39b1f9f

<!--- createShadowHtml UDF - Using ColdFusion to generate HTML to create & populate a shadow DOM.
NOTE: This approach requires using absolute HREF/SRC references for links and static resources.
GIST: https://gist.github.com/JamoCA/0774f707e8b18b61bc95b5f2c39b1f9f
BLOG: https://dev.to/gamesover/embed-a-full-html-document-inline-using-shadow-dom-5d5d
TWEET: https://x.com/gamesover/status/1836493265186754801
--->
<cfscript>
string function createShadowHtml(required string html, string id="", boolean enableLinks=true, string placeholder="Previewable content will be displayed here...") hint="I generate a shadow DOM on-the-fly to preview HTML/CSS (No JS)" {
local.newLine = createobject("java", "java.lang.System").getProperty("line.separator");
local.id = (isvalid("variablename", arguments.id)) ? arguments.id : "shadow" & randrange(10000,99999,"SHA1PRNG");
local.html = ["<div id=""#local.id#"">#trim(arguments.placeholder)#</div>"];
arrayappend(local.html, "<script>");
arrayappend(local.html, "var pane_#local.id# = document.querySelector('###local.id#');");
arrayappend(local.html, "var shadow_#local.id# = pane_#local.id#.attachShadow({mode: 'open'});");
arrayappend(local.html, "var html_#local.id# = #serializejson({"html": trim(arguments.html)})#");
arrayappend(local.html, "shadow_#local.id#.innerHTML = html_#local.id#.html;");
if (arguments.enableLinks){
arrayappend(local.html, "shadowLinks_#local.id# = shadow_#local.id#.querySelectorAll('a');");
arrayappend(local.html, "for (link of shadowLinks_#local.id#){");
arrayappend(local.html, "link.addEventListener('click', function(e){");
arrayappend(local.html, "e.preventDefault();");
arrayappend(local.html, "alert('Sorry. Links aren\'t enabled in preview mode.');");
arrayappend(local.html, "});");
arrayappend(local.html, "}");
}
arrayappend(local.html, "</script>");
return arraytolist(local.html, local.newLine);
}
</cfscript>

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay