使用JQUERY Tabs插件宿主IFRAMES


Posted in Javascript onJanuary 01, 2010

必备的东西:

Windows XP/Vista/7/2003/2008
Visual Studio 2005 or 2008 (download the correct version of Home Site project above)
.NET Framework 2.0 and ASP.NET AJAX 1.0
今天,很多浏览器提供了使用tab的能力去浏览更多的网页和网站。当然这是一个非常有用的功能来替代同时打开几个浏览器,但如果提供在一个页面中浏览多个网页也非常的不错。

例如,如果你的主页是由很多不同的有用的Web工具或者站点组成,一个tab页面将可能非常的有用。使用框架集,IFRAME等等,都是宿主外部内容的典型方式。这些方法允许你在单个页面上宿主多个网页。 但是使他们能正确的布局却非常不容易。 更不用说去处理页面和IFRAME的scrollbars等问题。

这篇文章中,尝试去宿主外部数据,提供了一个基本的解决方案,利用ASP.NET,AJAX和javascript 去处理一些遇到的麻烦。

计划

主旨是提供一个简单的宿主外部数据的方案,这个解决方案有下面这些简单的需求。

1、提供一个tab界面,方便浏览。

2、提供一个配置方法来添加tab

3、使每个tab页都能宿主配置的页面

基本的技术需要是:

1、仅当tab被选中的时候,加载外部的数据内容

2、保证纵向的scrollbars的设置成显示,而且仅当需要处理的数据溢出的时候,才显示scrollbars 。

3、保证该解决方案是能跨浏览器工作

解决方案的名字和主页面的名字都是 Home Site

分析

对于这个解决方案,我决定使用JQuery UI Tabs 来实现表格式的导航功能。我以前也使用过商业的开源的tab控件。但是JQuery UI Tabs 是轻量级的,实现非常地简单,而且是免费的。

除了JQuery 和tab控件以及.net提供的功能,不需要其它的控件。 VS2005 将足以结合整个项目的开发环境,选择C#作为开发语言。

我将使用一个IFRAME的宿主网站内容,由于跨站点(又名跨域)的安全限制,使用JQuery UI Tabs去宿主外部网页将无法直接工作。

设计

这里有一个为客户提供视觉上的最低限度的需求:
使用JQUERY Tabs插件宿主IFRAMES
该解决方案,将需要三种不同的功能模块:
1、配置模块
2、使用JQuery UI Tabs 插件的tab界面
3、使用IFRAME元素宿主网页内容办法。
配置模块:
一个需求的功能是是使tab可配置。 我选择最低限度,将tab的配置信息放到一个xml文件之中。虽然我可以更进一步的深入,使tab能的动态增加和删除,我决定在本文的第二篇中提供此功能。
XML文件的格式如下:
代码

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
<tab id="TabOne" displayName="Tab One" path="www.msn.com" /> 
<tab id="TabTwo" displayName="Tab Two" path="www.amazon.com" /> 
</configuration>

参数描述:
id = tab的唯一ID。这个ID不能包含空格
displayName =显示在tab头部的名字
path = 带查询参数的URL,"http://"是可选的。
配置文件的名字是:TabConfig.xml。现在必须手动添加或删除tab来更新解决方案的配置文件。
内容加载器:
可以说,没有内容加载模块是需要用IFRAME去为tab页面设置内部的列表项。但是如果IFRAME在一个通过使用锚作为每个tab列表项元素的子元素的独立的宿主网页中,我觉得在功能和测试方面没有比IFRAME的更好的控件了:
使用JQUERY Tabs插件宿主IFRAMES
如果你愿意,将内容装载器做成一个通用的模块,它接受查询字符串参数,以便正确设置IFRAME元素;参数为元素的唯一的ID,和源属性值,也就是加载该网页的URL。

另一个内容加载器的设计要求是,必须使该IFRAME负载整个页面(scrolling设置为auto)。此外,该网页body必须隐藏溢出(通过设置样式属性),以避免重复滚动条。特别是当改变浏览器的大小时。最后,滚动条的处理必须能跨浏览器。

tab界面

