Click here to Skip to main content
Click here to Skip to main content
Go to top

Master Pages using HTML and JavaScript

, 17 Nov 2010
Rate this:
Please Sign up or sign in to vote.
Describes a method whereby Web Master Pages can be developed using HTML and JavaScript.

Introduction

Most web site pages are divided into three parts: header, page contents, and footer. Usually, headers and footers are the same across all pages on the web site, leaving only the page content to vary from page to page. A "master page" allows the page header and footer to be created once and then reused.

In the ASP.NET environment, a master page is defined with place holders for the content that varies from page to page. In a PHP environment, the header and footer are "brought into" a web page by using the PHP include facility. There are third-party products that provide a master page facility in ways akin to ASP.NET. It has also been suggested that a global search and replace operation would suffice.

I have problems with each of these methods. PHP is not an option. The IDE that I use is Visual Studio 2008 Express. There is no built-in PHP support. Global search and replace is, at best, dangerous since unanticipated results can occur. Third party technologies require that I reach into my pocket and make a payment. And although I use an ASP.NET supporting IDE, I want the web pages to be independent of Microsoft, that is, written only in HTML and JavaScript.

This article describes a method to build web site "master pages" using HTML and JavaScript.

Background

The pages that I want to create have the form:

Header Content Footer

The header and the footer must be constant across the site. Not constant in the sense that they cannot be changed, but rather constant in that they are the same from web page to web page. For example:

Header Footer Loading

For my particular purpose, I want the header to contain:

  • A site logo and a target page if the user clicks on the logo
  • A page heading
  • A page subheading
  • A horizontal line that separates the header from the content

Header Example

I want to be able to modify the page heading and page subheading on page load.

I want the footer to contain:

  • Links to other site pages
  • A copyright notice

Footer Example

In the event that I validate a page using the W3C Markup Validation Service, I want to place the W3C validation icon in the footer.

Validated Footer Example

I also want to be able to completely revise the contents of both the header and the footer, independently of the page contents.

Using the Code

The header and footer text reside in text files located in the website directory tree. The pertinent directory structure of my website is:

GGGustafson
    CSS
      GGGustafson.css
    HeaderFooterContents
      footer_contents.txt
      header_contents.txt
    Images
      favicon.ico
      SiteLogo.png
      ValidXHTML10.png
    Scripts
      add_footer.js
      add_header.js
      IO.js
      place_in_outerHTML.js
    ContactMe.html
    Default.html
    PrivacyPolicy.html

Note that I am hosting my site on Microsoft Office Live (MSOL). MSOL expects web pages to be ASPX or HTML pages. For this article, I will be using HTML pages in the MasterPage downloadable source. However, if the reader uses Visual Studio to build web site pages, chose ASPX pages. Once a new page is defined, you may, if desired, delete the aspx.cs and designer.cs pages that are automatically generated by Visual Studio. You may also change the web page extension from .aspx to .html (replying "Yes" in the following dialog box).

Rename Dialog Box

Note, however, that the action attribute of a <form> tag may not have a target with an extension of .html. That is why the ContactMeSuccessful web page in the MasterPage downloadable source has the extension .aspx.

The header is encapsulated in the header_contents.txt file, accessed by the add_header script; the footer is in the footer_contents.txt file, accessed by the add_footer script. On document load, these two JavaScript scripts are executed:

<body onload="add_header('PAGE LOGO',
                           'PAGE LOGO TARGET',
                           'PAGE HEADER',
                           'PAGE SUBHEADER');
                add_footer('PAGE VALIDATION LOGO');">

In the preceding example, pseudo arguments are used. In an actual page, they would be replaced by actual arguments. For example, on my site Default.html page, I use:

<body onload="add_header('Images/SiteLogo.png',
                           'Default.html',
                           'Journeys',
                           'Welcome');
                add_footer('Images/ValidXHTML10.png');">

For the add_header script to execute to completion, four arguments must be passed, and the web page must contain:

<div id="header">
</div>

This "header" <div> is placed wherever the header is to appear in the web page (usually, immediately following the <body> tag).

For the add_footer script to execute to completion, the web page must contain:

<div id="footer">
</div>

The "footer" <div> is placed wherever the footer is to appear in the web page (usually, immediately before the required <script> include tags). The add_footer script accepts one optional argument.

I place the <script></script> blocks following the footer <div> but before the </body> tag. I add the attribute defer="defer" to the <script> include blocks to ensure that the HTML loads completely before the scripts are loaded. Note that the "defer" attribute of the <script> tag is currently implemented only by Internet Explorer. I expect other browsers to implement this standard attribute in the future.

