65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 HTML 和 JavaScript 的母版页

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.74/5 (29投票s)

2010年9月30日

CPOL

7分钟阅读

viewsIcon

268277

downloadIcon

8154

介绍了一种使用 HTML 和 JavaScript 开发 Web 母版页的方法。

引言

大多数网站页面分为三个部分:页眉、页面内容和页脚。通常,网站上的所有页面在页眉和页脚方面都是相同的,只有页面内容会因页面而异。“母版页”允许页面页眉和页脚只创建一次然后重复使用。

在 ASP.NET 环境中,母版页是通过为页面内容变化的部分定义占位符来定义的。在 PHP 环境中,页眉和页脚通过使用 PHP include 功能“引入”到网页中。有一些第三方产品以类似于 ASP.NET 的方式提供母版页功能。也有人建议全局搜索和替换操作就足够了。

我遇到了这些方法的每一个问题。PHP 不是我的选择。我使用的 IDE 是 Visual Studio 2008 Express。它没有内置的 PHP 支持。全局搜索和替换充其量是危险的,因为可能出现意想不到的结果。第三方技术需要我掏钱付款。尽管我使用支持 ASP.NET 的 IDE,但我希望网页独立于 Microsoft,也就是说,只用 HTML 和 JavaScript 编写。

本文介绍了一种使用 HTML 和 JavaScript 构建网站“母版页”的方法。

背景

我想创建的页面是这样的格式:

Header Content Footer

页眉和页脚在整个网站中必须是恒定的。不是不能更改的意义上的恒定,而是从网页到网页相同的意义上的恒定。例如:

Header Footer Loading

对我而言,我希望页眉包含:

  • 一个网站 logo,如果用户点击 logo,则链接到目标页面
  • 页面标题
  • 页面副标题
  • 一条分隔页眉和内容的水平线

Header Example

我想在页面加载时修改页面标题和页面副标题。

我希望页脚包含:

  • 指向其他网站页面的链接
  • 版权声明

Footer Example

如果我使用 W3C Markup Validation Service 验证页面,我希望将 W3C 验证图标放在页脚。

Validated Footer Example

我还希望能够完全独立于页面内容,修改页眉和页脚的内容。

Using the Code

页眉和页脚文本保存在网站目录树中的文本文件中。我的网站的相关目录结构是:

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

请注意,我的网站托管在 Microsoft Office Live (MSOL) 上。MSOL 希望网页是 ASPX 或 HTML 页面。在本文中,我将使用母版页可下载源代码中的 HTML 页面。但是,如果读者使用 Visual Studio 构建网站页面,请选择 ASPX 页面。一旦定义了新页面,您可以选择删除 Visual Studio 自动生成的 *aspx.cs* 和 *designer.cs* 页面。您也可以将网页扩展名从 *.aspx* 更改为 *.html*(在以下对话框中选择“是”)。

Rename Dialog Box

请注意,`

` 标签的 `action` 属性的 target 不能是 `.html` 扩展名。这就是为什么母版页可下载源代码中的 ContactMeSuccessful 网页的扩展名为 `.aspx`。

页眉包含在 `header_contents.txt` 文件中,由 `add_header` 脚本访问;页脚包含在 `footer_contents.txt` 文件中,由 `add_footer` 脚本访问。在文档加载时,这两个 JavaScript 脚本将被执行:

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

在前面的示例中,使用了伪参数。在实际页面中,它们将被实际参数替换。例如,在我的网站 *Default.html* 页面中,我使用:

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

为了使 `add_header` 脚本成功执行,必须传递四个参数,并且网页必须包含:

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

这个“header”`

` 标签被放置在页眉需要出现的位置(通常紧跟在 `` 标签之后)。

为了使 `add_footer` 脚本成功执行,网页必须包含:

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