tab界面代码非常的简单,可以从 JQuery UI Tabs documentation说明文档中得到详细的演示代码。JQuery UI Tabs说明文档中的和我们JQuery UI Tabs具体的实现不同的地方在于:每个tab项锚的href指向内容加载页面,随后加载所需的网页到IFRAME里面。

一些额外的东西

上面的标签,我认为它很方便去用一个div来显示头,logo,甚至一些链接或菜单项。一个更好的需求,我想要头部能够折叠,能使每个标签宿主的页面能有一个最大的视觉效果。

最终的设计布局如下:
使用JQUERY Tabs插件宿主IFRAMES
代码/开发
我们从内容加载器开始,下面是标记:
代码

<%@ Page Language="C#" AutoEventWireup="true" 
CodeBehind="ContentLoader.aspx.cs" Inherits="HomeSite.ContentLoader" %> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head runat="server"> 
<title>ContentLoader</title> 
<style type="text/css"> 
.contentsBody { margin:0; overflow:hidden; } 
.contentsTable { width:100%; height:92%; valign:top; 
cellspacing:0; cellpadding:0; border:0 } 
.contentsIframe { height:100%; width:100%; marginwidth:0; 
marginheight:0; scrolling:auto } 
</style> 
</head> 
<body class="contentsBody"> 
<table class="contentsTable"> 
<tr> 
<td> 
<asp:Literal ID="Literal1" 
runat="server"></asp:Literal> 
</td> 
</tr> 
</table> 
</body> 
</html>

真正神奇的地方是css代码,我设置body 的margin 为0,设置overflow 为hidden。防止scrollbars 出现在页面的body上。
IFRAME的scrolling设置为auto,因此,如果需要滚动条,也只有IFRAME提供给它。因为在IFRAME的周围有大量不好看的空白空间,将margins 也设置为0,IFRAME的height和width被设置成100%,来确保网页内容占尽可能多的空间。请注意html标签中使用了Literal控件。正如你将看到以下的隐藏代码, 使用Literal的目的是允许后台代码注入东西到IFRAME元素中,它能构建了正确的querystring的ID和Path参数。
代码
using System; 
using System.Data; 
using System.Configuration; 
using System.Collections; 
using System.Web; 
using System.Web.Security; 
using System.Web.UI; 
using System.Web.UI.WebControls; 
using System.Web.UI.WebControls.WebParts; 
using System.Web.UI.HtmlControls; 
namespace HomeSite 
{ 
/// <summary> 
/// Content Loader code behind class 
/// </summary> 
public partial class ContentLoader : System.Web.UI.Page 
{ 
/// <summary> 
/// On Page Load we need to capture query string parameters, construct 
/// an IFRAME element, and inject the IFRAME element into our Literal control 
/// </summary> 
/// <param name="sender"></param> 
/// <param name="e"></param> 
protected void Page_Load(object sender, EventArgs e) 
{ 
string id = ""; 
string path = ""; 
// Validate we have valid querystring parameters 
// namely "ID" and "Path" 
if (HasValue(Request["ID"]) && 
HasValue(Request["Path"])) 
{ 
// Set our local variables 
id = Request["ID"].Trim().ToString(); 
path = Request["Path"].Trim().ToString(); 
// Prepend the path URL with http:// if needed 
if (!path.ToLowerInvariant().StartsWith("http://")) 
path = "http://" + path; 
// Construct the IFRAME element and set the Text value of the Literal control 
Literal1.Text = "<iframe class=\"contentsIframe\" " + 
"id=\"contentFrame" + id + "\" " + 
"frameborder=\"0\" src=\"" + path + 
"\"></iframe>"; 
} 
else 
{ 
// Either query parameter or both are not set or do not 
// exist (not passed as request parameters) 
Literal1.Text = "<span id=\"contentFrame\">An " + 
"error occurred while attempting to load a web page.</span>"; 
} 
} 
/// <summary> 
/// Simple static class used to validate the value of querystring 
/// parameter is not null or an empty string 
/// </summary> 
/// <param name="o">The object to check</param> 
/// <returns>Returns true if the object (string) 
/// has a value; false otherwise.</returns> 
public static bool HasValue(object o) 
{ 
if (o == null) 
return false; 
if (o is String) 
{ 
if (((String) o).Trim() == String.Empty) 
return false; 
} 
return true; 
} 
} 
}

