10

I'm trying to avoid using a data URI because I do not want the generated document to be stored in the browser's history. Is it possible to replace the entire HTML document in-place?

I tried jQuery("html").html("<html>....</html>"), but the style information does not survive.

MD XF
  • 7,860
  • 7
  • 40
  • 71
james
  • 1,181
  • 3
  • 8
  • 5
  • I'm confused by your statement "style information does not survive", the styles will be linked to, or included in the document, so if you replace the whole document you would expect the styles to get lost? Do you really need to replace the /whole/ document? What are you trying to achieve from the visitors point of view? – Andrew M May 13 '10 at 09:20
  • sorry, i meant that the *new* style information, .html("......"), doesn't survive. – james May 13 '10 at 11:34
  • I didn't realize you were talking about *new* style info. That comment got me wondering; I've updated my answer a bit. – T.J. Crowder May 13 '10 at 12:48
  • May be this [answer](http://stackoverflow.com/a/1236378/1770571) is helpful. – Salma Gomaa Feb 15 '17 at 07:52
  • May be this answer is helpful http://stackoverflow.com/a/1236378/1770571 – Salma Gomaa Feb 15 '17 at 07:54

2 Answers2

16

You probably want to do this:

jQuery("body").html("new content");

...where "new content" would ideally only include the markup that would normally appear within the body element and not the rest. That will replace the body element's contents, whilst leaving anything you have in head (like style sheet information) alone. If you also want to update the title, you can do that via document.title = "new title";

Edit I got to wondering about replacing everything inside the html element, whether that would work, and what would happen. So I did this:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
    font-weight: bold;
    color:       blue;
}
</style>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'></script>
<script type='text/javascript'>
(function() {
    $(document).ready(pageInit);
    function pageInit() {
        $('#btnChange').live('click', changePage);
        $('#btnHiThere').live('click', sayHi);
    }

    function changePage() {
        $('html').html(
            "<head><\/head>" +
            "<body>" +
            "<input type='button' id='btnHiThere' value='Click for Alert'>" +
            "<p>Note how this text now looks.<\/p>" +
            "<\/body>"
        );
    }

    function sayHi() {
        alert("Hi there");
    }
})();
</script>
</head>
<body>
<input type='button' id='btnHiThere' value='Click for Alert'>
<input type='button' id='btnChange' value='Change Page'>
<p>Note now this text current appears in a sans-serif, bold, blue font.</p>
</body>
</html>

And the results were quite interesting — I end up with a DOM structure that doesn't have a head or body at all, just html with the descendants of head and body inside. This is probably what's messing up the (new) styling in the new content. I get essentially the same result setting innerHTML directly (which may be why it doesn't work in jQuery; jQuery uses innerHTML when it can, although it's very sophisticated about not doing so when it can't); whereas if I do something similar by explicitly creating the head and body elements via document.createElement and document.appendChild, it works.

All of which almost certainly means this is more effort than it's worth.

But: Note that changing the content of the head and body elements seems to work just fine:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
    font-family: sans-serif;
    font-weight: bold;
    color:       blue;
}
</style>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'></script>
<script type='text/javascript'>
(function() {
    $(document).ready(pageInit);
    function pageInit() {
        $('#btnChange').live('click', changePage);
        $('#btnHiThere').live('click', sayHi);
    }

    function changePage() {
        $('head').html(
            "<style type='text/css'>\n" +
            "body { color: green; }\n" +
            "<\/style>\n"
        );
        $('body').html(
            "<input type='button' id='btnHiThere' value='Click for Alert'>" +
            "<p>Note how this text now looks.<\/p>"
        );
    }

    function sayHi() {
        alert("Hi there");
    }
})();
</script>
</head>
<body>
<input type='button' id='btnHiThere' value='Click for Alert'>
<input type='button' id='btnChange' value='Change Page'>
<p>Note now this text current appears in a sans-serif, bold, blue font.</p>
</body>
</html>

So if you separate the "page" you're loading into head and body parts, you can easily update it in place.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • so it's not possible to load a new tag? – james May 13 '10 at 10:12
  • @james: It *may* be possible, but if you do, you'll need to make sure it has everything in it you want to still have (like the style sheet references). You probably don't have to worry about maintaining script tags; once script has been loaded and evaluated, it's no longer bound to the element that created it and removing the element has no effect on the script. – T.J. Crowder May 13 '10 at 10:46
  • I'll note (much belated, yes), that the presence of heavy JS in the head (through Optimizely or Ensighten, for instance), can make even replacing just the body untenable. – Melissa Avery-Weir Aug 07 '12 at 16:10
2

No jquery:

var newString = [
      "<!doctype html>"
    , "<html>"
    , "<head>"
    , "</head>"
    , "<body>"
    , "<h1>new content</h1>"
    , "</body>"
    , "</html>"
].join("");

document.getElementById('myIframeId').contentDocument.documentElement.outerHTML = newString;
bob
  • 7,539
  • 2
  • 46
  • 42