The <script></script> blocks that I include to implement master pages are:

<script type="text/javascript" 
      defer="defer" 
      src="Scripts/place_in_outerHTML.js"></script>
<script type="text/javascript" 
      defer="defer" 
      src="Scripts/IO.js"></script>
<script type="text/javascript" 
      defer="defer" 
      src="Scripts/add_footer.js"></script>
<script type="text/javascript" 
      defer="defer" 
      src="Scripts/add_header.js"></script>

The add_header script and the contents of the header contents file are dependent upon what the header is to contain. I mentioned earlier what I wanted my header to contain. For that, I need four arguments to add_header. The revised version of the add_header script is now:

// ******************************************************* add_header

function add_header ( site_logo,
                      site_logo_target,
                      page_header,
                      page_subheader )
{
  if ( arguments.length == 4 )
    {
    var document_title = "";
    
    document_title = page_header;
    document_title += ' - ' + page_subheader;
    document.title = document_title;

    if ( document.getElementById )
      {
      var header = document.getElementById ( 'header' );

      if ( header )
        {
        var header_contents = read_contents ( 
              "HeaderFooterContents/header_contents.txt" );
      
        if ( header_contents )
          {
          header_contents = header_contents.replace ( 
                                              '{{SiteLogoTarget}}',
                                              site_logo_target );
          header_contents = header_contents.replace ( 
                                              '{{SiteLogo}}',
                                              site_logo );
          header_contents = header_contents.replace ( 
                                              '{{PageHeader}}',
                                              page_header );
          header_contents = header_contents.replace ( 
                                              '{{PageSubHeader}}',
                                              page_subheader );
                                              
          place_in_outerHTML ( header, header_contents );
          }
        }
      }  
    }
}

Note that, at a reader's suggestion, the four substitutable fields (i.e., SiteLogoTarget, SiteLogo, PageHeader, and PageSubHeader) are now enclosed within the delimiters "{{" and "}}", eliminating an earlier ambiguity that arose during the replacement operation.

If four arguments were not supplied, the script exits. A document title is created. If the <div> with the ID of "header" is not found, the script exits. The content of the header_contents text file is then read. If the file was read successfully, various substrings are replaced by their new values. Finally, the revised header contents replace the header <div>'s outerHTML. For the header, I use a header contents file that contains:

<table class="header" 
         cellpadding="0"
         cellspacing="0" >
    <tr>
      <td class="left_column left_header">
        <a href="{{SiteLogoTarget}}" >
          <img alt="Site Logo" 
               src="{{SiteLogo}}" 
               style="height:100px;
                      width:89px;"/>
        </a>
      </td>
      
      <td class="center_column center_header">
        <table>
          <tr class="header_title_subtitle">
            <td>
              <span id="header_title">{{PageHeader}}</span>
            </td>
          </tr>
          <tr class="header_title_subtitle">
            <td>
              <span id="header_subtitle">{{PageSubHeader}}</span>
            </td>
          </tr>
        </table>
      </td>
      
      <td class="right_column right_header">

      </td>
    </tr>
    <tr>
      <td colspan="3">
        <hr class="colored_spacer" />
      </td>
    </tr>
</table>

The add_footer script and the footer contents file contents are dependent upon what the footer is to contain. I mentioned earlier what I wanted my footer to contain. For that, I need one optional argument to add_footer. The revised version of the add_footer script is then:

// ******************************************************* add_footer
function add_footer ( footer_image )
{
    if ( document.getElementById )
    {
      var footer = document.getElementById ( 'footer' );

      if ( footer )
        {
        var footer_contents = read_contents ( 
              "HeaderFooterContents/footer_contents.txt" );

        if ( footer_contents )
          {
          if ( footer_image )
            {
            footer_contents = footer_contents.replace ( 
                                                '{{FooterImage}}',
                                                footer_image );
            footer_contents = footer_contents.replace ( 
                                                '{{DisplayFooterImage}}',
                                                'block' );
            }
          else
            {
            footer_contents = footer_contents.replace ( 
                                                '{{FooterImage}}',
                                                '' );
            footer_contents = footer_contents.replace ( 
                                                '{{DisplayFooterImage}}',
                                                'none' );
            }
          place_in_outerHTML ( footer, footer_contents );
          }
        }
    }  
}

Note that here too, at a reader's suggestion, the two substitutable fields (i.e., FooterImage and DisplayFooterImage) are now enclosed within the delimiters "{{" and "}}", eliminating an earlier ambiguity that arose during the replacement operation.