只要你将querystring的ID和Path参数传递给它,装载的页面能够单独的运行。通过VS2005浏览网页,有URL的示例:http://localhost:49573/ContentLoader.aspx?ID=1234&Path=www.amazon.com.
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
using System.Xml;
using System.Text;
namespace HomeSite
{
    /// <summary>
    /// Tab configuration static handling class
    /// </summary>
    public static class TabConfiguration
    {
        /// <summary>
        /// This class returns a collection of TabDefinition classes created from
        /// parsing the tab definitions defined in the TabConfig.xml file.
        /// </summary>
        /// <param name"page">The Page reference
        ///         calling this class</param>
        /// <returns>ArrayList of TabDefinition classes</returns>
        public static ArrayList LoadConfiguration(Page page)
        {
            // Local container for tab definitions
            ArrayList tabList = new ArrayList();
            try
            {
                // Read the contents of the TabConfig.xml file
                StreamReader reader = new StreamReader(new FileStream(
                   page.MapPath("./TabConfig.xml"), 
                   FileMode.Open, FileAccess.Read));
                string xmlContent = reader.ReadToEnd();
                reader.Close();
                reader.Dispose();
                // Create an XML document and load the tab configuration file contents
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(xmlContent);
                // Iterate through each tab definition, create a TabDefinition class,
                // and add the TabDefinition to the local ArrayList container
                foreach (XmlNode node in xmlDoc.SelectNodes("/configuration/tab"))
                {
                    TabDefinition tab = new TabDefinition();
                    tab.ID = node.Attributes["id"].Value;
                    tab.DisplayName = node.Attributes["displayName"].Value;
                    tab.Path = node.Attributes["path"].Value;
                    tabList.Add(tab);
                }
            }
            catch
            {
                // Do nothing
            }
            // Return the tab definition
            return tabList;
        }
    }
    /// <summary>
    /// This class serves as the container for a tab definition
    /// </summary>
    public class TabDefinition
    {
        /// <summary>
        /// Member variable for the Unique ID for the tab
        /// </summary>
        private string _id;
        /// <summary>
        /// Member variable for the displayed name of the tab
        /// </summary>
        private string _displayName;
        /// <summary>
        /// Member variable for the web page URL to host in the tab (IFRAME)
        /// </summary>
        private string _path;
        /// <summary>
        /// Property for the Unique ID for the tab
        /// </summary>
        public string ID
        {
            get { return _id; }
            set { _id = value; }
        }
        /// <summary>
        /// Property for the displayed name of the tab
        /// </summary>
        public string DisplayName
        {
            get { return _displayName; }
            set { _displayName = value; }
        }
        /// <summary>
        /// Property for the web page URL to host in the tab (IFRAME)
        /// </summary>
        public string Path
        {
            get { return _path; }
            set { _path = value; }
        }
    }
}

    请注意页面实例必须提供LoadConfiguration方法来正确引入TabConfig.xml的位置。我本可以使用XmlTextReader,但选择使用StreamReader读取整个配置文件的内容和使用XmlDocument对象解析tab的配置信息。因为我觉得快速转储整个配置文件比通过流程解析打开配置文件要好很多。使用XmlTextReader正属于这种情况。

    现在,让我们看看Home Site 的主页的标记

代码

