Search code examples
javascripthtmlframesframesetscript-tag

How to put a <script> in the "body" of a frameset document?


Usually we put our JavaScript <script> tags at the bottom of an HTML document, right before the closing </body> tag, with the benefit that they are executed after all the elements are already available in the DOM and some more things.

However, I'm using a frame document1 which does have a <frameset> instead of a <body> tag. I don't want to put them in the <head> of the document because they wouldn't have immediate access the DOM elements below3. And I don't want to use <iframe>s in a standard body tag either4. I've tried

<head>
  <title>Framesets are interesting</title>
</head>
<frameset cols="50%,50%">
  <frame id="frame-a" src="a.html">
  <frame id="frame-b" src="b.html">
  <script type="text/javascript">
    console.log("hello world!");
    console.log(document.getElementById("frame-a")); // this is what I'm after
  </script>
</frameset>

However, the script was not executed at all, it didn't even show up in the DOM inspector. Sure, a <frameset> may only contain <frame> and <noframes> tags. But, is there really no way to get scripts execute after a <frame> tag?

Just for reference, placing them after </frameset> like it's sometimes done with <body>s doesn't work either.

1: Yes, I know they're deprecated. They were just the natural choice2 for my project, a neat side-by-side view that shows two documents and scrolls them together in a sophisticated manner.
2: …and I never used them before, so I wanted to give it a try.
3: That is what I ended up with, after all an onload handler is trivial. Still the question remains, I'm curious.
4: works fine, but requires intricate CSS styling


Solution

  • A <script> element can only appear in either a <head> element or <body> element. It cannot appear as a child of a <frameset> element; a <frameset> can only contain <frame> elements, <noframes> elements, or other <frameset> elements. See the Transitional DTD:

    <!ELEMENT FRAMESET - - ((FRAMESET|FRAME)+ & NOFRAMES?) -- window subdivision-->
    

    Normally, browsers are happy to insert elements into other elements where they don't belong in complete defiance of what the DTD says since the DTD is just a rulebook, but this doesn't always happen (for example, you can never put any other flow element into an HTMLParagraphElement no matter how hard you try), so if a browser is refusing to place an HTMLScriptElement in an HTMLFrameSetElement, chances are this is why.

    Any workarounds will involve placing a <script> element in either the frameset's <head> element, or within one of the frames. (You can also place a <script> element within a <noframes> element since <noframes> has the same content model as <body>, but this won't solve your problem for obvious reasons.)