eMy wordpress site has a custom post type called tool
, which has three custom fields called html
, css
, and js
. This tool post type is displayed on my site with a custom template, and a shortcode that renders the contents of the html
, css
, and js
fields in an iframe. This shortcode is created by a function in my functions.php file, and a simplified version of this function looks like this:
function renderTool() {
return '
<html>
<head>
<script
src="https://kit.fontawesome.com/c48c422dea.js"
crossorigin="anonymous"
></script>
<style>
.container {background:blue;}
</style>
</head>
<body>
<div id="container" >
<div id="tool" >
<iframe src="data:text/html;charset=utf-8,' .
'<html>
<head>
<style>' . htmlspecialchars(get_field("css")) . '</style>
<script>' . htmlspecialchars(get_field("js")) . '</script>
</head>
<body>' . htmlspecialchars(get_field("html")) . '</body>
</html>'
. '"></iframe>
</div>
<div id="bar">
<i ></i>
</div>
</div>
<script>
function myfunction() {
console.log("hello world");
}
</script>
</body>
</html>';
}
The problem with this code is that when i use the shortcode which runs this function, it only renders the css, and the html and js are rendered in plain text.
I chatted with openai's chatgpt about it and it recommended me to store the iframe contents in a seperate html file and then to set the iframe src
to this html file as such:
$css = htmlspecialchars(get_field("css"));
$js = htmlspecialchars(get_field("js"));
$html = htmlspecialchars(get_field("html"));
// Create the inner document and save it to a separate file
$inner_document = '
<html>
<head>
<style>
'. $css .'
</style>
<script>
'. $js .'
</script>
</head>
<body>
'. $html .'
</body>
</html>
';
file_put_contents('inner-document.html', $inner_document);
// Use the inner document as the src for the iframe
return '<iframe src="inner-document.html"></iframe>';
This soultion i already dislike because it just seems like it is complicating things even more. When i did try to use this code, the iframe instead rendered the page i was on and an equally broken iframe in it. iframeception.
Another thing i tried was passing the html that should be in the iframe inside a urlencode
as such: (this is a snippet, this code would replace the iframe in my current approach)
<iframe src="data:text/html;charset=utf-8,' . urlencode(
'<html>
<head>
<style>' . htmlspecialchars(get_field("css")) . '</style>
<script>' . htmlspecialchars(get_field("js")) . '</script>
</head>
<body>' . htmlspecialchars(get_field("html")) . '</body>
</html>'
) . '"></iframe>
This just rendered it as raw text into the correct html elements at least (view https://prnt.sc/4I24tFgxQ56m)
To clarify further, these get_field()
functions are from a wordpress plugin called acf, view the documentation here: https://www.advancedcustomfields.com/resources/get_field/
From all of this information i concluded that the error must stem from the string literals not properly being escaped and therefore conflict with/ cause the rest of the values to be returned in string format. Yet i was hoping that the htmlspecialchars was gonna solve it, but it only helped with rendering CSS. In my current solution, I put all the code I want to render inside the iframe's source using a data URL, the iframe closing tag comes after the content of the iframe therefore.
So you can further visualize what i am trying to convey, here is the link to the page that is suffering this bug: https://tropical.team/tools/calculator. It is posible i still have some testing code on here so you will see something a bit diffrent than i described but i will try to change it to this when i get back.
I chatted with chatGPT for over 3 hours yesterday but concluded that when i realized that it started going in circles. I doubt there is a replacement for the get_field
function, or an argument that would help with this, but if there is do tell me, i also have yet to integrate validation for those html, css, and js fields, so for now, just expect proper code being passed.
CodePudding user response:
Instead of passing the html as a data url, a better approach is to use the srcdoc attribute of the iframe. This allows you to pass a string (the html) to it and render it properly. You will have to keep the htmlspecialchars function because otherwise the double quotes that may be returned by get_field as it is user submitted would end the srcdoc value string.