9

I'm creating an HTML editor, similar to this one I'm typing in right now with the output below. I'm using an iframe and dumping the $htmlTextBox.val() into the body of the iframe.

I'm trying to create a stylesheet inside the iframe so that it looks as good as it works.

Thanks in advance!

$htmlTextBox.keyup(function(){
    SetPreview();
});   

function SetPreview()
{
    var doc = $preview[0].contentWindow.document;
    var $body = $("body", doc);

    $body.html($htmlTextBox.val());
}
hunter
  • 62,308
  • 19
  • 113
  • 113

5 Answers5

14

Whilst you can interact with an iframe's document.styleSheets, the old-school reliable way is either to have the stylesheet there in the first place (by writing an iframe-src to point to an empty document with the desired stylesheet), or put it in place with document.write(). For example:

<body>
    <iframe></iframe>
    <script type="text/javascript">

        var d= frames[0].document;
        d.open();
        d.write(
            '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional //EN" "http://www.w3.org/TR/html4/loose.dtd">'+
            '<html><head><style type="text/css">'+
            'body { font-size: 200%; }'+
            '<\/style><\/head><body><\/body><\/html>'
        );
        d.close();

        d.body.innerHTML= '<em>Hello</em>';
    </script>
</body>

(This will also set the iframe document to Standards Mode, assuming that's what you want.)

bobince
  • 528,062
  • 107
  • 651
  • 834
  • I went the route with basic page with the stylesheet included in the . $(document).ready(function(){ SetPreview(); }); does not set the iframe body in Chrome, Safari, or FireFox, but it works in IE... – hunter Mar 09 '09 at 14:28
  • just some background, I'm setting the Preview on page load and then on each keyup in the TextArea – hunter Mar 09 '09 at 15:09
  • 1
    jQuery's document.ready only requires the main document to be loaded, it doesn't have to wait for the iframe content to load. So it can be a race condition to see whether the iframe document's loads before ‘ready’, and hence whether document.body is available yet. – bobince Mar 09 '09 at 15:10
  • (Which is why the otherwise quite ugly document.write() approach is often used.) – bobince Mar 09 '09 at 15:10
  • I will give that a shot. Is there a way to ensure that the iframe is finished loading from the containing document? IE7 worked as expected, which was a shock. That actually makes me think that it should not have worked. – hunter Mar 09 '09 at 16:57
  • The plain old ‘document.onload’ event waits for iframes (and images) to be loaded. Because this is usually not what's wanted, jQuery tries to bind on the ‘DOMContentLoaded’ event instead. But IE doesn't support this event, so on that browser jQuery falls back to the slower, later ‘onload’ option. – bobince Mar 09 '09 at 17:52
  • 1
    (Actually ‘onreadystatechange’, but it's effectively the same thing.) – bobince Mar 09 '09 at 17:53
  • writing to the document worked great! I also make it only do the d.write the 1st time and each request after only sets the HTML. Thanks! – hunter Mar 10 '09 at 13:05
13

Followup:

HTML

<iframe id="message" name="message" />

jQuery

setTimeout(function() {
    var $head = $("#message").contents().find("head");                
    $head.append($("<link/>", 
        { rel: "stylesheet", href: url, type: "text/css" }
    ));                
}, 1); 
hunter
  • 62,308
  • 19
  • 113
  • 113
  • As the content is loaded before the css is applied a flicker of an unstyled page is displayed. Any idea how to prevent the page from being shown until the style has been applied? – ᴍᴀᴛᴛ ʙᴀᴋᴇʀ Feb 26 '15 at 15:00
5

We can insert style tag into iframe.

<style type="text/css" id="cssID">
.className
{
    background-color: red;
}
</style>

<iframe id="iFrameID"></iframe>

<script type="text/javascript">
    $(function () {
        $("#iFrameID").contents().find("head")[0].appendChild(cssID);
        //Or $("#iFrameID").contents().find("head")[0].appendChild($('#cssID')[0]);
    });
</script>
Palanikumar
  • 6,940
  • 4
  • 40
  • 51
1

Pulled this code below from jHtmlArea source. Funny thing is, the feature was there and I didn't realize it. I came across this SO Question when I was trying to figure out how to implement it. :)

This isn't really "jQuery" but it works. I'm guesing something with append isn't working with iframe so they did it old school. Bobince's answer looks good too, and might be more reliable? So far, this seems to work ok.

var e = edit.createElement('link'); 
e.rel = 'stylesheet'; 
e.type = 'text/css'; 
e.href = options.css; 
this.iframe[0].contentWindow.document.getElementsByTagName('head')[0].appendChild(e);

EDIT: I think the reason this works is if it is sameDomain (including creating iframe in js) Otherwise the usual browser security blocks will prevent you from manipulating iframe dom.

See How to apply CSS to iFrame

Community
  • 1
  • 1
Jason
  • 2,691
  • 27
  • 28
1

Assuming you're on the same domain and not dealing with cross domain security, you'll want to check if the iFrame is loaded--NOT JUST THE IFRAME, BUT THE DOCUMENT INSIDE!! (This hung me up for a while whilst trying to use .load())

Then, using jquery, access the contents, access the head, and append your style sheet to the head.

 $(document.getElementById('yourIframeID').contentWindow.document).ready(function() {
      $('yourIframeID').contents().find('head').append('<link rel="stylesheet" href="/path/to/stylesheet/style.css" type="text/css" />');
 });
Will Lanni
  • 923
  • 12
  • 25