Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does jQuery remove body and head tags when populating an iframe with a style tag but not without?

If I run the following code, the body and head tags are removed.

<iframe src='blank.htm'>
    <html>
    <head>
        <style type="text/css">

        </style>
    </head>
    <body>
        test
    </body>
    </html>
</iframe>
<script>
    $('iframe').load(function () {
        var contents = $('iframe').text();
        $('iframe').contents().find('html').html(contents);
    });
</script>

If I then remove the style tags, everything shows up correctly. Why is this and how can I get it to stop removing the head and body tags? I want the contents to be populated as is, without any manipulation.

like image 301
Middletone Avatar asked Apr 08 '12 04:04

Middletone


2 Answers

I had the same issue and learned from this SO question that in fact it's the browser that does the stripping, as part of its parsing of the innerHTML property, which is what jQuery uses internally in .html().

Rather, the problem is actually that jQuery uses an intermediate new DIV, which I believe is not allowed to contain certain tags, resulting in the stripping.

To the solution, then. It's as simple as setting the innerHTML property of the html element directly, like so:

// This forcibly removes head, body, and other tags, since it internally uses an empty DIV and its innerHTML property
jQuery(targetElement).html(exitHtml);

// This works fine
h = document.getElementsByTagName('html')[0];
h.innerHTML = htmlString;

For your particular case, simply target the html element inside the iframe. Your final code might be:

<script>
    $('iframe').load(function () {
        var contents = $('iframe').text();
        iframeHtml = $('iframe').contents().find('html').get(0);
        iframeHtml.innerHTML = contents;
    });
</script>
like image 184
guillobuteler Avatar answered Nov 15 '22 21:11

guillobuteler


Here are steps you can follow to get an idea of how to copy the iframe content to a string, modify it, and replace it in the iframe. These steps are meant to be performed in your Chrome Debugger and are for educational/demonstration purposes only. Once you understand the process, you can then attempt to replicate in your code on your website.

Run each function one at a time in Chrome Debugger:

// test iframe I loaded in a sandbox also at http://example.com
$('body div:first').before('<iframe src="http://example.com/company"></iframe>');

// retrieved the body of the iframe - I cloned it so I could perform operations
  // on the copy without bothering the iframe content.
iBody = $('iframe:first').contents().find('body').clone();

// got the head
iHead = $('iframe:first').contents().find('head').html();

// if there is script in the body, it really messes things up. So in my tests, 
  // I had to remove it before adding it back to the iframe.
iBody.find("script").remove();

// verify there are no script elements
ibody.html(); 

// replace the iframe head
$('iframe:first').contents().find('head').html(iHead);

// replace the iframe body first with a placeholder. The body is the sensitive 
  // part. If you destroy the DOM, it can make the entire page, including 
    // the parent, turn completely white.
$('iframe:first').contents().find('body').html('<div></div>');

// ** inserted something in the copy to prove we modified it!!
iBody.append("<div style='position:absolute;z-index:5000; color:red; " + 
    "background-color:white;'>JAMES WAS HERE</div>");

// now replace the body
$('iframe:first').contents().find('body').html(iBody);

In the bottom of the website, inside the iframe, I saw my message in red on a white background. I could also see it in the source:

$('iframe:first').contents().find('body').html();

I performed these actions on an anonymous website, using Chrome's Debugger console to run the commands. The first command creates an iframe in the home page with the site's company page in the iframe. The remaining commands get the head and body of that iframe, clone the body, add some stuff, like a "JAMES WAS HERE" message, to the body clone, and then replace the iframe body with that copy.

I also verified the same results by loading http://getsatisfaction.com in my browser and loading http://getsatisfaction.com/explore/product-innovation in the iframe. By repeating the above steps, I saw my message, "JAMES WAS HERE".

**The most severe problem I ran into was not being able to leave the JavaScript or CSS style tags in the copy. The solution for styling is to make all style modifications before adding the body back to the iframe. **

For some reason, the browser attempted to run the JavaScript and threw errors. The context of those errors made me think that the JavaScript was being run in the toplevel context instead of the iframe context! This may be a showstopper for you. You may need to come up with another plan for showing previews in a popup window or inline. Also, this may behave differently on different websites with different JavaScript. It also may behave strangely in IE.

In short, I don't actually see this solution being something that would support a production website, since the JavaScript must be removed in the iframe; however, depending on your needs, it may be possible to make this work in production if you don't need the iframe DOM to use any JavaScript.

Nevertheless, this was an interesting problem to explore!

like image 29
jmort253 Avatar answered Nov 15 '22 22:11

jmort253