<%@ Page Language="C#" AutoEventWireup="true" 
  CodeBehind="Default.aspx.cs" Inherits="HomeSite._Default" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Home Site</title>
    <link href="css/jquery-ui-1.7.2.custom.css" 
          type="text/css" rel="stylesheet" />
    <link href="css/Main.css" 
          type="text/css" rel="stylesheet" />
    <script src="JavaScript/jquery-1.3.2.min.js" 
          type="text/javascript"></script>
    <script src="Javascript/jquery-ui-1.7.2.custom.min.js" 
          type="text/javascript"></script>
    <script src="Javascript/jquery.hijack.min.js" 
          type="text/javascript"></script>
    <script type="text/javascript">
        // JQuery scripting
        $(document).ready(function()
        {
            var browser = navigator.appName;
            var heightAdjust = 23;
            var widthAdjust = 7;
            // Make height and width offset adjusts for non-IE browsers 
            if (browser != "Microsoft Internet Explorer")
            {
                heightAdjust = 18;
                widthAdjust = 9;
            }
            // Show the panelList UL element so we can setup the tabs
            // Please note this approach eliminates Flash of Unstyled Content (FOUC)
            $('#panelList').show();
            // Setup the jQuery UI tabs
            $('#tabPage').tabs({
                cache: true, // This ensures selecting a tab does not refresh the page
                load: function(event, ui)
                {
                    // Keep links, form submissions, etc. contained within the tab
                    $(ui.panel).hijack();
                    // Adjust the IFRAME size correctly in the browser window
                    $('.contentsIframe').width((ViewPortWidth() - widthAdjust));
                    $('.contentsIframe').height((ViewPortHeight() - 
                       $('.menuRow').height() - $('.tabs').height() - heightAdjust));
                }
            });
            // Toggle arrow button image and hide/show menu area
            $('#collapseArrow').click(function()
            {
                if ($(this).hasClass('ui-icon-circle-triangle-s'))
                {
                    $(this).removeClass('ui-icon-circle-triangle-s');
                    $(this).addClass('ui-icon-circle-triangle-n');
                    $('#menuDiv').show();
                }
                else
                {
                    $(this).removeClass('ui-icon-circle-triangle-n');
                    $(this).addClass('ui-icon-circle-triangle-s');
                    $('#menuDiv').hide();
                }
            // Adjust the IFRAME size correctly in the browser window
            $('.contentsIframe').width((ViewPortWidth() - widthAdjust));
            $('.contentsIframe').height((ViewPortHeight() - 
              $('.menuRow').height() - $('.tabs').height() - heightAdjust));
        });
        // Adjust tab header width and visible iframe window
        // height and width after the window is resized
        $(window).resize(function(){
        $('.contentsIframe').width((ViewPortWidth() - widthAdjust));
        $('.contentsIframe').height((ViewPortHeight() - 
          $('.menuRow').height() - $('.tabs').height() - heightAdjust));
        $('.ui-widget-header').width(ViewPortWidth() - widthAdjust);
        });
        // Adjust tab header height and width according to the IE client viewing area
        $('.ui-widget-header').width(ViewPortWidth() - widthAdjust);
        // Adjust the IFRAME height correctly in the browser window
        $('.contentsIframe').height((ViewPortHeight() - 
          $('.menuRow').height() - $('.tabs').height() - heightAdjust));
        });
        // Returns width of viewable area in the browser
        function ViewPortWidth()
        {
            var width = 0;
            if ((document.documentElement) && 
                (document.documentElement.clientWidth))
            {
                width = document.documentElement.clientWidth;
            }
            else if ((document.body) && (document.body.clientWidth))
            {
                width = document.body.clientWidth;
            }
            else if (window.innerWidth)
            {
                width = window.innerWidth;
            }
            return width;
        }
        // Returns height of viewable area in the browser
        function ViewPortHeight()
        {
            var height = 0;
            if (window.innerHeight)
            {
                height = window.innerHeight;
            }
                else if ((document.documentElement) && 
                         (document.documentElement.clientHeight))
            {
                height = document.documentElement.clientHeight;
            }
            return height;
        }
    </script>
</head>
<body class="mainBody" style="margin:0">
    <form id="form1" runat="server">
        <asp:ScriptManager id="ScriptManager1" runat="server" />
        <div>
            <table id="mainTable" cellpadding="0" cellspacing="0">
                <tr class="menuRow">
                    <td align="left" valign="top">
                        <span id="collapseArrow" 
                           title="Show/Hide Header" 
                           class="menuSpan ui-icon ui-icon-circle-triangle-n"></span>
                        <div id="menuDiv" 
                          class="menuDiv">This is the header area.
                                  <br /><i>Please customize this area as you set 
                                  fit; i.e. add a logo, menu options, links, 
                                  etc.</i><br /><br /></div>
                    </td>
                </tr>
                <tr>
                    <td class="tabPageCell" colspan="2" 
                             valign="top" align="left">
                        <div id="tabPage" class="contents">
                            <ul id="panelList" 
                                class="tabs" runat="server" />
                        </div>
                    </td>
                </tr>
            </table>
        </div>
    </form>
