Frames, JavaScript and HTML

From Free Knowledge Base- The DUCK Project: information for everyone
Revision as of 10:04, 19 April 2019 by Admin (Talk | contribs)

Jump to: navigation, search

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.

Frame Buster Buster using 204 status verified working on Google Chrome Version 71.0.3578.98 (Official Build) (64-bit) April 2019.

Inline Frames

First introduced by Microsoft Internet Explorer in 1997, standardized in HTML 4.0 Transitional, allowed in HTML5. Modern browsers 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.

HTML5 Does Away with Frames, Mostly

The <frameset> tag and all of it’s associated tags will not be coming back in HTML 5. The frame and frameset elements are not deprecated in HTML5, they have been designated obsolete. Semantics? By being declared obsolete in HTML5 standard it is said the elements are not to be used by Web developers. User agents will still have to support them and various sections in HTML. In the case of frame, frameset, and noframes they are designated as elements not in HTML because using them damages usability and accessibility - all according to the web standard nerds that think they know best. For more details see HTML5 Obsolete Elements.

the iframe element is still included in HTML5 because frame and frameset elements are not the same thing as the iframe. They do not result in the same product.

  • The frameset element replaces the body element in pages as a means to include a different document model for web pages: they are problematic for usability and accessibility. They break web browser bookmarking. Now what the original purpose for them was can be done more elegantly with CSS.
  • The iframe element will not replace the body of a page. Within the same page body using iframe will include a new browsing context embedded within a block of content. Bookmarking still functions as it should. It works better than the frameset model and is used when the developer needs to include an embedded browsing context such as a Google calendar within a web page.

Not all of the HTML5 changes are positive. Although it is positive to do away with frameset, HTML5 changes to iframe are bad in that they force CSS onto the developer. The Nerds behind this stuff want to shove as much CSS on developers as possible. The original intent of the web was to allow flexibility in creating a pure HTML web site. HTML5 nerds want to force CSS on us. We learn that iframe no longer allows you to specify width and height as attributes in the tag. Instead, this is all handled with CSS.