Home Articles FAQs XREF Games Software Instant Books BBS About FOLDOC RFCs Feedback Sitemap
irt.Org

Related items

Re-directing access within Frames #2

Changing the location of another frame

Re-directing access within Frames

Internet Explorer Floating Frames

Re-directing access within frames - Revisited

You are here: irt.org | Articles | JavaScript | Frame | Re-directing access within frames - Revisited [ previous next ]

Published on: Monday 1st November 1999 By: Martin Webb and Jan Ehrhardt

Introduction

This latest article builds on the two previous articles:

The basic idea being to allow pages to correctly load within the required frameset, even though the visitor may have come directly to one of our pages, or one of the pages may have been framed by another web site.

We thought we had cracked this problem late last year - and up until recently Jan Ehrhardt has been able to direct people to the previous article with the knowledge that it worked in all versions of the main two browsers (Microsoft Internet Explorer and Netscape Navigator). We knew that there was a slight problem in one of the later releases of the Opera browser - but we were not too worried about the Opera browser.

This article extents the previous working example to include further workarounds to fix problems since found with the Microsoft Internet Explorer 3.01 for the Mac, and outstanding problems with the Opera browser.

Now you may feel that these particular browsers are not worth the extra effort and code required to have (an as yet) 100% proof solution.

Browser statistics for irt.org show that MSIE3 as a browser is used by less than 1% of the people visiting irt.org, and that at the time of writing this article the Opera browser doesn't even appear in the top twenty list of browsers/user agents. However, this is a developer web site, and developers have traditionally upgraded to the latest versions of browser before anyone else. Also, you may have a site that is visited by a particular group of individuals that do use an older or non main stream browser. Especially true if your visitors are based in Norway - the home of Opera.

So yet again we will list the incompatabilities that we found, and ways and means to overcome them. We have to realise that we might not have found all the problems with this script - in which case - if you do find a problem, let us know and we'll try and overcome it for you and the rest of the irt.org visitors.

A Summary

In the previous article we showed the following JavaScript code that could be added to any one of your pages that was required to be loaded within a specific frameset:

<html>
<head>

<script type="text/javascript" language="JavaScript"><!--
function checkforframe() {
    if (document.layers && (self.innerHeight == 0 && self.innerWidth == 0)) return;
    if ((top == self) || ((document.images) ? (parent.ergi87143548 ? false : true) : (parent.frames[1].name != 'ergi87143548'))) {
        var newURL = self.location.protocol + '//' + self.location.host + self.location.pathname.substring(0,self.location.pathname.lastIndexOf('/')) + '/frameset.htm?contents.htm&title.htm&main.htm';
        if (document.images) top.location.replace(newURL);
        else top.location.href = newURL;
    }
}
//--></script>

</head>

<body onLoad="checkforframe()">
...

The above code attempts to access frames/images within a parent frame, if one of the particular tests fail, then the document is reloaded within the correct parent frameset:

<html>

<script type="text/javascript" language="JavaScript"><!--
var passed     = location.search ? unescape(location.search.substring(1)) + '&' : '';
var myContents = passed ? passed.substring(0,passed.indexOf('&')) : 'contents.htm';
passed         = passed.substring(passed.indexOf('&')+1);
var myTitle    = passed ? passed.substring(0,passed.indexOf('&')) : 'title.htm';
passed         = passed.substring(passed.indexOf('&')+1);
var myMain     = passed ? passed.substring(0,passed.indexOf('&')) : 'main.htm';

if (top != self) {
    if (document.images)
        top.location.replace(self.location.href);
    else
        top.location.href = self.location.href;
}
else {
    if (document.images) {
        ergi87143548 = new Image();
        document.write('<frameset framespacing="0" border="false" frameborder="0" cols="50%,50%">');
        document.write('    <frame name="contents" scrolling="no" marginwidth="0" marginheight="0" src="' + myContents + '">');
        document.write('    <frameset rows="50%,50%">');
        document.write('        <frame name="title" scrolling="no" marginwidth="0" marginheight="0" src="' + myTitle + '">');
        document.write('        <frame name="main" scrolling="no" marginwidth="0" marginheight="0" src="'+ myMain + '">');
        document.write('    <\/frameset>');
        document.write('<\/frameset>');
    }
    else {
        document.write('<frameset framespacing="0" border="false" frameborder="0" cols="50%,50%">');
        document.write('    <frameset rows="100%,*">');
        document.write('        <frame name="contents" scrolling="no" marginwidth="0" marginheight="0" src="' + myContents + '">');
        document.write('        <frame name="ergi87143548" scrolling="no" marginwidth="0" marginheight="0" SRC="blank.htm">');
        document.write('    <\/frameset>');
        document.write('    <frameset rows="50%,50%">');
        document.write('        <frame name="title" scrolling="no" marginwidth="0" marginheight="0" src="' + myTitle + '">');
        document.write('        <frame name="main" scrolling="no" marginwidth="0" marginheight="0" src="'+ myMain + '">');
        document.write('    <\/frameset>');
        document.write('<\/frameset>');
    }
}
//--></script>

