Frames, JavaScript and HTML

Revision as of 10:42, 19 April 2019 by Admin (Talk | contribs)

A frame is a window within a window. Using frames allows you to divide a browser window into a collection of panes. Each pane can then be used to display a separate document.

As far as JavaScript is concerned, each frame can be treated as a separate window. In fact, each frame has its own Window object.

JavaScript establishes relationships between the Window objects of frames, creating a hierarchy of Window objects. Each frame is stored as an element in the frames[] array of the Window object of the parent window for that frame. The contents of each element of the frames[] array is a Window object for the frame pane in question.

Frames are created in a special HTML document called a frameset document. Frameset documents have their own DTD that is separate from the XHTML and HTML document DTDs. You may recall when we first mentioned DTDs for XHTML, we said there were three:

  1. transitional,
  2. strict,
  3. and frameset.

The frameset document has no body element, instead it has a frameset element, which is delimited by <frameset> tags. A typical frameset document might look as follows:

<html>
<head>
<title>A simple frames document</title>
</head>
<frameset cols="200,*">
  <frame name="frame1" src="document1.html" />
  <frame name="frame2" src="document2.html" />
</frameset>
</html>

The <frameset> tag defines how the frames are to be laid out on the screen. It has two attributes to do this, cols, which specifies how many columns to split the screen into, and rows, which specifies how many rows to split the screen into. Each specifies that number by a listing of the width of each column or height of each row to be created. This size can be specified as a numberic value, representing pixels, as a percentage of the screen, or using the wild card (an asterisk - *) to signifies to use whatever space is left over.

For the above document, there are three window objects. There is the top-level window that the frameset document has been loaded into. There are also two frames, each with its own window object, that are children of the top-level window. From the top-level window, the two other windows are contained within its frames[] array. Thus, from the top level they could be addressed by their index positions, window.frames[0] and window.frames[1], or by their name attributes, window.frame1 and window.frame2.

Unfortunately, you normally are not referring to the frames from within the top-level window, but rather, within the frames themselves. That is to say, within the documents that have been loaded into those frames. For instance, one frame may contain a menu that performs certain actions on the document in the other window when you click on the options provided. In fact, any code within the documents within the frame panes thinks that the Window object is the Window object for that pane, not the parent Window object for the entire frame set.

As we stated early on, the Window object is the global object, so if each frame has its own Window object, how do we address the other frames? parent and top

The Window object has some properties specifically designed to address the situation of having a hierarchical arrangement of windows. These are top and parent.

window.parent refers to the Window object of the parent window. Thus, in the above coding snippet, if you wanted to use some code in frame1 to change something in frame2, you could address it with the following: window.parent.frame2.someElement.

window.top is a reference to the Window object of the top window element in the window hierarchy. Thus if you have multiple levels of nested frames, you can get to the top of the frameset hierarchy. Otherwise, you would have to repeat the parent property for each level we wanted to step out. This can get tedious quickly:

window.parent.parent.parent.otherframe.someElement

In our example above, since there is only one level of nesting, window.top and window.parent are synonymous.

Of course, if you want to address a frame that is multiple levels down in the hierarchy, you still have to specify the name or frames[] array position of each intermediate Window object on the way down:

window.parent.fLeft.fBottom.someElement
window.parent.frames[0].frames[2].someElement

The advantage of being able to address between frames like this goes beyond the simple ability to get the scripts to talk to other frames. It also allows you to better structure your code by modularizing your scripts.

For instance, by putting all your common scripts that are used by all pages in your top-level frameset document, you then only need to include them once, in one place, and all pages know where they are. Since object tree elements can be passed to variables, you can make the scripts generic, by passing along the name of the frame the code is actually supposed to apply to.

function doSomething(frameName) {
  frameName.status = 'Are you talking to me?';
}
[ ... ]
<input type="button" value="Click Me"
  onclick="doSomething(window.parent.frame2);" />

Clearing Frames

Another useful thing you can do with JavaScript is create a framebuster, which prevents a document from loading into another frame. The code for it is as follows:

if (window.location.href != window.top.location.href) {
  window.top.location.replace(window.location.href);
}

In other words, if the URL of the current document is not the same as the URL as the top level in the window hierarchy, then replace the URL of the top-level Window object with that of the current document. If you are at the top level, then the top property will point to the current Window object and the two values will be the same.

You can do the same thing in the other direction if you want to make sure that a document always occurs in a frame set.

if (window.location.href == window.top.location.href) {
  window.top.location.replace('mainframe.html');
}

Updated "frame-busting" JavaScript

if (parent.frames.length > 0) {
  top.location.replace(document.location);
}

Busting Frame Busting JavaScript

You can defeat basic frame busting JavaScript if the server your page with frames is hosted on supports HTTP status 204 with a custom 204 page.

<script type="text/javascript">
    var prevent_bust = 0  
    window.onbeforeunload = function() { prevent_bust++ }  
    setInterval(function() {  
      if (prevent_bust > 0) {  
        prevent_bust -= 2  
        window.top.location = 'http://server-which-responds-with-204.com'  
      }  
    }, 1)  
</script>

The code creates a counter "prevent_bust" and increments it every time the browser attempts to navigate away from the current page by making use of the window.onbeforeonload event handler. Parent to that is the timer setInterval() which is constantly going. In the loop if prevent_bust ever gets incremented as a result of frame busting JavaScript then the frame is redirected to a custom 204 page. By protocol standard 204 tells the web browser not to redirect anywhere.

Inline Frames

Newer browsers also allow the <iframe> tag to define an inline frame. The same rules for addressing frames applies to inline frames as to regular frames. The only different is that the top-level window contains a normal HTML document instead of a frameset document.

Last modified on 19 April 2019, at 10:42