If the <div> with the ID of "footer" is not found, the script exits. The contents of the footer_contents text file is then read. If the file was successfully read, various substrings are replaced by their new values. Finally, the revised footer contents replace the <div>'s outerHTML. For the footer, I use a footer contents file that contains:

<table class="footer"
         cellpadding="0" 
         cellspacing="4">
    <tr>
      <td class="left_column" 
          rowspan="2">
        <img alt=""
             src="{{FooterImage}}" 
             style="display:{{DisplayFooterImage}};" />
      </td>

      <td class="center_column footer_first_line" >
        <a href="ContactMe.html">
          Contact Me
        </a>
        |
        <a href="PrivacyPolicy.html">
          Privacy Policy
        </a>
      </td>

      <td class="right_column">
      
      </td>
    </tr>
    
    <tr>
      <td class="center_column footer_second_line" >
        © 2010 G. G. Gustafson, All Rights Reserved 
      </td>

      <td class="right_column">
      
      </td>
    </tr>
</table>

The add_footer and add_header scripts access the footer_contents and header_contents files, respectively, through the IO script.

// *************************************************************** IO

// http://codingforums.com/showthread.php?t=143412

// LA MOD String Version. 
// A tiny ajax library by DanDavis

// Revised 20101006
// http://www.quirksmode.org/js/xmlhttp.html

var XMLHttpFactories = [
        function ( )
          {
          return ( new XMLHttpRequest ( ) );
          },
        function ( )
          {
          return ( new ActiveXObject ( "Msxml2.XMLHTTP" ) );
          },
        function ( )
          {
          return ( new ActiveXObject ( "Msxml3.XMLHTTP" ) );
          },
        function ( )
          {
          return ( new ActiveXObject ( "Microsoft.XMLHTTP" ) );
          }
        ];

// ********************************************** createXMLHTTPObject

function createXMLHTTPObject()
{
    var xmlhttp = false;

    for ( var i = 0; ( i < XMLHttpFactories.length ); i++ )
      {
      try
        {
        xmlhttp = XMLHttpFactories [ i ] ( );
        }

      catch ( e )
        {
        continue;
        }

      break;
      }

    return ( xmlhttp );
}

// **************************************************** read_contents

function read_contents ( url )
{
    var request = createXMLHTTPObject ( );

    request.open ( 'GET', url, false );
    request.setRequestHeader ( 'Content-Type', 'text/html' );
    request.send ( '' );

    return ( request.responseText );
}

The JavaScript IO function is the reason that this "master page" paradigm works. Without it, it would have been impossible to read in the web page header and footer text.

Lastly, the place_in_outerHTML script should be mentioned.

// *********************************************** place_in_outerHTML

function place_in_outerHTML ( element, 
                            contents )
{

    if ( element.outerHTML )
      {
      element.outerHTML = contents;
      }
    else
      {
      element.innerHTML = contents;    
      }
}

Firefox does not support outerHTML as an element property (it is currently only available in Internet Explorer). To keep the browser differences out of add_header and add_footer, I invoke place_in_outerHTML. This script is not quite what I want, but again, it works. By placing the JavaScript in a separate file, I can revisit the code, and possibly make changes, without disturbing the rest of the site.

Web Page Template

Whenever I build a new web page, I use the following template as a starting point:

<!DOCTYPE html PUBLIC 
            "-//W3C//DTD XHTML 1.0 Transitional//EN"
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title></title>

      <link rel="shortcut icon" 
            media="screen,print" 
            href="Images/favicon.ico" />

      <link type="text/css" 
            rel="Stylesheet" 
            media="screen,print" 
            href="CSS/GGGustafson.css" />

    </head>
    <body onload="add_header('PAGE LOGO',
                             'PAGE LOGO TARGET',
                             'PAGE HEADER',
                             'PAGE SUBHEADER');
                  add_footer('PAGE VALIDATION LOGO');">

      <div id="header">
      </div>

      <table class="content" 
             cellpadding="0" 
             cellspacing="0">
        <tr>
          <td class="left_column left_content">

          </td>

          <td class="center_column center_content">

          </td>

          <td class="right_column right_content">

          </td>
        </tr>
      </table>

      <div id="footer">
      </div>

      <script type="text/javascript" 
              defer="defer" 
              src="Scripts/place_in_outerHTML.js"></script>
      <script type="text/javascript" 
              defer="defer" 
              src="Scripts/IO.js"></script>
      <script type="text/javascript" 
              defer="defer" 
              src="Scripts/add_footer.js"></script>
      <script type="text/javascript" 
              defer="defer" 
              src="Scripts/add_header.js"></script>

    </body>
</html>

Points of Interest

