I'm currently builidng a service that lets users create email templates. Users can preview the templates in the UI. The html for the preview is rendered on the server and send to the client. I currently render the "escaped" and "sanitized" content like this:
<div dangerouslySetInnerHTML={{ __html: digestPreview }} />
The problem with this approach is that the templates contain style
tags like: <style>h1 {color: red}</style>
.
This style will be injected into the rest of the page and messes up the rendering of all h1
tags on the screen. Not just the ones within the div
that has the innerHTML
prop set.
I'm looking for a way to render the html without injecting the styles. I don't want to convert all my styles to inline css.
CodePudding user response:
You can try using <iframe>
.
An example:
import { useEffect, useRef } from "react";
import "./styles.css";
export default function App() {
const iframeRef = useRef();
useEffect(() => {
const iframeDoc = iframeRef.current.contentWindow.document;
iframeDoc.open("text/html", "replace");
iframeDoc.write(
"<style>h1 { color: red }</style><h1>Header inside iframe</h1>"
);
iframeDoc.close();
}, []);
return (
<div className="App">
<h1>Styled H1 in the app</h1>
<iframe ref={iframeRef} src="about:blank" title="myIframe" />
</div>
);
}
Full available here: https://codesandbox.io/s/vibrant-drake-000u0x?file=/src/App.js.
Another way is to use Shadow DOM.
CodePudding user response:
Perhaps you can apply a filter on the digestPreview
to remove the styles before passing the markup down.
const htmlString = `
<body>
<style>
h1 {color: red;}
</style>
<h1>hello</h1>
<p>
<style>
h1 {color: red;}
</style>
world
</p>
</body>
`;
function toStylelessDocument(htmlString) {
const doc = (new DOMParser()).parseFromString(htmlString, "text/html");
var treeWalker = document.createTreeWalker(
doc,
NodeFilter.SHOW_ELEMENT, {
acceptNode: function(node) {
if (node.tagName === 'STYLE') {
return NodeFilter.FILTER_ACCEPT;
}
}
},
false
);
let currentNode = treeWalker.currentNode;
const styles = [];
while (currentNode) {
if (currentNode.tagName === "STYLE") {
styles.push(currentNode)
}
currentNode = treeWalker.nextNode();
}
for (let style of styles) {
style.parentElement.removeChild(style);
}
return doc;
}
d = toStylelessDocument(htmlString);
console.log(d.body.innerHTML);
You can use the toStylelessDocument
function create an HTMLDocument with the style
elements removed from the DOMtree
.
const stylelessDocument = toStylelessDocument(digestPreview);
<div dangerouslySetInnerHTML={{ __html: stylelessDocument.body.innerHTML }} />