</body>
</html>

     这些代码标记非常的繁琐,我用了很多内部的注释来解释他们。请注意出现在标头区的左上角的箭头按钮,其实就是来自我选择的JQuery的主题中的图像文件,使用ui-icon 和ui-icon-circle-triangle-n causes 设置collapseArrow span ,造成JQuery 显示一个名字为ui-icon-circle-triangle-n的ico的图像。在文档头部的脚本区中,我创建一个函数,当我们单击它的时候,改变向上箭头图标为向下箭头图标,此外,同样的单击事件处理程序将显示或隐藏标头部域的div(menuDiv)。

Home Site 页面的隐藏代码如下:

代码

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace HomeSite
{
    /// <summary>
    /// Home Site (default) web page code behind class
    /// </summary>
    public partial class _Default : System.Web.UI.Page
    {
        /// <summary>
        /// On page load we need to create the tab
        /// list items for tab interface construction
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                AddTabsToForm();
        }
        /// <summary>
        /// This method calls to our business logic
        /// class to read the tab configuration file,
        /// which will return an ArrayList of TabDefinition
        /// classes. This method iterates
        /// through the ArrayList building HTML controls to add to the tab panel.
        /// </summary>
        protected void AddTabsToForm()
        {
            foreach (TabDefinition tab in TabConfiguration.LoadConfiguration(this.Page))
            {
                HtmlGenericControl tabListItem = new HtmlGenericControl();
                tabListItem.TagName = "li";
                tabListItem.InnerHtml = "<a title=\"" + 
                  tab.DisplayName + "\" href=\"ContentLoader.aspx?ID=" + 
                  tab.ID + "&Path=" + tab.Path + 
                  "\">" + tab.DisplayName + "</a>";
                panelList.Controls.Add(tabListItem);
            }
        }
    }
}

隐藏代码不需要做过多的解释,关键的动作是创建和设置HtmlGenericControl列表项,最后它通过编程被添加tab panel中。

遇到的问题

我遇到的主要的问题是跨浏览器的情况下自动适应IFRAME的大小,该方案在IE 8,Firefox v3.5.6,和谷歌v3.0.195.38浏览器进行测试。

我必须进行浏览器检测,根据IFRAME在三个浏览器测试的尺寸,调整相应的宽度和高度。当浏览器改变大小的时候,Chrome 和FireFox看起来IFRAME有个固定的高度。 但是, IE8看来来会丢失在IFRAME和浏览器顶部之间的padding。调整宽度和高度特别是IE似乎应该尽可能的减少IFRAME到IE浏览器窗口的底部“蜷缩”影响。

限制

1、以下JavaScript将使你加载的网页跳出IFRAME,我不知道任何解决办法此(如果存在)。Code Project 网站上目前拥有类似下面的代码,这样配置选项指向http://www.codeproject.com/非常的容易,这里重现描述的动作。

<script type="text/javascript" language="javascript">
    if (top!=self) top.location.href = location.href;
</script>
2、在浏览器中,Web网页被迫改变页面本身的大小,有可能跳出IFRAME窗体,从而取代了前父窗口。

3、我没有使用Safari,Opera,早期版本的IE浏览器,或任何其他浏览器的早期版本测试的此解决方案,所以要在Home Site中适当地调整heightAdjust和widthAdjust变量,适应没有测试的IE浏览器或低于IE8浏览器的版本。

总结和兴趣点
虽然这种解决方案不是很复杂,通过一个标签界面宿主外部网站内容。这是我所见过的许多网络论坛和博客要求的功能。请注意:您也可以配置标签可显示自己相关的域名或网站(在同一台服务器)。