“footer”`

` 标签被放置在页脚需要出现的位置(通常在所需的 `` 块放在页脚 `
` 之后,但在 `
© . All rights reserved.
` 标签之前。我为 `` 块是:

<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>

`add_header` 脚本和 header contents 文件中的内容取决于页眉的内容。我之前提到过我希望我的页眉包含什么。为此,我需要向 `add_header` 传递四个参数。`add_header` 脚本的修订版本现在是:

// ******************************************************* 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 );
          }
        }
      }  
    }
}

请注意,应读者建议,四个可替换字段(即 `SiteLogoTarget`、`SiteLogo`、`PageHeader` 和 `PageSubHeader`)现在都包含在 `{{` 和 `}}` 分隔符中,从而消除了先前在替换操作中出现的歧义。

如果未提供四个参数,脚本将退出。将创建一个文档标题。如果找不到 ID 为“header”的 `

`,脚本将退出。然后读取 header_contents 文本文件的内容。如果文件读取成功,将用新值替换各种子字符串。最后,修订后的页眉内容将替换页眉 `
` 的 `outerHTML`。对于页眉,我使用包含以下内容的页眉内容文件:

<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>

`add_footer` 脚本和 footer contents 文件中的内容取决于页脚的内容。我之前提到过我希望我的页脚包含什么。为此,我需要向 `add_footer` 传递一个可选参数。`add_footer` 脚本的修订版本现在是:

// ******************************************************* 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 );
          }
        }
    }  
}

请注意,这里也应读者建议,两个可替换字段(即 `FooterImage` 和 `DisplayFooterImage`)现在都包含在 `{{` 和 `}}` 分隔符中,从而消除了先前在替换操作中出现的歧义。

如果找不到 ID 为“footer”的 `

`,脚本将退出。然后读取 footer_contents 文本文件的内容。如果文件读取成功,将用新值替换各种子字符串。最后,修订后的页脚内容将替换 `
` 的 `outerHTML`。对于页脚,我使用包含以下内容的页脚内容文件:

<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>

`add_footer` 和 `add_header` 脚本分别通过 IO 脚本访问 footer_contents 和 header_contents 文件。

// *************************************************************** 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 );
}

JavaScript IO 函数是这个“母版页”范例起作用的原因。如果没有它,将无法读取网页的页眉和页脚文本。

最后,应该提及 `place_in_outerHTML` 脚本。

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

function place_in_outerHTML ( element, 
                            contents )
{

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

Firefox 不支持 `outerHTML` 作为元素属性(目前仅在 Internet Explorer 中可用)。为了将浏览器差异排除在 `add_header` 和 `add_footer` 之外,我调用了 `place_in_outerHTML`。这个脚本并不完全是我想要的,但它确实有效。通过将 JavaScript 放在一个单独的文件中,我可以重新访问代码,并可能进行更改,而不会干扰网站的其余部分。

网页模板

每当我构建一个新网页时,我都使用以下模板作为起点:

<!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>

关注点

最重要的是,本文介绍的页眉、页脚和访问函数是我在我网站上使用的;它们应该根据您的需求进行修改。

我鼓励您使用网页模板(或类似的模板)。我发现它为我节省了很多构建新页面的时间。

我使用 `

`s 来组织我的网页(其他人可能会主张使用 `
`s 来实现此目的)。但要获得百分比布局三栏文档的效果,我发现 `
`s 比 `
`s 工作得更好。当然,也许我对 `
`s 的使用不够精通,无法让它们正常工作。

最后,我强烈建议您首先在网页模板中,分别在 header 和 footer `

`s 的位置,构建页眉和页脚的内容。当模板看起来是您想要的样式后,再将页眉和页脚内容移至相应的文本文件中,并重新插入 header 和 footer `
`s。

参考文献

已成功测试的浏览器

Internet ExplorerFirefoxNO Google ChromeOperaSafari

历史

  • 2010/06/10 - 修改了 *IO.js*、*add_header.js* 和 *add_footer.js* 以提高安全性。
  • 2010/11/15 - 修改了文章以回应读者的评论并纠正了排版和逻辑错误;确保该范例能在所有常用浏览器上运行。
© . All rights reserved.