|
JavaScript Articles Buy this website for $5000 USD Priced to sell, this is an established JavaScript website, growing in popularity as evidenced by its Alex ranking. Includes most website content. The price is NON-NEGOTIABLE. Serious buyers should contactLoading Frames into Dynamically Generated Framesets One of the biggest problems with frames is that you can't always count on users opening them through their corresponding frameset. For example, a Google search result may return a link for a document which was only intended to be opened within a frameset. This, as well as other major problems associated with frames, can be corrected with a small amount of JavaScript code, which I will discuss in this article. Example code Click the button above to download the file frame_demo.zip to your computer. frame_demo.zip contains the following files:
frameHandler.js Introducing frameHandler.js Below is the source code for frameHandler.js, which is in the downloadable example code. FRAMESET_TEMPLATE ='<html>\ <head>\ <title></title>\ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\ </head>\ <frameset rows="*" cols="150,*" border="0">\ <frame src="side_frame.htm" name="sideFrame" scrolling="no" noresize>\ <frame src="__CONTENT_FRAME_URL__" name="contentFrame">\ </frameset>\ </html>'; PARENTED_FLAG = "?parented"; if( location.search != PARENTED_FLAG ) { var contentFrameUrl = location.href + PARENTED_FLAG; var framesetHtml = FRAMESET_TEMPLATE. replace(/__CONTENT_FRAME_URL__/, contentFrameUrl); document.write(framesetHtml); document.close(); } else { parent.document.title = parent.contentFrame.document.title; } You use frameHandler.js by including it in between the opening and closing <head><head> tags of an HTML document as follows: <script src="frameHandler.js" type="text/javascript"></script> content_frame_1.htm in the downloadable example is an ordinary HTML document that includes frameHandler.js in its <head> tag. Open content_frame_1.htm and then look at its HTML code. Notice there is no HTML code that represents the gray sidebar with links that you saw in the browser. The HTML code for the sidebar is actually contained in side_frame.htm. But how can this be? What's happening is that, as a result of including the frameHandler.js script in content_frame_1.htm, the document dynamically generates a frameset which contains two frames: content_frame_1.htm and side_frame.htm. How frameHandler.js works We'll go through the source code one step at a time. In the code below, I assign the contents of the HTML code for creating a frameset to variable FRAMESET_TEMPLATE. Note that in the HTML code, the src attribute of the first <frame> tag is assigned side_frame.htm, which is the relative URL for the frame that acts as a sidebar. Also note that the src attribute of the second <frame> tag is assigned __CONTENT_FRAME_URL__. __CONTENT_FRAME_URL__ does not specify a URL. Instead it acts as a placeholder for the actual URL for the frame, which will be inserted later. FRAMESET_TEMPLATE ='<html>\ <head>\ <title></title>\ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\ </head>\ <frameset rows="*" cols="150,*" border="0">\ <frame src="side_frame.htm" name="sideFrame" scrolling="no" noresize>\ <frame src="__CONTENT_FRAME_URL__" name="contentFrame">\ </frameset>\ </html>'; In the code below, PARENTED_FLAG is used as the query portion for a URL. I'll cover this later. For now, let me stress that parented following the ? character is for demonstration purposes only. You only need the ? character, and anything after that can be an arbitrary string of your choosing, as long as the string contains valid URL characters. PARENTED_FLAG = "?parented";Below, we check to see if the search part of the window's URL is equivalent to PARENTED_FLAG. if( location.search != PARENTED_FLAG )If the search part of the window's URL isn't equal to PARENTED_FLAG, we execute the following block: 1.) {2.) var contentFrameUrl = location.href + PARENTED_FLAG; 3.) 4.) var framesetHtml = FRAMESET_TEMPLATE. 5.) replace(/__CONTENT_FRAME_URL__/, contentFrameUrl); 6.) 7.) document.write(framesetHtml); 8.) document.close(); 9.) } The bulk of the routine resides in this block. For clarity, I've numbered the lines in this block. In line 2, variable contentFrameUrl is assigned the current window's URL with the PARENTED_FLAG appended to it. So, for example, if you clicked on HTML file content_frame_1.htm, and it lived in the following path: http://localhost/content_frame_1.htm, then contentFrameUrl would be assigned: http://localhost/content_frame_1.htm?parented In line 4, variable framesetHtml is assigned the contents of the FRAMESET_TEMPLATE string with its __CONTENT_FRAME_URL__ portion replaced with the contents of contentFrameUrl. Using our example above, where the value of contentFrameUrl was assigned http://localhost/content_frame_1.htm?parented, the value of framesetHtml would now be this: <html><head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> </head> <frameset rows="*" cols="150,*" border="0"> <frame src="side_frame.htm" name="sideFrame" scrolling="no" noresize> <frame src="http://localhost/content_frame_1.htm?parented" name="contentFrame"> </frameset>\ </html> Note that the src attribute of the second <frame> tag is now pointing to the URL of the file which created this window, with "?parented" appended to its end. The significance of "?parented" is very important. We'll to the reason for this soon. In line 7 we write the value of framesetHtml to the document. In line 8, we close the document. When you replace the contents of a document, as we've done, always close it. Older versions of Netscape will get flakey if you don't. Let's take a look at what we've done here. When we wrote the contents of framesetHtml to the document with the document.write() method, what this does is replace the contents of our document with a frameset which has two frames: One is the sidebar frame which has the URL side_frame.htm, and the other frame has the URL of this window, with "?parented" appended to it. Note that this window was initially a regular document. But now it has transformed itself into a frameset by rewriting its contents using document.write(). Make sure that you understand this: We've transformed our document into a frameset by writing frameset HTML to the document using document.write(). Now that we have transformed our document into a frameset, it will act and behave as a frameset. And what does a frameset do? It loads frames included in its frameset. Our dynamically created frameset does just that: It loads the two frames described in its frameset: One is side_frame.htm. The other is the URL of the current window, which, the first time it was loaded, caused us to transform into a frameset. The question arises, how do we keep a document that has the potential to transform itself into a frameset from doing so? What is to stop the window from indefinitely transforming into a frameset and reloading itself? The answer is, if the window's URL has "?parented" appended to the end of it, then we know it is not a frameset and should be treated as a frame within the frameset. Otherwise we transform the current window into a frameset by dynamically generating a frameset via document.write(). We still have to deal with the condition of what happens when the window's URL has "?parented" appended to the end of it. The only thing we need to do once we're a frameset is assign our title to that of the frame named contentFrame. The code below does this. You might wonder where contentFrame came from? Looking back at the HTML code for the frameset you'll see that this is the name of the second frame in the frameset. else{ document.title = parent.contentFrame.document.title; } Setting the target frame Notice how in side_frame.htm I use the <base> tag to globally set the target frame to _parent as follows: <base target="_parent">Setting the target to _parent1 causes the frame to replace the contents of its parent window, which in our case is the frameset. Since the content frame in the example transforms into a frameset, the frameset that is replaced, is replaced with another frameset. The effect is seamless. It looks to the user as if an entirely new page is loaded. In addition, the URL of the frame that just got loaded is displayed in the browser. Caveats The World Wide Web Consortium (W3C) has deprecated the target attribute of the base tag in the HTML 4.0 Specification. Although it is still supported in the Transitional DTD2 used in the example, it isn't supported by the Strict DTD3. If you want Strict HTML 4.0 compatibility, you can get around this problem by using the target attribute of the anchor tag. However, if you want XHTML 1.0 Strict DTD4 capability, you are out of luck as this attribute isn't supported by the XHTML Strict DTD. The W3C has proposed a new and improved way of working with frames in XHTML called XFrames5. At the time of this writing, XFrames is still only a working draft. Another problem with the example is that the border attribute of the <frameset> tag we dynamically generated is not standard HTML. It is however supported by all browsers that I've tested it on, including Opera. After an hour or so of fiddling, I was unable to figure out how to portably get the same result without using the border attribute. There might be a way. If anyone knows, send me an email and I'll give you credit. In any case, you will run into this problem whether your frameset is dynamically generated using my technique or you are creating a frameset in the traditional way by placing the frameset in a separate HTML file. Summary Anti-frame sentiment is rampant amongst web designers. Dr. Jakob Nielsen seems to be one of the pioneering anti-frame zealots. I encourage you to read his article, Why Frames Suck (Most of the Time) appearing in his website, useit.com. Actually, trying to solve problems without thinking creatively sucks. There is nothing wrong with using frames in HTML design. Written by: Moshe Moskowitz |