后台服务器代理查询文件。为什么需要这个文件呢?因为Ajax的本质是使用JavaScript查询网站的少量数据,而基于安全原因,客户端的JavaScript不能够跨域请求数据,即www.a.com网站上的JavaScript不可以请求www.b.com网站上的数据。然而,服务端的脚本却不存在这个限制,于是,在自己网站上添加别人的RSS新闻内容的这个需求上,有必要也有可能在客户端与别的服务器之间设置一个代理,它接收客户端JavaScript的请求,转发给目的地服务器,收到回复后将回复再转发给客户端JavaScript。(假如不是由于这个安全原因,就不需要这个文件了。)
好了,说了这么多,要记住的就是这个文件仅仅起个传话筒的作用,将目的服务器的回复响应原封不动地转发给客户端JavaScript。它的构建非常简单,也不是重点,但却是个基础。我们的重点在于构建第二个文件,而第二个文件需要通过这个文件来获取目的服务器的数据。如果你的服务器脚本采用ASP,那么建立下面这样一个文件就行了(假设命名为newsfeeder.asp,保存在网站根目录)。
<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<% Option Explicit %>
<%Session.CodePage=65001%>
<%
Response.ContentType = "text/xml"
Dim sRSSUrl, oHTTP
sRSSUrl = Request.QueryString("rssurl")
Set oHTTP = Server.CreateObject("Microsoft.XMLHTTP")
oHTTP.Open "GET", sRSSURL, False
oHTTP.Send
Response.Write oHTTP.ResponseXML.XML
Set oHTTP = Nothing
%>
如果你的服务器脚本是PHP,那么建立如下的文件就可以了(假设命名为newsfeeder.php,保存在网站根目录)。
<?php
header('Content-type: text/xml;');
// 返回RSS源数据
echo file_get_contents($_REQUEST['rssurl']);
?>
获取RSS的核心JavaScript文件。此文件由一个mfRSSReader类以及一个辅助函数组成。正是这个mfRSSReader类隐藏了发送异步数据请求的细节,而辅助函数隐藏了如何处理响应数据的细节,使得最终使用它们是如此的简单。
在详细展示它的代码前,我再做一些介绍。mfRSSReader类有如下几个属性,通过设置这些属性,你可以按照自己的偏好来使用它。
- Request。这个属性就是XMLHTTP请求对象,它保存着服务器的响应数据,即RSS源的内容。它不需要设置。
- ReaderDivId。这个属性就是HTML文档中的一个层(Div)或者一块其他区域的ID值,通过自定义这个属性,你可以将RSS源内容显示到HTML文档中的指定的地方。
- RssUrl。这个属性保存着RSS源的Url地址。通过自定义这个值,你可以获取指定的RSS源内容。
- WaitDesc。这个属性可以用来设置查询RSS源时的说明文字。它不是必需要进行设置的。
- WaitImageSrc。这个属性是一个Url(可以是相对Url,如images/wait.gif,也可以是绝对Url,如http://www.myfootprints.cn/images/logo.gif)。它可以用来设置查询RSS源时的等待图标。它也不是必需要进行设置的。
- AjaxStateId。这个属性保存了显示Ajax查询状态信息的HTML文档中的层的ID值。它不是必需的,如果进行了设置,那么在HTML文档的指定区域,会显示Ajax的查询状态(如查询是否成功等)。
- FeedCount。这个属性不是必需设置的。它用来指定显示几条新闻。默认是10。它是用来给辅助处理函数(后面会讲到辅助处理函数)用的。
- Handler。它是处理函数的引用。通过设置它的值,你可以选择以何种方式显示RSS源。它不是必需设置的,默认为将RSS源的内容显示为一个标题列表。
- ServerFeeder。它存储着后台服务器查询代理的Url。
- TimeoutIds。它不需要进行设置。
以上是mfRSSReader类的属性,它还有一些方法,然而你真正会用到的就是loadFeed()方法,在设置好相关属性后(如果你懒得设置,可以一个都不设置),直接调用它,就完成了所有工作。
辅助处理函数是干什么的呢?它就是对mfRSSReader类里的Request对象的xml数据(如上所述,Request对象保存着目的服务器的响应数据)进行分析,并以恰当的格式将数据呈现出来。你可以自定义这个函数,以自己的偏好来显示RSS新闻源内容。如果你将这个函数命名为myHandler(oRssReader),那么,将你的mfRSSReader类的实例的Handler属性指向它即可,如下所示。注意,这个处理函数有一个参数,就是你的mfRSSReader的实例,在处理函数内部对这个参数进行处理。
// 定义一个mfRSSReader类的实例
var oRssReader = mfRSSReader();
// 做一些设置,如RSS源是什么,显示在HTML文档的哪个地方等
// 然后,设置自己的处理函数
oRssReader.Handler = myHandler;
// 设置完成后,加载RSS源即可
oRssReader.loadFeed();
以下即将展示这个核心JavaScript文件的所有代码,有详细的注释方便阅读,如果你不想深究,那么将复制下来,保存为mfRSSReader.js,即可按照上述介绍使用它了!注意在网页的<head>部分加入对此文件的引用,即:
<script type="text/javascript" src="mfRSSReader.js"><script/>
mfRSSReader.js的代码:
/**
* mfRSSReader.js.
*
* Version 2.0
* Copyright (C) 2009- admin@myfootprints.cn.
* http://www.myfootprints.cn
*
*
* 用途:将指定的RSS源的内容显示在指定的DIV框架中
*/
//
// mfRSSReader 对象
//
// 用途:用来构建 mfRSSReader 对象
// 参数:
// sReaderDivId, RSS内容将会在HTML文档中的ID为sReaderDivId的Div层中显示
// sRssUrl, 指定的RSS源
// sWaitDesc, 可选参数,加载描述文字,如"正在加载"
// sWaitImageSrc, 可选参数,加载提示图片的Url,可以是相对路径
// sAjaxStateDivId, 可选参数,用于显示提示Ajax状态的HTML文档中的Div层的Id,一般可以不在文档中放置这个层,这个参数可以省略
//
function mfRSSReader(sReaderDivId, sRssUrl, sWaitDesc, sWaitImageSrc, sAjaxStateDivId) {
if (sReaderDivId) {
this.ReaderDivId = sReaderDivId;
} else {
this.ReaderDivId = '';
}
if (sRssUrl) {
this.RssUrl = sRssUrl;
} else {
this.RssUrl = '';
}
if (sWaitDesc) {
this.WaitDesc = sWaitDesc;
} else {
this.WaitDesc = '加载中……';
}
if (sWaitImageSrc) {
this.WaitImageSrc = sWaitImageSrc;
} else {
this.WaitImageSrc = 'http://www.myfootprints.cn/jsLib/ajaxtoolkit/wait.gif';
}
if (sAjaxStateDivId) {
this.AjaxStateDivId = sAjaxStateDivId;
} else {
this.AjaxStateDivId = null;
}
// 以下为默认属性
// 10条新闻
this.FeedCount = 10;
// 处理XML文档的函数
this.Handler = mfRRA.handleFeedRequest;
// 由于javascript的HTTP查询不能够跨越不同的服务器,需要配置一个服务器端的HTTP查询代理,在这里写上代理的服务器查询脚本的URL
this.ServerFeeder = 'newsfeeder.asp?rssurl=';
// Request 对象
this.Request = null;
// 计时器ID
this.TimeoutIds = null;
}
//
// loadFeed 方法
//
// 用途:用来加载RSS内容
//
mfRSSReader.prototype.loadFeed = function() {
// 清除上一次的源内容,同时显示加载中的提示信息
var oReaderDiv = document.getElementById(this.ReaderDivId);
if (oReaderDiv) {
var oChilds = oReaderDiv.childNodes;
for (var i = oChilds.length - 1; i >= 0; i--) {
oReaderDiv.removeChild(oChilds[i]);
}
if (this.WaitImageSrc) {
var oImg = new Image();
oImg.src = this.WaitImageSrc;
if (this.WaitDesc) {
oImg.alt = this.WaitDesc;
} else {
oImg.alt = '加载中……';
}
oReaderDiv.appendChild(oImg);
} else if (this.WaitDesc) {
var oText = document.createTextNode(this.WaitDesc);
oReaderDiv.appendChild(oText);
}
} else {
return;
}
// 发送 Ajax 请求加载新闻源内容
var oRSSReader = this;
this.ajaxSendRequest('GET', this.ServerFeeder + encodeURIComponent(this.RssUrl), function() { oRSSReader.Handler(oRSSReader); });
};
//
// ajaxSendRequest() 方法
//
// 用途:发送HTTP请求
//
mfRSSReader.prototype.ajaxSendRequest = function(sType, sUrl, fnHandler, sPostDataType, vPostData) {
if (this.Request == null) {
// 创建一个新的 Request 对象
this.Request = this.createXMLHTTPRequest();
} else {
// 杀掉之前的 Request 对象
this.Request.abort();
}
if (this.Request == null) {
throw new Error('Ajax 在创建 XMLHTTPRequest 对象时遇到错误。');
} else {
try {
// var sReaderDivId = this.ReaderDivId;
// var lFeedCount = this.FeedCount;
// var sAjaxStateDivId = this.AjaxStateDivId;
// var oRequest = this.Request;
// if (!fnHandler) {
// fnHandler = function() { mfRRA.handleFeedRequest(oRequest, sReaderDivId, lFeedCount, sAjaxStateDivId); };
// }
this.Request.onreadystatechange = fnHandler;
// 总是异步 第三个参数设置为true
this.Request.open(sType, sUrl, true);
if (sType.toLowerCase() == 'get') {
// 发送一个GET请求,不涉及到数据
this.Request.send(null);
} else {
// 发送一个POST请求,最后一个参数是数据
this.Request.setRequestHeader('Content-Type', sPostDataType);
this.Request.send(vPostData);
}
} catch (oError) {
throw new Error('Ajax 在与服务器的通信中遇到错误。\n' + '详情:' + oError);
}
}
};
//
// createXMLHTTPRequest()方法
//
// 用途:创建一个新的Request对象
//
mfRSSReader.prototype.createXMLHTTPRequest = function() {
if (typeof XMLHttpRequest == 'undefined' && window.ActiveObject) {
var arrSignatures = ["MSXML2.XMLHTTP.5.0", "XSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"];
for (var i = 0; i < arrSignatures.length; i++) {
try {
var oRequest = new ActiveXObject(arrSignatures[i]);
return oRequest;
} catch (oError) {
//忽略
return null;
}
}
//throw new Error("MSXML 没有安装到你的系统中。");
} else if (window.XMLHttpRequest) {
try {
var oRequest = new XMLHttpRequest();
return oRequest;
} catch (oError) {
return null;
}
} else {
return null;
}
};
//
// 隐藏Ajax状态信息
// 如果给定lDelay参数,则启动计时器,否则不启动
//
mfRSSReader.prototype.toggleAjaxStateInfo = function(bShow, lDelay) {
var oStateElem = document.getElementById(this.AjaxStateDivId);
function toggleAjaxStateInfoTo() {
if (bShow) {
oStateElem.style.display = 'block';
} else {
oStateElem.style.display = 'none';
}
}
if (oStateElem) {
if (lDelay) {
if (this.TimeoutIds) {
this.TimeoutIds[this.TimeoutIds.length] = window.setTimeout(toggleAjaxStateInfoTo, lDelay);
} else {
this.TimeoutIds = window.setTimeout(toggleAjaxStateInfoTo, lDelay);
}
} else {
toggleAjaxStateInfoTo();
}
}
};
//
// 清除计时器
//
mfRSSReader.prototype.clearHideAjaxStateInfoTimeout = function() {
if (this.TimeoutIds) {
for (var i = this.TimeoutIds.length - 1; i >= 0; i--) {
window.clearTimeout(this.TimeoutIds[i]);
this.TimeoutIds.splice(i, 1);
}
}
};
//
// 更新Ajax状态
//
// 参数:
// oRequest(*), 更新哪个XMLHttpRequest对象的状态
// sAjaxStateDivId(*), 更新状态信息到HTML文档中ID为sAjaxStateDivId中
//
mfRSSReader.prototype.ajaxUpdateState = function() {
var oStateElem = document.getElementById(this.AjaxStateDivId);
var oRequest = this.Request;
if (oStateElem) {
// 修饰一下状态元素
oStateElem.style.fontSize = 'small';
oStateElem.style.border = '1px solid';
oStateElem.style.textAlign = 'center';
try {
this.clearHideAjaxStateInfoTimeout();
} catch (oError) {
} finally {
this.toggleAjaxStateInfo(true);
}
switch (oRequest.readyState) {
// UNITIALIZED - Request has not yet been opened
case 0:
oStateElem.style.backgroundColor = "#FFFFFF"; // white
//oStateElem.innerHTML = "Request uninitialized.";
oStateElem.innerHTML = "查询没有启动。";
this.toggleAjaxStateInfo(false, 2000);
break;
// LOADING - Request has not yet been sent
case 1:
oStateElem.style.backgroundColor = "#999999"; // gray
//oStateElem.innerHTML = "Request initialized.";
oStateElem.innerHTML = "查询启动了。";
this.toggleAjaxStateInfo(false, 2000);
break;
// LOADED - Request has been sent
case 2:
oStateElem.style.backgroundColor = "#FF0000"; // red
//oStateElem.innerHTML = "Waiting for response...";
oStateElem.innerHTML = "等待响应……";
this.toggleAjaxStateInfo(false, 2000);
break;
// INTERACTIVE - Response data is being downloaded
case 3:
oStateElem.style.backgroundColor = "#FFFF00"; // yellow
//oStateElem.innerHTML = "Downloading response...";
oStateElem.innerHTML = "正在加载……";
this.toggleAjaxStateInfo(false, 2000);
break;
// COMPLETE - Request/response is complete
case 4:
if (oRequest.status == 200) {
// Everything is OK
oStateElem.style.backgroundColor = "#00FF00"; // green
//oStateElem.innerHTML = "Request complete!";
oStateElem.innerHTML = "查询完成!";
this.toggleAjaxStateInfo(false, 2000);
} else {
// There was a problem
oStateElem.style.backgroundColor = "#FF8800"; // orange
//oStateElem.innerHTML = "Request failed!";
oStateElem.innerHTML = "查询失败!";
// 失败的信息不要隐藏
try {
this.clearHideAjaxStateInfoTimeout();
} catch (oError) {
} finally {
this.toggleAjaxStateInfo(true, 2000);
}
}
break;
}
}
};
mfRSSReader.prototype.getText = function(oElem) {
// 获取一个元素的内部文本
var sText = '';
if (oElem != null) {
if (oElem.childNodes) {
for (var i = 0; i < oElem.childNodes.length; i++) {
if (oElem.childNodes[i].nodeValue != null) {
sText += oElem.childNodes[i].nodeValue;
}
}
}
}
return sText;
};
// mfRSSReader 的助手,用来处理获取到的XML文档,你可以添加自定义的处理函数,并在调用mfRSSReader.loadFeed()方法前将 mfRSSReader.Handler 设置成为你自定义的处理函数
// 设置方法如下:
// var oRssReader = new mfRSSReader('rssreader', 'http://www.myfootprints.cn/blog/rss.xml', '正在加载……', 'http://www.myfootprints.cn/jsLib/ajaxtoolkit/wait.gif');
// oRssReader.Handler = mfRRA.myHandleFeedRequest;
//
var mfRSSReaderAssistant = new Object();
var mfRRA = mfRSSReaderAssistant;
//
// handleFeedRequest() 方法 - 默认
//
// 用途:获取到XML对象后的处理过程
//
mfRRA.handleFeedRequest = function(oRSSReader) {
var oRequest = oRSSReader.Request;
if (oRequest.readyState == 4 && oRequest.status == 200) {
// 保存XML响应数据
var oXMLData = oRequest.responseXML;
// 从源标题开始生成源内容
var sFeedContent = '';
var oChannelElem = oXMLData.getElementsByTagName('channel')[0];
sFeedContent += '';
// 生成源条目组成的列表
sFeedContent += '
';
var oFeedItems = oChannelElem.getElementsByTagName('item');
var lFeedCount = oRSSReader.FeedCount;
if (lFeedCount == -1) {
lFeedCount = oFeedItems.length;
}
for (var i = 0; i < (oFeedItems.length < lFeedCount ? oFeedItems.length : lFeedCount); i++) {
var sItemTitle = oRSSReader.getText(oFeedItems[i].getElementsByTagName('title')[0]);
var sItemLink = oRSSReader.getText(oFeedItems[i].getElementsByTagName('link')[0]);
var sItemPubDate = oRSSReader.getText(oFeedItems[i].getElementsByTagName('pubDate')[0]);
if (sItemPubDate == '') {
sItemPubDate = oRSSReader.getText(oFeedItems[i].getElementsByTagName('date')[0]);
}
if (sItemPubDate == '') {
sFeedContent += '- ' + sItemTitle + '
';
} else {
var sItemPubDateString = sItemPubDate;
try {
var oDate = new Date(Date.parse(sItemPubDate));
sItemPubDate = oDate.getFullYear() + '-' + (oDate.getMonth() + 1) + '-' + oDate.getDay();
} catch (e) {
sItemPubDate = sItemPubDateString;
} finally {
}
sFeedContent += '- ' + sItemTitle + '(' + sItemPubDate + ')
';
}
}
sFeedContent += '
';
// 显示源内容
document.getElementById(oRSSReader.ReaderDivId).innerHTML = sFeedContent;
}
// 更新 Ajax 状态
if (oRSSReader.AjaxStateDivId) {
oRSSReader.ajaxUpdateState();
}
};
//
// handleFeedRequest_FullText_LikeGoogle() 方法 - 与默认的标题列表不同,这里将每篇新闻的全文显示出来,模仿Google Reader的样式
//
// 用途:获取到XML对象后的处理过程
//
mfRRA.handleFeedRequest_FullText_LikeGoogle = function(oRSSReader) {
var oRequest = oRSSReader.Request;
if (oRequest.readyState == 4 && oRequest.status == 200) {
// 保存XML响应数据
var oXMLData = oRequest.responseXML;
// 从源标题开始生成源内容
var sFeedContent = '';
var oChannelElem = oXMLData.getElementsByTagName('channel')[0];
sFeedContent += '' + oRSSReader.getText(oChannelElem.getElementsByTagName('title')[0]) + '
» ';
// 生成源条目组成的列表
sFeedContent += '';
var oFeedItems = oChannelElem.getElementsByTagName('item');
var lFeedCount = oRSSReader.FeedCount;
if (lFeedCount == -1) {
lFeedCount = oFeedItems.length;
}
for (var i = 0; i < (oFeedItems.length < lFeedCount ? oFeedItems.length : lFeedCount); i++) {
var sItemTitle = oRSSReader.getText(oFeedItems[i].getElementsByTagName('title')[0]);
var sItemLink = oRSSReader.getText(oFeedItems[i].getElementsByTagName('link')[0]);
var sItemPubDate = oRSSReader.getText(oFeedItems[i].getElementsByTagName('pubDate')[0]);
if (sItemPubDate == '') {
sItemPubDate = oRSSReader.getText(oFeedItems[i].getElementsByTagName('date')[0]);
}
if (sItemPubDate == '') {
sFeedContent += '';
} else {
var sItemPubDateString = sItemPubDate;
try {
var oDate = new Date(Date.parse(sItemPubDate));
sItemPubDate = oDate.getFullYear() + '-' + (oDate.getMonth() + 1) + '-' + oDate.getDay();
} catch (e) {
sItemPubDate = sItemPubDateString;
} finally {
}
sFeedContent += '
';
}
var sAuthor = oRSSReader.getText(oFeedItems[i].getElementsByTagName('author')[0]);
sFeedContent += '
由 ' + sAuthor + ' 发表
';
var sItemContent = oRSSReader.getText(oFeedItems[i].getElementsByTagName('description')[0]);
sFeedContent += '
' + sItemContent + '
'
}
sFeedContent += '
';
// 显示源内容
document.getElementById(oRSSReader.ReaderDivId).innerHTML = sFeedContent;
}
// 更新 Ajax 状态
if (oRSSReader.AjaxStateDivId) {
oRSSReader.ajaxUpdateState();
}
};