Most importantly, the header, footer, and accessing functions presented in this article are what I use in my website; they should be modified to meet your needs.

I encourage you to use the web page template (or something like it). I have found that it saves me a lot of time building new pages.

I use <table>s to organize my web pages (others may argue for the use of <div>s for that purpose). But to obtain the effects of a percentage based three column document, I found that <table>s worked better than <div>s. Of course, then again, maybe I'm not well enough versed to make <div>s work.

Lastly, I strongly suggest that the contents of the header and the footer be initially built in the web page template, in place of the header and footer <div>s, respectively. When the template looks the way you want it to look, then move the header and footer contents to their respective text files and reinsert the header and footer <div>s.

References

Browsers Tested Successfully

Internet ExplorerFirefoxGoogle ChromeOperaSafari

History

  • 10/06/2010 - Revised IO.js, add_header.js, and add_footer.js to improve security.
  • 11/15/2010 - Revised article to address readers' comments and correct typographic and logic errors; ensured that paradigm would work on all common browsers.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

gggustafson
Software Developer (Senior)
United States United States
I started programming more than 42 years ago using AutoCoder and RPG (note no suffixing numbers). Programs and data were entered using punched cards. Turnaround between submitting a job for compilation and execution was about 3 hours. So much for the "good old days!" Today, I particularly enjoy programming real-time software. I consider myself capable in WinForms, Mobile Apps, and C# although there are occasions that I yearn to return to C and the Win32 API.

Comments and Discussions

 
GeneralMy vote of 5 Pinmemberguidoux13-Sep-14 1:35 
GeneralRe: My vote of 5 Pinprofessionalgggustafson13-Sep-14 4:39 
GeneralRe: My vote of 5 Pinmemberguidoux13-Sep-14 5:25 
GeneralRe: My vote of 5 Pinprofessionalgggustafson13-Sep-14 8:33 
GeneralMy vote of 5 PinprofessionalPaulo Augusto Künzel24-Jul-14 6:14 
GeneralRe: My vote of 5 Pinprofessionalgggustafson24-Jul-14 6:24 
SuggestionSuggest to replace template by .net instead of javascript. PinmemberLoveJenny20-Mar-14 15:18 
GeneralRe: Suggest to replace template by .net instead of javascript. Pinprofessionalgggustafson20-Mar-14 16:30 
GeneralRe: Suggest to replace template by .net instead of javascript. PinmemberLoveJenny23-Mar-14 17:08 
GeneralRe: Suggest to replace template by .net instead of javascript. Pinprofessionalgggustafson23-Mar-14 18:43 
GeneralMy vote of 5 PinprofessionalANKIT_JHA18-Aug-13 21:09 
GeneralRe: My vote of 5 Pinmembergggustafson19-Aug-13 4:49 
GeneralMy vote of 1 Pinmemberhaiderhussain23-Feb-12 0:53 
GeneralRe: My vote of 1 Pinmembergggustafson19-Aug-13 5:28 
GeneralRe: My vote of 1 PinmemberMember 1040926718-Nov-13 4:52 
GeneralRe: My vote of 1 Pinprofessionalgggustafson18-Nov-13 6:47 
GeneralMy vote of 5 Pinmemberenitan5-May-11 1:09 
GeneralMy vote of 4 PinmemberShahriar Iqbal Chowdhury16-Nov-10 23:37 
GeneralMy vote of 5 Pinmemberjsc426-Oct-10 22:30 
I do similar types of things in generating common web pages.
 
A couple of hints:
 
You do not need javascript: in onload and you could further reduce your code size by having a combined header + footer routine (one call with 5 arguments instead of 2 calls with 4 + 1 arguments).
 
Encase your substitutable fields within delimiters (I use {{ and }}), you can then do the replacements without worrying about the order (your ambiguity between SiteLogo and SiteLogoTarget would disappear as they would be called {{SiteLogo}} and {{SiteLogoTarget}}).
AnswerRe: My vote of 5 Pinmembergggustafson6-Nov-10 9:44 
GeneralRe: My vote of 5 Pinmembergggustafson13-Jun-11 6:13 
GeneralMy vote of 1 PinmemberSmirkinGherkin4-Oct-10 5:17 
GeneralRe: My vote of 1 Pinmembergggustafson6-Oct-10 13:11 
GeneralRe: My vote of 1 PinmemberSmirkinGherkin6-Oct-10 21:57 
AnswerRe: My vote of 1 Pinmembergggustafson7-Oct-10 4:53 
GeneralRe: My vote of 1 PinmemberSmirkinGherkin7-Oct-10 5:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 17 Nov 2010
Article Copyright 2010 by gggustafson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid