一些奇特的 javascript 功能
在北半球这里,夏天已经过去了。孩子们回到了学校。秋天临近的迹象使小组的成员感染到了这种新入学的情绪。我们什么时候会长大呢?!我们采访了 Jay,还有他的女儿,因此他们要伴随 Web Team Talking 走进秋天。您可以期待更多的精彩内容。
除了扔湿纸团、理新发型之外,本月我们研究了 MSN® Messenger、一些奇特的 JavaScript 功能、如何更新服务器上的 XML 数据,以及多得让您看不完的短篇集。
如果您愿意学习,请将有关 Web 的问题发送给我们,我们会立即对您的问题作答。Instant Gratification
亲爱的 Web Team :
我可以将 MSN Instant Messenger 嵌入在诸如 msn.com 这样的网站上吗?
谢谢,
Joshua Carroll
Web Team 的答复:
我们越来越需要一种比 Instant Gratification 更好的功能,在这一方面,MSN Messenger 最有说服力。http://www.msn.com 中的 IM 功能是使用提供 Messenger 功能的两个 Microsoft ActiveX ® 控件以及提供 DHTML 用户界面的一些脚本来实现的。为简短起见,假定我要在 Intranet 主页中添加一个小的提要栏,以便在用户登录后列出该用户的联系人(及其连接状态),并在用户未登录时提供登录按钮。下面提供了一个示例,但是为了表明我们不是得到了神(请注意小写字母“o”)的暗示才领悟到这一信息,我们先概述一下所涉及的技术。
由于没有关于这些控件的文档,因此我们需要做一些研究工作来实现我们的目标。首先,我们可以查看 msn.com 使用的代码以获得一些提示。这不是因为缺乏勇气,而是通过艰苦地研究这些代码,我们可以从大体上了解这两个控件的功能,以及我们可以怎样编写自己的代码。我们可以使用的第二个工具(并且是有用得多的工具)是 OLE View。OLE View 是 Microsoft Visual Studio_ 附带的一个实用工具,它的功能很强大,可以分析并显示 COM 对象的类型信息。
因此,我们启动 OLE View,找到前面提到的两个控件(其友好名称分别为 MSN Messenger Application 和 MSN Messenger Object),右键单击这两个控件,然后选择 View type information 。该视图将显示对象使用的各个接口和枚举。标记为调度接口的任何接口都应该可用于基于脚本的调用方,因此我们开始着手编码了!
在确保控件正确实例化以后,代码需要做的第一件事情是确定用户是否联机。这是通过检查 Messenger Object 的 LocalState 属性来完成的。该属性从 MSTATE 枚举(其定义已部分包含在我们的代码中)返回一个值。如果用户未联机,我们将启用调用 Messenger Application 的 LaunchLogonUI 方法的登录按钮。如果用户已联机,则我们需要使用用户的所有联系人来填充 DIV。为此,我们循环访问默认列表 (List(0)) 中的每个联系人,并检索其 FriendlyName 以及描述其当前状态的 MSTATE 值,然后新建一个包含此信息的 DIV。
由于我们要允许用户通过单击列表中的任何联系人来向该联系人发送消息,因此需要以编程方式将 onclick 事件处理程序附加到新 DIV。该事件处理程序函数 ( sendMessage ) 基本上是 Messenger Application 的 LaunchIMUI 方法的包装。由于 LaunchIMUI 将目标联系人作为一个参数,因此我们需要能够确定 Messenger Object 的默认列表中的哪个联系人与被单击的 DIV 对应。这时最理想的是使用 expando 属性。我们将联系人在默认列表中的索引附加到新建的、表示该联系人的 DIV 元素。这样,onclick 处理程序就能够检索到此索引,然后从 Messenger Object 的列表中抽取正确的联系人并将其传递给 LaunchIMUI 。
本示例包含的一项额外功能是在该页加载后处理注销和登录。这是通过处理由 Messenger Object 激发的 OnLocalStateChangeResult 事件来完成的。此事件在 LocalState 属性更改时激发(并不太意外)。因此,相应地,我们仅仅查看我们已脱机还是已联机。如果已脱机,需要清空联系人 UI 列表;如果已联机,需要在短暂的 setTimeout(确保一切都正确同步)之后调用 populateContacts 。而且由于 ActiveX 控件依赖于独立 Messenger 的运行实例,因此我们的页面还将更新以响应它触发的事件。
至此,您可能已对我们的喋喋不休感到厌烦,那么,请看下面的代码。
<HTML>
<HEAD>
<TITLE>Embedding MSN Messenger Test</TITLE>
<OBJECT CLASSID="clsid:F3A614DC-ABE0-11d2-A441-00C04F795683"
CODEBASE="#Version=2,0,0,83"
CODETYPE="application/x-oleobject" ID="oMsgrObj" WIDTH="0"
HEIGHT="0" OnUserStateChanged="alert();">
</OBJECT>
<OBJECT CLASSID="clsid:FB7199AB-79BF-11d2-8D94-0000F875C541"
CODETYPE="application/x-oleobject"
ID="oMsgrApp" WIDTH="0" HEIGHT="0">
</OBJECT>
<STYLE>
BODY
{
FONT-FAMILY: Verdana, Arial, Helvetica;
FONT-SIZE: 8pt;
}
INPUT
{
FONT-FAMILY: Verdana, Arial, Helvetica;
FONT-SIZE: 8pt;
}
.clsHeading
{
FONT-WEIGHT: bolder;
FONT-SIZE: 10pt;
}
.clsContact
{
PADDING: 2px;
CURSOR: hand;
}
</STYLE>
</HEAD>
<BODY onLoad="body_onLoad();">
<SCRIPT LANGUAGE="JScript">
// Here are the definitions for the Messenger enumerated values we use
var MSTATE_OFFLINE = 1;
var MSTATE_ONLINE = 2;
var MSTATE_BUSY = 10;
var MSTATE_BE_RIGHT_BACK = 14;
var MSTATE_IDLE = 18;
var MSTATE_AWAY = 34;
function body_onLoad()
{
// First, we need to make sure that the Messenger controls got instantiated correctly
if ("undefined" != typeof(oMsgrObj) && null != oMsgrObj.object && "undefined"
!= typeof(oMsgrApp) && null != oMsgrApp.object)
{
// If so, let's check to see if we're online and (if so) populate our contacts UI
if (oMsgrObj.LocalState == MSTATE_ONLINE)
{
populateContacts();
}
else if (oMsgrObj.LocalState == MSTATE_OFFLINE)
{
btnLogon.disabled = false;
}
}
else
{
// Uh, oh - the controls didn't get instantiated correctly;
// the user probably needs to install Messenger
alert("You need to install the latest version of MSN Messenger!
/nGo to http://messenger.msn.com right now!");
}
}
function populateContacts()
{
var oList = oMsgrObj.List(0);
var oContact;
var i;
// To populate our contact list, we're going to iterate throught the
// default list collection, check the state of each contact,
// and add a DIV with the approppriate appearance to our UI
for (i = 0; i < oList.Count; i++)
{
oContact = oList.Item(i);
oNewElement = document.createElement("DIV");
oNewElement.innerText = oContact.FriendlyName;
switch (oContact.State)
{
case MSTATE_ONLINE:
// Don't need to do anything
break;
case MSTATE_OFFLINE:
oNewElement.innerText += " (Offline)";
oNewElement.style.color = "graytext";
break;
case MSTATE_BUSY:
oNewElement.innerText += " (Busy)";
break;
case MSTATE_BE_RIGHT_BACK:
oNewElement.innerText += " (Be Right Back)";
break;
case MSTATE_IDLE:
oNewElement.innerText += " (Idle)";
break;
case MSTATE_AWAY:
oNewElement.innerText += " (Away)";
break;
default:
oNewElement.innerText += "(Just plain not around!)";
oNewElement.style.color = "graytext";
break;
}
oNewElement.className = "clsContact";
// To enable us to respond to the onclick event,
// we're programmatically setting the event
// handler AND we're defining an expando property
// on the contact DIV whose value is set to
// the index of the contact in the default list (so we can find them later)
oNewElement.onclick = sendMessage;
oNewElement.setAttribute("CONTACTID", i.toString());
divContacts.appendChild(oNewElement);
}
}
function doLogon()
{
// To logon, we just ask Messenger to display its logon UI
if (oMsgrObj.LocalState == MSTATE_OFFLINE)
{
btnLogon.disabled = true;
oMsgrApp.LaunchLogonUI();
}
}
function sendMessage()
{
// To send a message, we likewise just ask Messenger to do the heavy
// lifting (we just have to
// pass it a contact from the default list)
var nContactID = parseInt(window.event.srcElement.getAttribute("CONTACTID"));
if (!isNaN(nContactID))
{
var oContact = oMsgrObj.List(0).Item(nContactID);
oMsgrApp.LaunchIMUI(oContact);
}
}
</SCRIPT>
<SCRIPT LANGUAGE="JScript" EVENT="onLocalStateChangeResult(hr)" FOR="oMsgrObj">
if (hr == 0)
{
if (oMsgrObj.LocalState == MSTATE_ONLINE)
{
// Now we're online
window.setTimeout("populateContacts();", 3000);
}
else if (oMsgrObj.LocalState == MSTATE_OFFLINE)
{
// Now we're offline
divContacts.innerHTML = "";
btnLogon.disabled = false;
} }
</SCRIPT>
<DIV CLASS="clsHeading">Contacts (click on a contact to send a message!)</DIV>
<DIV ID="divContacts" STYLE="MARGIN-TOP: 8px;
MARGIN-BOTTOM: 8px; BORDER: 1px solid steelblue"></DIV>
<INPUT TYPE="BUTTON" ID="btnLogon" VALUE="Logon" onClick="doLogon();" DISABLED="yes">
</BODY>
</HTML> 将 XML 发送回服务器
亲爱的 Web Team :
你们怎样修改客户端 Web 浏览器上的 XML 并将最后的 XML 发送到 Web 服务器呢?
Web Team 的答复:
我们假定您的 Web 服务器运行的是 Microsoft Internet Information Server (IIS) 4.0(或更高版本)
或支持 Active Server Page (ASP) 技术的产品。
我们来简要地回顾一下 Web 浏览器是如何提供丰富的交互式 Internet 体验的。考虑最简单的形式:Web
浏览器从 Web 服务器请求一个页面,处理返回的信息(通常是超文本标记语言 (HTML) 格式),并显示结果。
超文本传输协议 (HTTP) 定义了 Web 服务器和浏览器用来相互通讯的语言。Web 浏览器还执行大量的幕后工作,
以便用户能够导航 Web 并显示各种媒体类型。
因此,您需要一个交互式 Web 站点。那么,用户怎样将信息发送到 Web 服务器呢?
Web 浏览器或 Web 应用程序有许多种用来将 XML 发送到 Web 服务器的方法,我们将介绍其中的两种。Web
浏览器通常使用 HTTP GET 方法来请求 Web 页,并使用 HTTP POST 方法来发送信息。要实现用户与
Web 服务器之间的通讯,最简单的方法是使用 HTML FORM 元素。
FORM 元素提供了一种使用 GET 或 POST 方法将数据发送到服务器的途径。将哪些控件置于窗体内由您决定,
并且您还决定只有具有 NAME 属性的那些控件才能够提交其数据。例如,当用户单击 Submit 按钮时,
每个控件的数据都发送到 Web 服务器。
在下面的示例中,存储在 XML 数据岛中的 XML 将作为窗体的一部分提交到服务器。可以通过多种方法获取此
XML:硬编码、通过设置 SRC 属性从外部文件中加载、从 ADO 记录集中检索、从 SQL Server 2000 中检索或者使用
XML DOM 脚本对象模型来创建/修改。可以通过将 XML 绑定到 HTML 元素来将 XML 呈现给用户(请参阅 Binding HTML Elements to Data)。将 XML 数据包含在表单中提交就像在 onsubmit 事件处理程序中将 XML 分配给隐藏的 INPUT
元素一样简单,如下所示:
<FORM ACTION="http://yourserver/form.asp" METHOD="post" NAME="myform" 注 为方便起见,本例中服务器返回 XMP 元素内的 XML。尽管此 HTML 元素是否决的,但是它具有将 HTML 和 XML
ONSUBMIT=" myform.mytext.value = myxml.XMLDocument.xml;">
<INPUT TYPE="HIDDEN" NAME="Text" ID="mytext">
<INPUT TYPE="Submit"> </FORM>
<XML ID="myxml"> <root>
<item id="1" name="Item 1"/>
<item id="2" name="Item 2"/>
</root> </XML>
下面是 FORM.ASP 页的内容:
<%@ language=JScript %>
<%
var xmldom = Server.CreateObject( "Microsoft.XMLDOM" );
var xml = Request.Form( "Text" );
Response.Write( "<XMP>" + xml + "</XMP>" );
%>
元素作为文本显示的好处。
XMLHttpRequest 对象是 Microsoft Internet Explorer 5.0 或更高版本附带的 MSXML 组件的一部分。该对象是