Introduction
This article introduces a method to dynamically generate tabs to host "IFrames" using "jQuery" and "Json" in "ASP.NET MVC".
Background
"IFrames" have been used in the "Web Development" for a long time to improve the responsiveness of the web sites and to improve the user experiences. "IFrames" can also provide some programming advantages by separating the application program into logically related blocks. In some cases, it should be ideal to organize the "IFrames" in "tabs". The "jQuery" provided us a very nice way to create "tabs". This article will use an example to introduce a method to dynamically generate tabs to host "IFrames" using "jQuery" and "Json" in "ASP.NET MVC".
This article is inspired by Matt Esterak's article "Hosting IFRAMEs using the JQuery UI Tabs plug-in". This article takes advantage of some new features and the "Json" support from the "ASP.NET MVC" to generate tabs that host the "IFrames" dynamically. The information for each tab is configured in the "MVC controller" and loaded to the client by "jQuery" in "Json" format.
The example project is developed in "Visual Studio 2010" and "MVC 2". The "jQuery" version used in this example is "1.4.2". The language used for the server side operations is "C#". The following is a screen shot of the Visual Studio "Solution Explorer":
The "ASP.NET MVC 2" project "DynamicJsonIFrame
" is a simple web project. It has the following major components:
- In the "Models" folder, the project has a model class "
TabInfo
". - In the "Controllers" folder, the project has a "Controller" named "
HomeController
". - Corresponding to the "
HomeController
", there are three "Views", namely "Index.aspx", "IFrameContainer.aspx", and "IFrameWebLoader.aspx". The "Index.aspx" is the main application web page. The "IFrameContainer.aspx" and "IFrameWebLoader.aspx" are used to create and load the "IFrames". - The "Index.aspx" uses a "Master Page" named "
Site.Master
".
Besides the standard "jQuery" library "jquery-1.4.2.min.js", the example application also needs the "jQuery Plug-in" "jquery-ui-1.8.5.custom.min.js" and the "Stylesheet" file "jquery-ui-1.8.5.custom.css". You can download these files from here. This article assumes the readers having some basic knowledge on "ASP.NET MVC", "jQuery", and "Json". If you are new to these subjects, you should be able to easily find reference materials on the web.
Let us first take a look at the model class "TabInfo
".
The Model Class "TabInfo"
The only model class in the project is the class "TabInfo
" implemented in the "TabInfo.cs":
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DynamicJsonIFrame.Models
{
public class TabInfo
{
public int TabID { get; set; }
public string TabText { get; set; }
public string TabInformationText { get; set; }
public string TabUrl { get; set; }
}
}
The public "Properties" are used for keeping the information for each tab item. The "TabText
" is the text displayed on the tab item. The "TabInformationText
" is used for the "Tooltip" for the tab item in the example project. The "TabUrl
" is the "URL" of the web page to be loaded when the tab item is activated.
The Controller "HomeController"
The "HomeController
" is implemented as the following:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using DynamicJsonIFrame.Models;
namespace DynamicJsonIFrame.Controllers
{
[HandleError]
public class HomeController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index(){ return View(); }
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult IFrameContainer(string WebURL,
string InformationText)
{
ViewData["WebURL"] = WebURL;
ViewData["InformationText"] = InformationText;
return View();
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult IFrameWebLoader(string WebURL)
{
ViewData["WebURL"] = WebURL;
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GetTabs()
{
List<TabInfo> tabList = new List<TabInfo>();
tabList.Add(new TabInfo
{
TabID = 1,
TabText = "Yahoo",
TabInformationText = "",
TabUrl = "http://www.yahoo.com"
});
tabList.Add(new TabInfo
{
TabID = 2,
TabText = "Hall of fame",
TabInformationText
= "Michael Jordan's hall of fame acceptance speech",
TabUrl = "http://www.youtube.com/watch?v=owbYN3XstVQ"
});
tabList.Add(new TabInfo
{
TabID = 2,
TabText = "Soccer Game",
TabInformationText = "The greatest soccer goal",
TabUrl = "http://www.youtube.com/watch?v=MsAgu45qqWE"
});
tabList.Add(new TabInfo
{
TabID = 2,
TabText = "Cool",
TabInformationText = "Soccer and soccer !",
TabUrl = "http://www.youtube.com/watch?v=X5SwdvUWx0E"
});
return Json(tabList);
}
}
}
In this "Controller", there are four "Action Methods". The first three "Action Methods", namely "Index
", "IFrameContainer
", and "IFrameWebLoader
" are standard "Action Methods". Each of the three "Action Methods" simply returns an "ActionResult" that leads the program flow to the corresponding "View" in the "Views\Home" folder. The last "Action Method" "GetTabs
" does not have a corresponding "View". It returns a "JsonResult". I hard-coded a few "TabInfo
" objects into a "List". In a real world web application, this "List" can be dynamically created from any configuration sources, such as a database. This "Action Method" will be accessed from a "jQuery" client by an "Ajax" call in the "Index.aspx".
The "Index.aspx" View and the "Site.Master"
Before going to the "Index.aspx", let us first take a look at the "Site.Master
":
<%@ Master Language="C#"
Inherits="System.Web.Mvc.ViewMasterPage" %>
<!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 runat="server">
<title>
<asp:ContentPlaceHolder ID="TitleContent" runat="server" />
</title>
<link rel="SHORTCUT ICON"
href="<%= Url.Content("~/Images/pig_mac_archigraphs.ico") %>" />
<link rel="stylesheet" type="text/css"
href="<%= Url.Content("~/Styles/AppStyle.css") %>" />
<link rel="stylesheet" type="text/css"
href="<%= Url.Content("~/Styles/jquery-ui-1.8.5.custom.css") %>" />
<script type="text/javascript"
src="<%= Url.Content("~/Scripts/jquery-1.4.2.min.js") %>">
</script>
<script type="text/javascript"
src="<%= Url.Content("~/Scripts/jquery-ui-1.8.5.custom.min.js") %>">
</script>
</head>
<body>
<div>
<div>
<div id="Header">
<h1>Dynamic Creation of Tabs for IFrames using
jQuery and Json in MVC</h1>
<label class="Author">Developed By Song Li
- 9/23/2010</label>
</div>
</div>
<div>
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
<div id="Footer"><h1>The Code Project Open License (CPOL)
2010 - 2020®</h1></div>
</div>
</body>
</html>
Besides showing the header and footer of the web application, the "Site.Master
" has a "ContentPlaceHolder" named "MainContent
". The "Index.aspx" will be using this "ContentPlaceHolder" to add HTML and JavaScript contents to the web application. The "Index.aspx" is implemented as the following:
<%@ Page Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="TitleContent"
ContentPlaceHolderID="TitleContent" runat="server">
IFrame host with dynamically generated jQuery Tabs
</asp:Content>
<asp:Content ID="MainContent"
ContentPlaceHolderID="MainContent" runat="server">
<div id="MainTabs" class="FluidContent">
<div id="divTabs" class="iframeMax"></div>
</div>
<script type="text/javascript">
var GetTabsUrl = '<%: Url.Action("GetTabs", "Home") %>';
var IFrameContainerUrl = '<%: Url.Action("IFrameContainer", "Home") %>';
$(document).ready(function () {
$.ajax({
cache: false,
type: "POST",
url: GetTabsUrl,
dataType: "json",
success: function (data) {
var GenerateTabs = function () {
var tabui = document.createElement("ul");
$.each(data, function () {
var li = document.createElement("li");
var a = document.createElement("a");
a.href = IFrameContainerUrl + "?WebURL="
+ escape(this.TabUrl)
+ "&InformationText="
+ escape(this.TabInformationText);
var span = document.createElement("span");
$(span).attr("title", this.TabInformationText);
var tx = document.createTextNode(this.TabText);
span.appendChild(tx)
a.appendChild(span);
li.appendChild(a);
tabui.appendChild(li);
});
$("#divTabs").html("").append(tabui);
var $tabs = $("#divTabs").tabs({
cache: true,
selected: -1,
select: function (event, ui) {
return true;
}
}).ajaxComplete(function (event, request, settings) {
return;
});
if (data.length > 0) {
$tabs.tabs('select', 0);
}
};
GenerateTabs();
},
error: function (xhr) {
var status = xhr.status;
var responseText = xhr.responseText;
alert("Error occurred when load the information for the tabs");
}
});
});
</script>
</asp:Content>
The "Index.aspx" is the main place where the dynamic tabs are created. The "Div" with id "divTabs
" is the HTML place holder for the dynamic tabs and it is the only thing required for the magic. The creation of the tabs happens in the "$.ajax" call in the JavaScript section:
- It first makes an "Ajax" call to the "Action Method" "
GetTabs
" in the "HomeController
". - If the call is successful, the "List" of the "
TabInfo
" objects will be passed into the "anonymous function" corresponding to the "success
" parameter of the "$.ajax" call as an array of "Json" objects. - The "
GenerateTabs
" function will be using the information in each "Json" object to build the HTML UI elements for the "jQuery" tabs and append them to the "Div" with id "divTabs
". If you want to know the detail of the HTML UI elements needed by the "jQuery" tabs, you can take a look at this link. - After the UI elements are built and added into the "Dom", the "jQuery" call "
$("#divTabs").tabs()
" will create the dynamic tabs, and "$tabs.tabs('select', 0)
" statement will activate the first tab.
One thing that you may need to pay some attention to here is that the "URL" passed to each tab item is not the true "TabUrl
" coming from the "Json" object. The content "URL" for the tab item to load is the "IFrameContainer.aspx" file.
Let us then take a look at the "IFrameContainer.aspx".
The "IFrameContainer.aspx" and "IFrameWebLoader.aspx" Views
The "IFrameContainer.aspx" is implemented as the following:
<%@ Page Language="C#"
Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<html>
<head runat="server">
<title></title>
</head>
<body>
<iframe class="iframeInner"
frameborder="0" scrolling="auto"
src="<%= Url.Action("IFrameWebLoader", "Home",
new {WebURL = ViewData["WebURL"]}) %>" />
</body>
</html>
The "IFrameContainer.aspx" file is where the "Iframe" tag is placed. What happens with the "jQuery" tabs is that when the users click a tab item, an "Ajax" call is made to retrieve the content of the "IFrameContainer.aspx" page and the content is inserted into the "Index.aspx" page. If you set the "src" attribute of the "Iframe" to the "TabUrl
", the desired web page specified in the "Json" object will be loaded. In this example, I did not directly set the "src" attribute of the "Iframe" to the "TabUrl
". I set it to the "IFrameWebLoader.aspx", which is implemented as the following:
<%@ Page Language="C#"
Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<!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 runat="server">
<title></title>
<link rel="stylesheet" type="text/css"
href="<%= Url.Content("~/Styles/WebLoaderStyle.css") %>" />
<script type="text/javascript"
src="<%= Url.Content("~/Scripts/jquery-1.4.2.min.js") %>">
</script>
<script type="text/javascript">
$(document).ready(function () {
document.location.replace($("#WebURL").val());
});
</script>
</head>
<body>
<input type="hidden" id="WebURL"
value="<%= ViewData["WebURL"]%>" />
<div id="WaitInfo">
Page loading, please wait ...
</div>
</body>
</html>
The "IFrameWebLoader.aspx" has the "TabUrl
" saved in a hidden field. In the "$(document).ready
" event, I use JavaScript to read the hidden field and re-direct the page to the "TabUrl
". This intermediate step is not an absolute necessity. It is merely used to give the user some kind of visual feedback that their desired page is loading, in case the web page pointed by "TabUrl
" takes a long time to load. I put the text "Page loading, please wait ..." and set the "cursor" of this page to "wait" for this purpose. Since the "IFrameWebLoader.aspx" page is very small, the overhead should be minimal.
Host External Web Pages in IFrams
Using "IFrames" to host web pages is a good idea in many cases. But you need to be careful if you are hosting external web pages that you do not have any control. I recently learned from one of my colleagues that if the authors do not want someone to host their web pages in "IFrames", they can place a "Frame Buster" in their pages with one line of JavaScript code:
<script type="text/javascript">
if (top != self) top.location.replace(self.location.href);
</script>
This simple "Frame Buster" is very effective. If it finds that the web page is hosted in a frame, it will break the frame and take over the whole web browser window. Although some people studied "Framekiller Killers", it seems that they have only limited capability to kill the "Frame Busters".
If your intention is to host your own web pages in "IFrames", you should not have any problem, except if you want to mix "http/https" in your web application. In this case, your users may see some warning messages.
Run the Application
Now let us run our application. In the application, I added a few tabs. One tab loads "Yahoo", and the other tabs load some "Youtube" videos. Neither "Yahoo" nor "Youtube" has "Frame Busters". If you click on a tab, you will notice that the web page that you put in the "Json" object is loaded. The following picture shows the tab to load a "Youtube" video.
Points of Interest
- This article introduced a method to dynamically generate tabs to host "IFrames" using "jQuery" and "Json" in "ASP.Net MVC".
- The content of the "Json" can be configured in a "MVC" "controller" and loaded to the client by an "Ajax" call.
- The "jQuery" tabs are created by a "jQuery Plug-in". You can refer to this link for more details.
- Instead of setting the "src" attribute of the "Iframe" directly to the desired web page, the example project goes to an intermediate page so the users are informed if the desired web page takes a long time to load.
- If you want to host external web pages in "IFrames", you should be aware of the "Frame Busters".
- I hope you like my postings and I hope this article can help you in one way or the other.
History
- This is the first revision of this article.