2

So I am a little confounded here. I do not understand why script executes when placed with jquery's .html() but not with innerHTML. I looked at the source code for jQuery's .html and it still seemed that innerHTML was being used. Can anyone explain this behavior?

I came across this during an ajax response. I was getting back html and a script tag with some javascript in it, and I usually use html, but for some reason had used innerHTML this time thinking there was no difference.

I understand that this might seem to be localized, so I made a jsFiddle with a timeout that behaves similar to an ajax response in order to highlight the issue.

jsFiddle demonstrating the behavior

Travis J
  • 81,153
  • 41
  • 202
  • 273
  • 1
    The HTML standard explicitly mentions that `script` elements are not evaluated when set via `innerHTML` (http://www.w3.org/TR/2008/WD-html5-20080610/dom.html#dynamic0): *"Note: `script` elements inserted using `innerHTML` do not execute when they are inserted."* – Felix Kling Mar 25 '13 at 00:01
  • @FelixKling - Thank you for the link. Not sure why I thought otherwise. – Travis J Mar 25 '13 at 00:07

1 Answers1

3

As far as I can tell from a bit of breakpointin', jQuery will try to use innerHTML, but, failing that, it'll fall back to emptying the target element, and appending the new content via the DOM.

Update: see Felix Kling's comment below; basically, if the string content to be inserted contains script or style/link tags, jQuery won't use straight innerHTML

When that happens - and it does in your case - the content goes through a function called domManip which I don't claim to fully understand, but it does specifically look for and evaluate scripts.

So the short answer is: jQuery's not using innerHTML after all. Wish I could give you a better step-by-step of exactly what happens, but I don't know my way around jQuery's source well enough. I just set a breakpoint (at line 6074 in jQuery-1.9.1.js; the point where jQuery decides not to use innerHTML after all) and stepped around the source a little.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
Flambino
  • 18,507
  • 2
  • 39
  • 58
  • +1 - Good analysis. I noticed that it goes through an interesting process as well but could not for the life of me find where `eval` would get used in the source code. This answer: http://stackoverflow.com/a/1197585/1026459 , seems to indicate that `eval` is used on scripts as well. – Travis J Mar 25 '13 at 00:01
  • 1
    @TravisJ yeah, the script content gets passed to `globalEval()` (see line 597) which does indeed use `eval` – Flambino Mar 25 '13 at 00:02
  • 1
    The test happens in https://github.com/jquery/jquery/blob/1.9.1/src/manipulation.js#L230. `!rnoInnerhtml.test( value )` evaluates to `false`. At the beginning of the file, it is defined as `rnoInnerhtml = /<(?:script|style|link)/i`, i.e. it tests for `script` tags. `.append` is then used instead, which does what you explained (I didn't look into it any further though). – Felix Kling Mar 25 '13 at 00:03
  • @FelixKling Ah, that's the trigger. Thanks for clarifying! – Flambino Mar 25 '13 at 00:05
  • @Flambino - Actually, I do not think that `globalEval` uses `eval`. What it does is build a new script element and then append it to the document head. Thank you for the help though, definitely helped me understand this issue! – Travis J Mar 25 '13 at 00:13
  • 1
    @TravisJ No problem, thanks for the checkmark. But I have to say that `globalEval()` does use actually use `eval` ([see here](https://github.com/jquery/jquery/blob/1.9.1/src/core.js#L577)). No scripts are inserted in the document head (at least not that I can see here in Chrome) – Flambino Mar 25 '13 at 00:24
  • @Flambino - Ah, you are correct! But so am I :P I wonder when they changed it, this project was using an older version (1.4.4). http://pastebin.com/xWaYtC6y – Travis J Mar 25 '13 at 00:27