給自己維護的一個小網站加個首頁新聞,瀏覽器用 Ajax 或者 Ajah 從服務器取新聞異步的顯示出來,同時提供 RSS 源供聚合器訂閱。
XMLHTTPRequest 從上次做過一個手機模擬器后就再沒碰過,這次在網上搜尋發現了一個好東西:
http://www.scss.com.au/family/andrew/webdesign/xmlhttprequest/,一個 Cross-Browser 的 XMLHttpRequest 實現,作者在創作共用條款(Creative Commons License)下發布這個庫,只要你不移除腳本中作者的姓名和網址就可以自由使用,就用它了!下載 xmlhttprequest.js 以備后用。
數據可以保存在數據庫中也可以簡單的寫入到服務器的一個文件中,為了靈活起見我就在 MySQL 中新建一個表放站內新聞了,就這么幾個字段:id、title、content、time。新聞錄入和從數據庫中取數據提供給 Ajax/Ajah 請求略。
下面是客戶端 XMLHTTPRequest 的代碼:
<html><head><title>首頁新聞測試</title><meta http-equiv="Content-Type" content="text/html; charset=gb2312"><script type="text/javascript" src="xmlhttprequest.js"></script></head><body><div id="News">新聞讀取中……</div><script type="text/javascript">var req = new XMLHttpRequest();if( req ){req.onreadystatechange = function(){var News = "新聞讀取失敗";try{if( req.readyState == 4 && ( req.status == 200 || req.status == 304 ) )News = req.responseText;}catch(e){News = e.description;}document.getElementById( 'News' ).innerHTML = News;};req.open( 'GET', 'news.php' );req.send( null );}</script></body></html>
這里唯一值得說明的就是 XMLHTTPRequest 默認是用 UTF-8 傳遞數據的,所以你的數據源最好也用 UTF-8 否則(因為我們這里客戶端已經指定了要用 GB2312 了)得到的會是亂碼,這在 IE 下會引發 JavaScript 錯誤,在 FireFox 下卻沒問題,所以我用 try 和 catch 把上面的代碼包住了,免得測試的時候出現錯誤。如果想試試的話可以用下面的代碼作為 news.php:
<?phpecho iconv( "GB2312", "UTF-8", "這就是新聞" );//echo "這就是新聞";?>
如果注釋掉第一句而開放第二句的話不僅看到的是亂碼,在 IE 下還會出現 -1072896748 錯誤。有意思的是 IE 好像還會緩存由 XMLHTTPRequest 收到的內容,而 FireFox 則不會,所以在測試中會出現刷新 IE 很多次看到的還是舊數據的問題,可以在程序里加上下面 4 句話解決這個問題:
header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" );header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" );header( "Cache-Control: no-cache, must-revalidate" );header( "Pragma: no-cache" );
要將首頁新聞提供給聚合器訂閱就要將后臺輸入的新聞生成 RSS feed,RSS 2.0 的規范可以參考
http://feedvalidator.org/docs/rss2.html,相應的中文翻譯版本可以在
http://www.cpcwedu.com/Document/WEBOfficial/095447158.htm 找到。
RSS 格式其實也是 XML 眾多方言中的一種,所以也要以 開頭,根節點的名稱必須是 rss,符合 RSS 2.0 規范的 RSS 中這個 rss 節點要有一個 version="2.0" 的屬性。rss 節點有一個名為 channel 的子節點,channel 就是一個頻道。channel 有 3 個必要的子節點,分別為 title、link 和 description。以 Blog 為例,title 就是 Blog 的名字,link 是 Blog 的 URL 地址,而 description 是 Blog 的描述。
channel 可以包含若干個 item 子節點,在 Blog 中每一個 item 就對應一篇 post,我們這里每一個 item 就是一篇新聞。item 的所有子節點均為可選,但至少要包含 title 和 description,由于新聞要有時間,所以我們再給它加上一個 pubDate 節點,下面就是一個符合 RSS 2.0 規范的 RSS 文件內容:
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>The name of my site</title><link>
http://www.mysite.com</link><description>Just for testing</description><item><title>About</title><description>Hi, I'm 2ndboy. Welcome to my site!</description><pubDate>Sat, 07 Sep 2005 0:00:01 GMT</pubDate></item><item><title>New service is out!</title><description>It's great for using.</description><pubDate>Sat, 21 Sep 2005 1:23:45 GMT</pubDate></item></channel></rss>
從數據庫中讀取最新的新聞內容生成 RSS 的代碼這里就不貼了,無非是一些字符串的拼接,當然了,你也可以用 PHP 的 XML DOM 接口來生成這個 RSS 內容。這樣一來在網站上提供 RSS 訂閱就搞定了。
在做完 RSS 部分后我突然有了一個想法,為了給使用 Ajax 取數據的瀏覽器提供信息,服務器要針對 Ajax 的請求單獨作些響應處理,那何不就讓瀏覽器把我們前面已經生成了的 RSS 當作數據源來顯示首頁新聞呢?這似乎是個不錯的想法,但 RSS 是個 XML 格式的文件,很顯然,想達到這樣的目的就要讓客戶端用 JavaScript 來解析 XML 文件并加以顯示。
為了用 JavaScript 來操控 XML,我決定使用 Google 的一個開源項目——AjaXSLT(
http://goog-ajaxslt.sourceforge.net/),更多 Google 開源項目可以訪問
http://code.google.com/。Google 提供的這個 AjaXSLT 項目用 JS 實現了一個 XML 的 DOM 接口和一個 XSLT 實現,下面把里面的幾個主要文件做個簡單介紹:
misc.js:一些常量定義和 helper 函數,還有 log 的實現。dom.js:XML DOM 接口的 JS 實現,主要的函數是 xmlParse( XMLString ),調用成功后就可以用標準 DOM 方式來操控返回的 XDocument 了。
xpath.js:XPath 的 JS 實現。
xslt.js:XSLT 的 JS 實現,要用到里面的 xsltProcess( XML, XSLT ),給定 XML 和 XSLT 得到轉換的結果。
你可以只使用其中的 DOM 實現部分,而 dom.js 又依賴于 misc.js,所以要先載入 misc.js 再載入 dom.js。為了在首頁上顯示好看的新聞,我們要用 CSS 來打扮一下新聞的外觀,所以要對新聞的數據組織方式有些要求,比如說要把得到的新聞最終組織成下面這個樣子
<div id="News"><div class="News"><span class="Title">title</span><span class="Time">time</span><span class="Content">content</span></div></div>
這樣之后我們就可以事先寫一些針對 #News 和 div.News 等等的規則來裝扮首頁新聞了。好了,下面看看用 JS 解析 RSS 的代碼怎么寫(當然,之前我們先要用 Ajax 取到 RSS 數據): var News = "";var doc = xmlParse( rss );items = doc.getElementsByTagName( "item" );for( var i = 0; i < items.length; i++ ){News += "<div class='News'>";title = items[i].getElementsByTagName( "title" );News += ( "<span class='Title'>" + title[0].firstChild.nodeValue + "<\/span>" );time = items[i].getElementsByTagName( "pubDate" );News += ( "<span class='Time'>" + time[0].firstChild.nodeValue + "<\/span>" );description = items[i].getElementsByTagName( "description" );News += ( "<span class='Content'>" + description[0].firstChild.nodeValue + "<\/span>" );News += "<\/div>";}document.getElementById('News').innerHTML = News;
好了,到現在為止我們的首頁新聞就算完工了,但是既然 AjaXSLT 提供了 XSLT 的 JS 實現,我們拿來試試。先照著《XML 高級編程》寫個 XSLT:
<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform"><xsl:template match="/"><xsl:apply-templates select="http://item"/></xsl:template><xsl:template match="item"><div class="News"><xsl:apply-templates select="title"/><xsl:apply-templates select="pubDate"/><xsl:apply-templates select="description"/></div></xsl:template><xsl:template match="title"><span class="Title"><xsl:value-of select="."/></span></xsl:template><xsl:template match="pubDate"><span class="Time"><xsl:value-of select="."/></span></xsl:template><xsl:template match="description"><span class="Content"><xsl:value-of select="."/></span></xsl:template></xsl:stylesheet>
我們上面的 JavaScript 代碼還要把這個 XSLT 從服務器上取下來,之后一句話就可以搞定上面一堆 JS 代碼才能完成的工作:document.getElementById('News').innerHTML = xsltProcess( xmlParse( rss ), xmlParse( xslt ) );
OK,大功告成!注意,由于 XSLT 在轉換時使用了 XPath,所以要先包含 xpath.js 再引用 xslt.js。