3

Here is the example I prepared to tell about the problem easier.

http://codepen.io/anon/pen/EVpYXm

As you can see the initial <html> is set to display a text:

"This is the old html!"

It sets the whole content to the data in the variable myHtml. however here is what I notice:

the style is not carried from the <body> element. Moreover, the <body> element is somehow not created at all!

Here is the sring myHtml, tidied up to display as an html:

<html>
<head>
<title>Title Here</title>
<link href='style.css' rel='stylesheet' type='text/css'/>
</head>
<body style='background-color: red'>
<div>Div!</div>
</body>
</html>

I've realized that when link element is removed, everything works fine. Try it, see it yourself.

Stuck with this issue for the last few hours. Looking for a result.

Here is the full code:

page html:

<html>
  This is the old html!
</html>

javascript:

$(function(){
  var myHtml = "<html><head><title>Title Here</title><link href='style.css' rel='stylesheet' type='text/css'/></head><body style='background-color: red'><div>Div!</div></body></html>"
  $("html").html(myHtml);
})

The main purpose of this question is to understand the reason of this behavior as well as finding the best solution.

Mia
  • 6,220
  • 12
  • 47
  • 81
  • Rather than the brute-force approach of setting the entire HTML section, just change small pieces as needed (such as setting the HTML and style of the `div` only). – musical_coder Nov 02 '15 at 01:45
  • right now I'm looking for an answer to the specific case I described above. Otherwise I change partsi normally. – Mia Nov 02 '15 at 01:45
  • @JaromandaX did you check the codepen link? – Mia Nov 02 '15 at 01:45
  • 1
    Post a *complete* code example in your question please. If codepen is ever inaccessible then your question loses all value without a complete example. That said, what do you expect `href='style.css'` to do in your example? How is codepen going to load that stylesheet? – j08691 Nov 02 '15 at 01:47
  • complete code is posted. refresh and check please. – Mia Nov 02 '15 at 01:47
  • @j08691 link does nothing, it breaks the code. That's the whole intention of it. – Mia Nov 02 '15 at 01:48
  • @j08691 that's why my question exits in the first place. – Mia Nov 02 '15 at 01:48
  • @JaromandaX I've explained the desired behavior. Tell me which part you don't understand and I'll clear it up for you. – Mia Nov 02 '15 at 01:50
  • Assuming this did what you want you'd have `...` because you're adding to `` **not** replacing. – Popnoodles Nov 02 '15 at 01:51
  • How am I replacing it @Popnoodles? I'm setting the html directly via `.html()` – Mia Nov 02 '15 at 01:52
  • "because you're adding to **not** replacing" – Popnoodles Nov 02 '15 at 01:54
  • Possible duplicate of [How do I replace the entire HTML node using jQuery](http://stackoverflow.com/questions/1236360/how-do-i-replace-the-entire-html-node-using-jquery) - Your answer is there http://codepen.io/anon/pen/EVpYXm – Popnoodles Nov 02 '15 at 01:54
  • @Popnoodles do you want to provide a working example? Feel free to edit my codepen. I'm not sure I get what you mean. And no, my question is def. not a duplicate of what you've posted as my problem depends on the weird issue about the link element I've explained in the thread question. – Mia Nov 02 '15 at 01:56
  • I did provide a working example using the answer in that duplicate. Here it is again http://codepen.io/anon/pen/EVpYXm – Popnoodles Nov 02 '15 at 01:57
  • @JaromandaX no sarcasm here - I'm direct about everything – Mia Nov 02 '15 at 01:57

2 Answers2

2

The issue is that, when you use jQuery's html(val), it does something like this:

html: function(value) {
  /* ... */
  // See if we can take a shortcut and just use innerHTML
  if ( typeof value === "string" && !rnoInnerhtml.test( value ) && /* ... */) {
    /* ... */ elem.innerHTML = value; /* ... */
  }
  /* ... */
}

That is, it checks the string with the regex rnoInnerhtml, which is

rnoInnerhtml = /<(?:script|style|link)/i

Therefore, presumably to avoid inserting stylesheets, jQuery avoids innerHTML and does complicated things with domManip.

I recommend using native innerHTML:

$("html").prop('innerHTML', myHtml);

var myHtml = "<head><title>Title Here</title><link href='style.css' rel='stylesheet' type='text/css'/></head><body style='background-color: red'><div>Div!</div></body>"
$("html").prop('innerHTML', myHtml);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
This is the old html!

Or with vanilla-js:

document.documentElement.innerHTML = myHtml;

var myHtml = "<head><title>Title Here</title><link href='style.css' rel='stylesheet' type='text/css'/></head><body style='background-color: red'><div>Div!</div></body>"
document.documentElement.innerHTML = myHtml;
This is the old html!
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • does this do the same? (innerHtml) – Mia Nov 02 '15 at 01:48
  • Why and who is downvoting this? Is there any problem with this? – Mia Nov 02 '15 at 01:50
  • 1
    @Mia Yes, jQuery's `html` should behave as a getter or setter of `innerHTML`. I guess I'm being downvoted by [jQuery fans which don't like vanilla-js](http://i.stack.imgur.com/sGhaO.gif). – Oriol Nov 02 '15 at 01:53
  • lol, okay, thanks. You got my vote up. However I want to understand what the problem is in my question. – Mia Nov 02 '15 at 01:54
0

The problem is that you're appending an html element inside the root html element, which is undefined behavior and cannot be rendered reliably by the browser. Strip the opening and closing <html> and </html> tags from your myHtml string and it works!

Working demo

$(function() {
  var myHtml = "<head><title>Title Here</title></head><body style='background-color: red; '><div>Div!</div></body>"
  $('html').html(myHtml);
})

Alternatively, you could keep the tags and write directly to the document object instead, which opens a new stream and overwrites the previous document.

// ...
document.write(myHtml);
Guybrush Threepwood
  • 1,253
  • 1
  • 11
  • 21
  • Ok fair enough. I tried it and that didn't work! Must have had a typo. – Popnoodles Nov 02 '15 at 01:58
  • No worries, just don't be too quick to downvote next time. :) – Guybrush Threepwood Nov 02 '15 at 01:58
  • @GuybrushThreepwood thanks for providing an answer --- I need the html tags in my current case. I'm loading full pages via ajax in the production version, this is just an oversimplified example. – Mia Nov 02 '15 at 02:00
  • also if you remove the link element it starts working. The main purpose of the question for me is to understand the reason of this behavior as well as finding the best answer – Mia Nov 02 '15 at 02:01
  • The reason of the behaviour was given in this answer. You're creating invalid HTML. Removing the HTML tags is the solution. – Popnoodles Nov 02 '15 at 02:03
  • @Mia I would not recomment to use AJAX in that way (see [this discussion](http://stackoverflow.com/questions/6189100/should-i-load-an-entire-html-page-with-ajax)) but in any case, you'd have to write to the document object instead. The problem is that an `html` inside an `html` causes undefined behavior. Browsers could render such a document however they want, therefore it can't be reasoned with. – Guybrush Threepwood Nov 02 '15 at 02:04
  • @Mia AJAX is usually reserved to dynamically load *parts* of a document. I could understand the choice If you're going for the SPA feel (you want to load a different document without changing the URL). Otherwise you may be overcomplicating things. – Guybrush Threepwood Nov 02 '15 at 02:09
  • 1
    @GuybrushThreepwood almost exactly my case. I just want to replace my whole html document without changing the url. – Mia Nov 02 '15 at 02:11
  • thank you very much @GuybrushThreepwood - but this is a jquery specific question (as I tried to make it clear in the title). I would love to have a jquery answer to the issue. – Mia Nov 02 '15 at 02:19
  • I don't see why. jQuery *is* JavaScript and this is a one-line solution. Anyway, as you wish. – Guybrush Threepwood Nov 02 '15 at 02:29