JavaScript And Ajax(带有 js 的控件)
JavaScript 在很多高级 Web 控件里起着很重要的作用,服务器端的面向对象编程和客户端的 js 修饰,可以使你两全其美。
弹出窗口
弹出窗口是 Web 最扰人的特性之一。通常,它们派发广告,但有时也会出于正当的目的提供有用的信息,或邀请用户参加调查和促销。一个相关的变体是跃出式(pop-under)窗口,它在当前窗口下面显示一个新窗口。
在 JavaScript 块中用 window.open() 函数显示一个弹出窗口非常容易。例如:
<script type="text/javascript">
window.open('http://www.qq.com', 'myWindow',
'toolbar=0, height=400, width=600, resizable=1, scrollbars=1');
window.focus();
</script>
window.open() 函数接收几个参数,包括新页面的链接、窗口的框架名称(如果希望通过另一个链接加载新文档到那个框架,框架名称是很重要的),以及一组用逗号分隔的特性字符串。
这些特性配置弹出窗口的样式和大小,可以包括以下几项:
- height、width:设置为像素值
- toobar、menubar:可设置为 1 或 0,或(yes 和 no)
- resizable:一个固定还是可以改变大小的窗体边框,可以设置为 1 或 0
- scrollbars:是否显示滚动条,可以设置 1 或 0
你可能会希望在多个页面上使用同一个弹出功能,并根据用户特有的信息修改弹出框的 URL。例如,可能要在显示广告前检查用户是否已经看过了该广告,或者要把用户名作为查询字符串参数传给新窗口以便它并入弹出信息。
对于这些场景,需要能够通过编程控制弹出窗口,因此创建一个包装所有这些细节的组件是很必要的。下面这个例子开发一个充当这种角色的 PopUp 控件。(可以放入 App_Code 目录中,但是更好的做法是放到一个单独的类库程序集中,本例采用这种方式)
通过从 Control 控件中继承,就能够把弹出窗口加入到工具箱并在设计时拖到 Web 表单上。为了确保控件尽可能的重用,它提供诸如 PopUnder、Url、WindowHeight、WindowWidth、Resizable 和 Scrollbars 之类的属性,这可以配置生成的 js 代码。
public class PopUp : Control
{
public bool PopUnder
{
get { return (bool)ViewState["PopUnder"]; }
set { ViewState["PopUnder"] = value; }
}
public string Url
{
get { return (string)ViewState["Url"]; }
set { ViewState["Url"] = value; }
}
public int WindowHeight
{
get { return (int)ViewState["WindowHeight"]; }
set { ViewState["WindowHeight"] = value; }
}
public int WindowWidth
{
get { return (int)ViewState["WindowWidth"]; }
set { ViewState["WindowWidth"] = value; }
}
public bool Resizable
{
get { return (bool)ViewState["Resizable"]; }
set { ViewState["Resizable"] = value; }
}
public bool Scrollbars
{
get { return (bool)ViewState["Scrollbars"]; }
set { ViewState["Scrollbars"] = value; }
}
public PopUp()
{
PopUnder = true;
Url = "about:blank";
WindowHeight = 300;
WindowWidth = 300;
Resizable = false;
Scrollbars = false;
}
}
现在该把它们放到 Render() 方法里,该方法把 JavaScript 代码写到页面上。首先要确保浏览器支持 JavaScript:
- Page.Request.Browser.JavaScript:返回 true 或 false。不过此方法已经被废弃了,它不能灵活区别 JavaScript 和 HTML DOM 支持的不同级别。
- Page.Request.Browser.EcmaScriptVersion:检查是大于还是等于 1 。它标识对 JavaScript 的支持度。
如果支持 JavaScript,开始构建脚本块。这非常简单,不过,唯一要注意的细节是布尔型的属性要先转化为整数然后再转换为字符串!例如 scrollbars=1 而不是 scrollbars=true,直接转换会得到布尔类型的字面值。
protected override void Render(HtmlTextWriter writer)
{
if (Page.Request.Browser.EcmaScriptVersion.Major >= 1)
{
StringBuilder javaScriptString = new StringBuilder();
javaScriptString.Append("<script type='text/javascript'>");
javaScriptString.Append("\n<!-- ");
javaScriptString.Append("\nwindow.open('");
javaScriptString.Append(Url + "', '" + ID);
javaScriptString.Append("','toolbar=0,");
javaScriptString.Append("height=" + WindowHeight + ",");
javaScriptString.Append("width=" + WindowWidth + ",");
javaScriptString.Append("resizable=" + Convert.ToInt16(Resizable).ToString() + ",");
javaScriptString.Append("scrollbars=" + Convert.ToInt16(Scrollbars).ToString());
javaScriptString.Append("');\n");
if (PopUnder) javaScriptString.Append("window.focus();");
javaScriptString.Append("\n-->\n");
javaScriptString.Append("</script>\n");
writer.Write(javaScriptString.ToString());
}
else
{
writer.Write("<!-- This browser doesn't support JavaScript! -->");
}
}
要使用 PopUp 控件,依然需要注册控件程序集并把它映射到具有 Register 指令的控件前缀。然后可以在页面上声明 PopUp 控件。类似下面:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="CustomServerControlsLibrary" Namespace="CustomServerControlsLibrary"
TagPrefix="cc1" %>
<!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>
</head>
<body>
<form id="form1" runat="server">
<div>
<cc1:PopUp ID="PopUp1" runat="server" Url="http://www.qq.com" Scrollbars="true">
</cc1:PopUp>
</div>
</form>
</body>
</html>
现在测试页面效果及查看源文件输出的 JavaScript 代码:
通常,自定义控件在 OnPreRender() 方法里注册 JavaScript 块,而不是直接将它写在 Render() 方法中。PopUp 控件没有采用这种方式是因为你不需要一般的行为(无论多少控件只创建 1 个脚本块),因为如果你添加了多个 PopUp 控件,你可能需要页面分别对每个控件输出单独的脚本块,这样才能创建显示多个弹出窗口的页面!
如果想要增强 PopUp 组件,可以添加更多属性。例如,增加指定窗口显示位置的属性。许多网站使用间隔一段时间才出现的广告,可以通过一个 JavaScript 定时器为这个组件实现这一技术。再次,基本思想是为页面开发人员提供整洁的对象,并使他们能够使用呈现方法产生页面所需的 JavaScript。
滚动按钮
滚动按钮是另一个有用的 JavaScript 技巧,ASP.NET 的世界里没有与之对应的内容。滚动按钮在刚开始显示一个图片,鼠标滑过时显示另一个图片(有时单击图片还会显示第三个图片)。
为了提供滚动效果,通常要有一个处理 JavaScript 事件 onclick、onmouseover、onmouseout 的 <img> 标签。这些事件调用为当前按钮切换图片的函数。像下面这样:
<script type="text/javascript">
function SwapImage(id, url) {
var elm = document.getElementById(id);
elm.src = url;
}
</script>
配置后的 <img> 标签看起来像是这样(RollOverButton1 是控件呈现后的 <img> 元素的 id):
<img src="buttonOriginal.jpg"
onmouseover="SwapImage('RollOverButton1','buttonMouseOver.jpg');"
onmouseout="SwapImage('RollOverButton1','buttonOriginal.jpg');" />
滚动按钮是 Web 的一个重要支柱,很容易通过自定义控件填补它在 ASP.NET 中的空白!创建它最简单的办法是从 WebControl 类继承并使用 <img> 作为其基标签。还需要实现 IPostBackEventHandler 接口处理单击时的服务器端事件。
public class RollOverButton : WebControl, IPostBackEventHandler
{
public RollOverButton()
: base(HtmlTextWriterTag.Img)
{ }
public string ImageUrl
{
get { return (string)ViewState["ImageUrl"]; }
set { ViewState["ImageUrl"] = value; }
}
public string MouseOverImageUrl
{
get { return (string)ViewState["MouseOverImageUrl"]; }
set { ViewState["MouseOverImageUrl"] = value; }
}
// 注入能在两个图片间切换的客户端 js
// 页面很可能有多个滚动按钮控件的实例,应以控件特定的键注册脚本块
// 这样,多个实例可以共享同一个函数实例
// 按约定,脚本块注册通过覆盖 OnPreRender() 方法实现
protected override void OnPreRender(EventArgs e)
{
if (!Page.ClientScript.IsClientScriptBlockRegistered("swapImg"))
{
string script =
@"<script type='text/javascript'>
function swapImg(id, url){
var elm = document.getElementById(id);
elm.src = url;
}
</script>";
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"swapImga", script);
}
base.OnPreRender(e);
}
...
}
这段代码使用 IsClientScriptBlockRegistered() 方法显式的检查脚本块是否已经注册过。其实不一定要检查这个属性,只要使用同一个健,ASP.NET 就只会呈现脚本块的单一实例。
但是,你可以借助 IsClientScriptBlockRegistered() 和 IsStartupScriptRegistered() 避免那些可能很耗时的工作。对于本例,它节省了构建本不需要的脚本块字符串的微小负载。(说白了,不需要拼接了 script 变量再注册时发现已经注册过了,少了拼接的过程。)
RollOverButton 继承自 WebControl 且使用 <img> 作为基本标签,所以它能够智能的输出 <img> 标签。你唯一需要提供的部分是特性,如 name 和 src 。此外,你还需要处理 onclick 事件(回传页面)、onmouseover、onmouseout 事件来切换图片。
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
writer.AddAttribute("id", ClientID);
writer.AddAttribute("src", ImageUrl);
writer.AddAttribute("onclick", Page.ClientScript.GetPostBackEventReference(
new PostBackOptions(this)));
writer.AddAttribute("onmouseover",
string.Format("swapImage('{0}','{1}');", ClientID, MouseOverImageUrl));
writer.AddAttribute("onmouseout",
string.Format("swapImage('{0}','{1}');", ClientID, ImageUrl));
}
Page.ClientScript.GetPostBackEventReference() 方法返回对客户端 _doPostBack() 函数的引用,可以构建出发回传的控件。你也必须为控件指定 id 特性,这样服务器才能将其作为回传的源来辨认它。
最后是创建 IPostBackEventHandler 接口要求的 RaisePostBackEvent() 方法,并使用它引发服务器端事件:
public void RaisePostBackEvent(string eventArgument)
{
OnImageClicked(new EventArgs());
}
public event EventHandler ImageClicked;
protected virtual void OnImageClicked(EventArgs e)
{
if (ImageClicked != null)
{
ImageClicked(this, e);
}
}
现在创建测试页面:
<cc1:RollOverButton ID="RollOverButton1" runat="server"
ImageUrl="buttonOriginal.jpg" MouseOverImageUrl="buttonMouseOver.jpg"
onimageclicked="RollOverButton1_ImageClicked" />
<br />
<br />
<cc1:RollOverButton ID="RollOverButton2" runat="server"
ImageUrl="buttonOriginal.jpg" MouseOverImageUrl="buttonMouseOver.jpg"
onimageclicked="RollOverButton2_ImageClicked" />
<br />
<br />
<asp:Label ID="Label1" runat="server"></asp:Label>
改进这个控件的一个办法是增加图片预加载,这样 Rollover 图片在页面第一次呈现时就会被下载,而不是鼠标滑过图片时,如果没有预加载,也许会觉得第一次把鼠标移到图片上会有一点延迟。
实现预加载最简单的办法是创建一段在页面加载后运行的脚本。这个脚本要创建一个 JavaScriptImage 对象并把 Image.src 属性设置为要预加载的图片。如果有多个图片要预加载,依次设置即可。Image 对象并不会真的在页面上使用,但已经预加载的图片会被保存在浏览器的缓存中。
这是修改后的 OnPreRender() 方法:
protected override void OnPreRender(EventArgs e)
{
if (!Page.ClientScript.IsClientScriptBlockRegistered("swapImg"))
{
string script =
@"<script type='text/javascript'>
function swapImg(id, url){
var elm = document.getElementById(id);
elm.src = url;
}
</script>";
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"swapImga", script);
}
if (!Page.ClientScript.IsStartupScriptRegistered("preload" + this.ClientID))
{
string script =
"<script type='text/javascript'> " +
"var preloadedImage = new Image(); " +
"preloadedImage.src = '" + MouseOverImageUrl + "'; " +
"</script> ";
Page.ClientScript.RegisterStartupScript(this.GetType(),
"preload" + this.ClientID, script);
}
base.OnPreRender(e);
}