Javascript 相关文章推荐
Jquey拖拽控件Draggable使用方法(asp.net环境)
Sep 28 Javascript
jQuery LigerUI 插件介绍及使用之ligerDrag和ligerResizable示例代码打包
Apr 06 Javascript
如何用ajax来创建一个XMLHttpRequest对象
Dec 10 Javascript
js 数组操作之pop,push,unshift,splice,shift
Jan 29 Javascript
javascript搜索框效果实现方法
May 14 Javascript
javascript动态添加删除tabs标签的方法
Jul 06 Javascript
jquery获取多个checkbox的值异步提交给php
Jul 07 Javascript
jQuery ajax时间差导致的变量赋值问题分析
Jan 22 Javascript
JavaScript表单焦点自动切换代码
Jul 24 Javascript
vue省市区三联动下拉选择组件的实现
Apr 28 Javascript
webpack vue 项目打包生成的文件,资源文件报404问题的修复方法(总结篇)
Jan 09 Javascript
微信公众号开发之微信支付代码记录的实现
Oct 16 Javascript
用jquery实现学校的校历(asp.net+jquery ui 1.72)
Jan 01 #Javascript
url 特殊字符 传递参数解决方法
Jan 01 #Javascript
JavaScript 数组循环引起的思考
Jan 01 #Javascript
javascript eval和JSON之间的联系
Dec 31 #Javascript
js下用gb2312编码解码实现方法
Dec 31 #Javascript
JavaScript 学习笔记(七)字符串的连接
Dec 31 #Javascript
JavaScript 学习笔记(六)
Dec 31 #Javascript
You might like
php环境配置 php5 mysql5 apache2 phpmyadmin安装与配置
2006/11/17 PHP
PHP 图片上传代码
2011/09/13 PHP
分享8个最佳的代码片段在线测试网站
2013/06/29 PHP
smarty内置函数section的用法
2015/01/22 PHP
PHP中session跨子域的三种实现方法
2016/07/25 PHP
php实现留言板功能(代码详解)
2017/03/28 PHP
CentOS7.0下安装PHP5.6.30服务的教程详解
2018/09/29 PHP
PHP单例模式数据库连接类与页面静态化实现方法
2019/03/20 PHP
jquery中实现简单的tabs插件功能的代码
2011/03/02 Javascript
一个js过滤空格的小函数
2014/10/10 Javascript
javascript设计模式之module(模块)模式
2016/08/19 Javascript
JQueryEasyUI之DataGrid数据显示
2016/11/23 Javascript
基于JQuery实现的跑马灯效果(文字无缝向上翻动)
2016/12/02 Javascript
小程序开发实战:实现九宫格界面的导航的代码实现
2017/01/19 Javascript
浅谈angularjs中响应回车事件
2017/04/24 Javascript
jquery实现楼层滚动效果
2018/01/01 jQuery
TypeScript的安装、使用、自动编译的实现
2020/04/10 Javascript
vue路由权限校验功能的实现代码
2020/06/07 Javascript
javascript递归函数定义和用法示例分析
2020/07/22 Javascript
javascript读取本地文件和目录方法详解
2020/08/06 Javascript
Python单链表的简单实现方法
2014/09/23 Python
python的random模块及加权随机算法的python实现方法
2017/01/04 Python
Python面向对象特殊成员
2017/04/24 Python
Python时间戳使用和相互转换详解
2017/12/11 Python
Django中自定义查询对象的具体使用
2019/10/13 Python
浅析使用Python搭建http服务器
2019/10/27 Python
俄罗斯品牌服装和鞋子的在线商店:KUPIVIP
2019/10/27 全球购物
人事主管岗位职责
2014/01/30 职场文书
家具促销活动方案
2014/02/16 职场文书
青春演讲稿范文
2014/05/08 职场文书
银行青年文明号事迹材料
2014/05/31 职场文书
优质服务口号
2014/06/11 职场文书
关于诚信的活动方案
2014/08/18 职场文书
工作期间打牌检讨书范文
2014/11/20 职场文书
给客户的感谢信
2015/01/21 职场文书
NodeJs使用webpack打包项目的方法详解
2022/02/28 NodeJs