<noscript>
<frameset framespacing="0" border="false" frameborder="0" cols="50%,50%">
    <frame name="contents" scrolling="no" marginwidth="0" marginheight="0" src="contents.htm">
    <frameset rows="50%,50%">
        <frame name="title" scrolling="no" marginwidth="0" marginheight="0" src="title.htm">
        <frame name="main" scrolling="no" marginwidth="0" marginheight="0" src="main.htm">
    </frameset>
</frameset>
</noscript>
 
<body>
<p>
This page uses frames, but your browser does not support them - or in the case of Opera - frames have been disabled
</p>
</body>

</html>

The rest of this article covers the changes made to the previous two scripts.

If this code is new or unintelligible then we suggest you read the article Re-directing access within Frames #2 at http://www.irt.org/articles/js126/ so that you understand what the above code attempts to do.

MSIE3.01 for the Mac and the onLoad event handler

Hal Pawluk:

This does not load the frameset with IE 3.01/Mac.

We tracked down the problem to the fact that the onLoad event handler was not working in MSIE3.01 for the Mac. The reason we had used the onLoad event handler to start the test (to see what the state of the frames were, and if they required redirecting) was due to the previous problem:

In some versions of NN and Opera when a script interrupts the loading of a page to redirect the browser to a framed version, the loading of the page does not always resume.

As you can see, a fix for one problem for one browser, causes a different problem in another. The solution? Browser specific coding.

We dislike using browser specific coding, we would much rather code for support of particular objects, but there is no way to test for a MSIE3.01 Mac browser without resorting to testing the appName and userAgent properties of the navigator object:

MSIE3.01 for the Mac does not suffer the problem with resuming an interrupted download, therefore we use a simple inline test for the browser and invoke the checkforframe() for a MSIE3.01 for the Mac:

<html>
<head>

<script type="text/javascript" language="JavaScript"><!--
function checkforframe() {
    if (document.layers && (self.innerHeight == 0 && self.innerWidth == 0)) return;
    if ((top == self) || ((document.images) ? (parent.ergi87143548 ? false : true) : (parent.frames[1].name != 'ergi87143548'))) {
        var newURL = self.location.protocol + '//' + self.location.host + self.location.pathname.substring(0,self.location.pathname.lastIndexOf('/')) + '/frameset.htm?contents.htm&title.htm&main.htm';
        if (document.images) top.location.replace(newURL);
        else top.location.href = newURL;
    }
}

var ua = navigator.userAgent.toLowercase();

if (navigator.appName == 'Microsoft Internet Explorer" && ua.indexOf('mac') > 0 && ua.indexOf('3.01') > 0)
    checkforframe();
//--></script>

</head>

<body onLoad="checkforframe()">
...

This addition ensures, that even though the onLoad event handler doesn't work, the call to checkforframe() is invoked by our additional inline test.

MSIE3.01 for the Mac and a looping reloading frameset

Once we had fixed the previous problem, Hal Pawluk:

Everything then blanks out and the grey continues to flash until I stop the infinite loop.

MSIE3.01 for the Mac starts to loop endlessly if you add these two lines to the framed pages:

var ua = navigator.userAgent.toLowercase();

if (navigator.appName == 'Microsoft Internet Explorer" && ua.indexOf('mac') > 0 && ua.indexOf('3.01') > 0)
    checkforframe();

To stop the endless loop - which appeared to be a result of a timing issue - we had to make two subsequent changes, first we had to include a setTimeout within the new inline test:

var ua = navigator.userAgent.toLowerCase();

if (navigator.appName == 'Microsoft Internet Explorer' && > ua.indexOf('mac') > 0 && ua.indexOf('3.01') > 0)
    setTimeout('checkforframe()',3000);

This introduces a delay between the loading of the page and any subsequent reloading of a frameset.

The second alteration required the following code in the frameset page to be changed from:

if (top != self) {
    if (document.images)
        top.location.replace(self.location.href);
    else
        top.location.href = self.location.href;
}

To:

function checkframed() {
    if (top != self) {
        if (document.images)
            top.location.replace(self.location.href);
        else
            top.location.href = self.location.href;
    }
}
setTimeout('checkframed()',10000);

MSIE3.01 for the Mac and the Image() constructor

One of the reasons why it was so difficult to check whether a page is within the correct frameset and not within another sites frameset, is that you just cannot get the top.location.href value of a frameset on another site. But you can interrogate some things about the top frame:

  1. the existence of a frame: if (top.frame_name)
  2. the existence of a new Image(): if (top.image_name)
  3. the value of a variable: if (top.variable)

Every browser supports 1. We thought that every browser that supports document.images also supported 2.

In the previous article we chose the following method to determine whether a framed page was in its own frameset:

With the various bugs described in the introduction it is possible to provide a solution that works around all the problems, by providing an image check named ergi87143548 for those browsers that support images, and a frame named ergi87143548 for those that don't.

MSIE3.01/Mac cannot perform 2 or 3 above.

The solution is to include a browser specific check within the frameset.htm page:

var ua = navigator.userAgent.toLowerCase();

var di = (navigator.appName == 'Microsoft Internet Explorer' && ua.indexOf('mac') > 0 && ua.indexOf('3.01') > 0) ? false : document.images;

if (di)	{
    ergi87143548 = new Image();    
    ...
}

This stops MSIE3.01 for the Mac from using the ergi87143548 image, but does allow it to default to the ergi87143548 frame.

Opera 3.0 and Images

Opera 3.0 does not support document.images. A quote from the our previous article:

One side effect of this, is that as all versions of Opera support images...

Well we've since found out that this isn't true. Only since version 3.10 has Opera started supporting images. The effect is that we did not defeat the "100%,*" problem in all versions of Opera. The only solution is to code a browser-specific check:

var star = '*';
if (ua.indexOf('opera') > 0) star = '0';
    document.write('<frameset onLoad="checkframed()" framespacing="0" border="false" frameborder="0" cols="50%,50%">');
    document.write('    <frameset rows="100%,'+star+'">');

Opera 3.0 and Layers

Opera 3.0 chokes when performing the following test:

if (document.layers) { ... }

Opera never generates any JavaScript errors, but stops the execution of the script. So the frameset was never being loaded in Opera 3.0. The solution is to replace the previous generic test with:

if (document.images && document.layers) { ... }

Now that we know that Opera 3.0 doesn't support the Image object, that we can make use of this fact to stop the failure when testing for the support of the Layer object.

JavaScript supports Short-Circuit logic, i.e., when using either && (logical AND) or || (logical OR), then if the expression is know to evaluate to false by testing the first expression using && then the second expression is not evalutated. Similarly, if the first expression evalutes to true using || then the second expression is not evlauted.

Opera and if (top == self)

As a simple test, we included the ability to load the frameset.htm page within one of the frames of the frameset.htm document. This wouldn't happen naturally, but we were interested to see if any browser stumbled over the check:

if (top == self) {...}

We found that Opera does have a problem with this test - it seems to fail to detect that the top frameset with the url of http://www.somewhere.com/frameset.htm, is equivalent to one of its frames with the same URL.

Interesting is that Opera reloads the top frameset.htm if the current URL is http://www.somewhere.com/frameset.htm?contents.htm&title.htm&main.htm In other words: Opera includes the locations search property in the comparison of top == self. This seems to suggest that Opera does not correctly compare one instance of a window object with another instance of a window object.

Opera 3.51+ and replace()

Opera 3.51+ added some extra security (or a bug): it will not let you change the location of the top frameset if it is on another domain. But strangely enough this depends on a setting in Opera. If you select the Opera setting 'Allow Window Create From Document' then the following fails:

top.location.href = 'http://www.some_other_domain.com/frameset.htm';

or:

top.location.replace('http://www.some_other_domain.com/frameset.htm');

If however, you uncheck the Opera setting 'Allow Window Create From Document' then your window will happily move on to some other domain.

The problem revolves around a data tainting issue in Opera. Opera seems to taint the href property so that it cannot be changed, and also the replace() method. However, it is still possible to read the href property:

if (top.location.href != newURL) { ... }

The solution for Opera is to open another window with the required url, using a browser specific check prior to comparing the location of the top frame with the required URL:

top.location.replace(newURL);
if (ua.indexOf('opera') > 0)
    setTimeout('window.open(newURL,"ergi87143548")',1000);

Note that the original replace() method is still used as it is required by all other browsers including all versions of Opera up until 3.51, but as the replace() method is tainted in 3.51 then the Opera 3.51 simply ignores it.

This solution does leave one window open with an incorrectly loaded foreign frameset. The only thing that can be done is to load a blank frame within the current frame (note we can't load a page from another domain into a page from a foreign frame therefore we cannot attempt to load a blank frame into the top frameset) using the following JavaScript function:

function dispFramed() {
    self.location.href = 'framed.htm';
    if (self.location.href.indexOf('framed.htm') == -1) setTimeout ('dispFramed()', 3000);
}

We invoke the above function using:

top.location.replace(newURL);
if (ua.indexOf('opera') > 0) {
    setTimeout('if ((top == self) || (parent.ergi87143548 ? false : true)) window.open(newURL,"ergi87143548")',8000);
    setTimeout('dispFramed()',10000);
}

As you can see we have two timers running, one to load the frameset.htm document into another window after an eight second delay, and another to invoke the dispFramed() function after a ten second delay. As this code will totally replace the current frameset and possibly interupt any running JavaScript code, we have to make sure that this doesn't interupt the request to load our frameset.htm document into another browser window and create a new window when we don't really need one by builing in a large enough time delay so that the replace() method (when not tainted) has a chance to reload the frameset.

We also need to continually invoke the dispFramed() function from within itself (iteration) until, either we notice that the document has changed, or the actual replacement of the document by a blank document interrupts our script. Strange? Maybe, but it all seems to work - almost...

One minor error occurs in 16-bit Netscape Navigator 3.01: ua is defined earlier in the script; the top frame is replaced by the new URL; the definition of ua is subsequently lost; but the script continues to execute the next line of code regardless:

top.location.replace(newURL);
if (ua.indexOf('opera') > 0) {

to remove the JavaScript error message, we can simply use the following instead:

top.location.replace(newURL);
if (navigator.userAgent.indexOf('Opera') > 0) {

Making the code generic

Be warned - this might not suit everyone. But if you do need to frame a lot of different pages, then the solution to date required an amendment to the redirection script for each and every main page. The following addresses that by making the code more generic.

The following code, always loads the three files: contents.htm, title.htm and main.htm. Now normally, the contents.htm and main.htm would always be those two pages (i.e. the list of contents page, and the toolbar page), but the main.htm page could change depending on which page we were interested in.

var newURL = self.location.protocol + '//' + self.location.host + self.location.pathname.substring(0,self.location.pathname.lastIndexOf('/')) + '/frameset.htm?contents.htm&title.htm&main.htm';

A more generic solution, would make this code page dependent on the actual page being loaded and not include a hard coded file name. The following amended code will allow this:

var newURL = self.location.protocol + '//' + self.location.host + self.location.pathname.substring(0,self.location.pathname.lastIndexOf('/')) + '/frameset.htm?contents.htm&title.htm&' + self.location.pathname.substring(self.location.pathname.lastIndexOf('/')+1,self.location.pathname.length);

You will however, require the original version (with the hard coded main.htm) within the contents.htm and title.htm pages, otherwise you'll end up with one or other of these pages within the main frame as well as in its correct frame.

The Fixed Code

frameset.htm:

<html>

<script type="text/javascript" language="JavaScript"><!--
var passed     = location.search ? unescape(location.search.substring(1)) + '&' : '';
var myContents = passed ? passed.substring(0,passed.indexOf('&')) : 'contents.htm';
passed         = passed.substring(passed.indexOf('&')+1);
var myTitle    = passed ? passed.substring(0,passed.indexOf('&')) : 'title.htm';
passed         = passed.substring(passed.indexOf('&')+1);
var myMain     = passed ? passed.substring(0,passed.indexOf('&')) : 'main.htm';

function checkframed() {
    if (top != self) { if (document.images) top.location.replace(self.location.href); else top.location.href = self.location.href; }
}

setTimeout('checkframed()',10000);

var ua = navigator.userAgent.toLowerCase();

var di = (navigator.appName == 'Microsoft Internet Explorer' && ua.indexOf('mac') > 0 && ua.indexOf('3.01') > 0) ? false : document.images;

if (di)	{
        ergi87143548 = new Image();
        document.write('<frameset onLoad="checkframed()" framespacing="0" border="false" frameborder="0" cols="50%,50%">');
        document.write('    <frame name="contents" target="main" scrolling="no" marginwidth="0" marginheight="0" src="' + myContents + '">');
        document.write('    <frameset rows="50%,50%">');
        document.write('        <frame name="title" target="main" scrolling="no" marginwidth="0" marginheight="0" src="' + myTitle + '">');
        document.write('        <frame name="main" scrolling="no" marginwidth="0" marginheight="0" src="'+ myMain + '">');
        document.write('    <\/frameset>');
        document.write('<\/frameset>');
}
else {
        var star = '*';
        if (ua.indexOf('opera') > 0) star = '0';
        document.write('<frameset onLoad="checkframed()" framespacing="0" border="false" frameborder="0" cols="50%,50%">');
               document.write('    <frameset rows="100%,'+star+'">');
        document.write('        <frame name="contents" target="main" scrolling="no" marginwidth="0" marginheight="0" src="' + myContents + '">');
               document.write('        <frame name="ergi87143548" target="main" scrolling="no" marginwidth="0" marginheight="0" src="blank.htm">');
               document.write('    <\/frameset>');
        document.write('    <frameset rows="50%,50%">');
        document.write('        <frame name="title" target="main" scrolling="no" marginwidth="0" marginheight="0" src="' + myTitle + '">');
        document.write('        <frame name="main" scrolling="no" marginwidth="0" marginheight="0" src="'+ myMain + '">');
        document.write('    <\/frameset>');
        document.write('<\/frameset>');
}
//--></script>

<noscript>
<frameset framespacing="0" border="false" frameborder="0" cols="50%,50%">
    <frame name="contents" target="main" scrolling="no" marginwidth="0" marginheight="0" src="contents.htm">
    <frameset rows="50%,50%">
        <frame name="title" target="main" scrolling="no" marginwidth="0" marginheight="0" src="title.htm">
        <frame name="main" scrolling="no" marginwidth="0" marginheight="0" src="main.htm">
    </frameset>
</frameset>
</noscript>

<body>
<p>
This page uses frames, but your browser does not support them - or in the case of Opera - frames have been disabled
</p>
</body>

</html>

The code to be added within one of your pages to be framed in the above frameset:

<html>
<head>

<script type="text/javascript" language="JavaScript"><!--
var newURL = self.location.protocol + '//' + self.location.host + self.location.pathname.substring(0,self.location.pathname.lastIndexOf('/')) + '/frameset.htm?contents.htm&title.htm&main.htm';

function dispFramed() {
    self.location.href = 'framed.htm';
    if (self.location.href.indexOf('framed.htm') == -1) setTimeout('dispFramed()',3000);
}

function checkforframe() {
    if (document.images && (document.layers && (self.innerHeight == 0 && self.innerWidth  == 0))) return;
    if ((top == self) || ((document.images) ? (parent.ergi87143548 ? false : true) : (parent.frames[1].name != 'ergi87143548'))) {
        if (document.images) {
            top.location.replace(newURL);
            if (navigator.userAgent.indexOf('Opera') > 0) {
                setTimeout('window.open(newURL,"ergi87143548")',8000);
                setTimeout('dispFramed()',10000);
            }
        }
        else top.location.href = newURL;
    }
}

var ua = navigator.userAgent.toLowerCase();

if (navigator.appName == 'Microsoft Internet Explorer' && ua.indexOf('mac') > 0 && ua.indexOf('3.01') > 0) setTimeout('checkforframe()',3000);
//--></script>

</head>

<body onLoad="checkforframe()">
...

Working Example

You can try out the working example for yourself:

Load the complete frameset.

Load the individuals documents on their own: contents, title or main.

Load the two of the documents into an alien frameset.

Load three of the documents into another servers frameset.

The Small Print

Because of the way MSIE and Opera work offline, the above frame re-direction techniques will not work offline.

As you can see, this was a difficult article to write. If you find any further problems with the techniques used then please use the feedback link at the bottom of this page.

Related items

Re-directing access within Frames #2

Changing the location of another frame

Re-directing access within Frames

Internet Explorer Floating Frames

©2018 Martin Webb