Blog外挂之:热门文章与导航栏
By 刘未鹏
C++的罗浮宫(http://blog.csdn.net/pongba)
还是看图说话:-)
图一:导航栏
图二:热门文章
导航栏的实现很容易:往main和header之间塞入一个div,往div里面写入所有的分类链接即可。
分类的获取有几个办法,一是把分类链接也收藏到del.icio.us上,这样就可以用JSON协议获取到所有分类(见上篇文章——《Blog外挂之:妙用del.icio.us实现“站内相关文章”》)。二是直接获取侧边栏上的分类列表。dodo的blog上用的就是这个办法,但这个办法太脆弱,因为分类的那个ul没有id,所以用getElementById就行不通了。只能依赖于对那个ul的顺序的掌握,先getElementById(‘rightmenu’)获得rightmenu的引用,然后再对后者getElementsByTagName(‘ul’)获得其中所有的ul容器的数组,然后基于对页面代码的观察从这个数组中取出对应于分类菜单的ul。这个办法的问题在于如果我们不小心在公告栏加入了新的ul,或者能生成ul的js代码,就会破坏分类那个ul所在的序号。另一方面,把分类链接收藏到del.icio.us上是个较好的选择,但del.icio.us帐号的空间有限,目前你最多能获取100个post,还是别浪费在分类上的好。用另一个del.icio.us帐号的办法又会增加一个http请求。想想还是不要杀鸡用牛刀了。于是结果使用的是brute force,直接把分类链接及标题硬编码到代码中了:-)反正分类的修改也并不频繁。比如我的分类之前都快一年没改动了。
导航栏实现的主要代码如下:
function insertTopMenuDiv()
{
var topMenuDiv = document.createElement('div');
topMenuDiv.setAttribute('id', 'topMenu');
var main = document.getElementById('main');
main.parentNode.insertBefore(topMenuDiv, main);
return topMenuDiv;
}
function appendNavigatorBar()
{
var topMenuDiv = insertTopMenuDiv();
var categoryList = getCategoryList();
var html = '';
for(var i = 0; i < categoryList.length; ++i){
html += '<a href = "' + categoryList[i].url + '" class = "tab ';
if(GetThisURL() != categoryList[i].url)
html += 'tabUnselected">';
else
html += 'tabSelected">';
html += categoryList[i].title;
html += '</a>';
}
document.getElementById('topMenu').innerHTML = html;
}
getCategoryList你自己写。目的是获取分类列表。我用的是硬编码。
CSS调整
导航栏就这样插入到main和header之间是不行的,需要调整格式。往后台定制CSS栏加入代码调整其位置,我的是:
#topMenu {
position:absolute;
top: 78px;
padding-left: 20px;
}
#topMenu a:hover {
background: #0044BB none repeat scroll 0%;
color: #FFFFFF;
text-decoration: none;
}
.tabUnselected {
border-left:1px solid #CCCCCC;
border-right:1px solid #CCCCCC;
}
.tab {
margin:0em 0em 0em 0.25em;
padding:2px;
font-size:
}
.tabSelected {
border-left:1px solid #CCCCCC;
border-right:1px solid #CCCCCC;
}
最重要的就是topMenu的位置。我定的是离顶部78px。你自己用firebug调节吧。tabSelected和tabUnselected的CSS一样是因为原本的效果不能在IE和firefox下统一,只好暂时就这样了。Web设计达人自己调整就是了:-)俺是程序员,只能朴素一点,用左右两条线分隔了。
热门文章的实现也不算复杂。最关键的问题是如何获取相应文章的评论数。我原本想通过blog的rss文件获得,观察rss文件会发现里面包含了每篇文章的评论数。等等,我说了每篇文章吗?唉,其实只有聊聊十几篇而已,所以没法对整个blog上的所有文章进行排名了。不过rss可以用来实现最近更新。
图三:最近更新
Anyway,那如何获取评论数呢?我们知道每篇文章的URL(从del.icio.us上获得的),问题转化为如何从URL到评论数。CSDN blog对文章的评论数是动态加载的:
图四:加载中…
图五:加载完毕
既然是动态加载的,那么必然有办法获取对应文章的评论数。偷窥一下页面代码:
图六:评论加载代码(一)
可见,每篇文章的postFoot处都有一个js调用,调用AddFeedbackCountStack将该文章的一个ID添加一下,这个ID其实就是文章URL的最后一部分。从数字大小猜测它应该是服务器上的文章编号,1601519,也就是第一百多万篇文章。随便找篇03年的文章,你会发现那时候CSDN blog系统上只有一万多篇文章。
为了节省HTTP请求,CSDN blog的页面加载过程中并非每遇到一个“评论数(…)”框都发起一次http调用获取相应文章的评论数,而是用刚才你看到的AddFeedbackCountStack先将该文章的ID保存起来,等到最后一并获取所有的评论数:
图七:评论加载代码(二)
LoadFeedbackCount做了两件事情:先使用一路保存下来的文章ID列表来获取一个评论数列表。然后将评论数一一填充到相应的“评论数(…)”框中。如果一篇文章的ID为1234那么其评论数框的span id就是FeedbackCount_1234。见图六。
AddFeedbackCountStack和LoadFeedbackCount的代码在LoadFeedbackCount.js中。自己看它是怎么发起http请求获取评论数的吧。
最后需要注意的一点是,无论ID列表里面文章的顺序如何,返回的评论数列表总是对应于ID从大到小来排列的。也就是说如果你给出的ID列表为“ID2,ID
知道了如何获取评论数就好办了,有了URL列表,有了相应的评论数。排名那就太简单了。
最热门文章排行的主要代码如下:
var getFeedbackCountReq;
var myBlogPosts = [];
var hotPostsContainer;
function showHotPosts(ct)
{
hotPostsContainer = ct;
for(i = 0; i < Delicious.posts.length; ++i){
myBlogPosts.push(
{url:Delicious.posts[i].u,
id:urlToId(Delicious.posts[i].u),
title:Delicious.posts[i].d, feedbackCount:0}
);
}
myBlogPosts.sort(compareById);
var idList = '';
for(i = 0; i < myBlogPosts.length; ++i){
idList += myBlogPosts[i].id + ',';
}
getFeedbackCountReq = HTTP.newRequest();
getFeedbackCountReq.onreadystatechange =
handleGetFeedbackCountReq;
getFeedbackCountReq.open('GET',
'/NewCount.aspx?FeedbackCountStack=' + idList);
getFeedbackCountReq.send(null);
}
function handleGetFeedbackCountReq()
{
if(getFeedbackCountReq.readyState==4){
if(getFeedbackCountReq.status==200){
handleGetFeedbackCountReq2(
getFeedbackCountReq.responseText);
}
}
}
function handleGetFeedbackCountReq2(feedbackStr)
{
var feedbackList = feedbackStr.split(',');
var i;
for(i = 0; i < myBlogPosts.length; ++i){
myBlogPosts[i].feedbackCount = feedbackList[i];
}
myBlogPosts.sort(compareByFeedbackCount);
var title = document.createElement('h3');
title.appendChild(document.createTextNode('本blog评论最多文章'));
hotPostsContainer.appendChild(title);
var NGList =
['http://blog.csdn.net/pongba/archive/
'http://blog.csdn.net/pongba/archive/
'http://blog.csdn.net/pongba/archive/
var maxNumHotPosts = 12 + NGList.length;
if(myBlogPosts.length < maxNumHotPosts)
maxNumHotPosts = myBlogPosts.length;
var ul = document.createElement('ul');
ul.setAttribute('id', 'my_hot_posts');
for(i = 0; i < maxNumHotPosts; ++i){
if(IsIn(myBlogPosts[i].url, NGList))continue;
var li = document.createElement('li');
var a = document.createElement('a');
a.setAttribute('href', myBlogPosts[i].url);
a.appendChild(document.createTextNode(myBlogPosts[i].title
+ '(' + myBlogPosts[i].feedbackCount + ')'));
li.appendChild(a);
ul.appendChild(li);
}
hotPostsContainer.appendChild(ul);
}
function compareByFeedbackCount(post1, post2)
{
return post2.feedbackCount - post1.feedbackCount;
}
function compareById(post1, post2){
return post2.id - post1.id;
}
其中NGList是排除在外的文章列表。如果你blog上也有一些你觉得价值不大的口水文章也可以排除一下:-)
Further Work
按评论数排行是最平凡的热门文章排行方式。揉合进点击率,tag等信息会是一个不错的选择。但似乎点击率没法获取,tag信息倒是有,但目前也没想到较好的利用办法。Tag越小文章越特殊是一个观察,但未必特殊的文章就一定重要。所以如果要加入文章的重要性,还不如直接利用del.icio.us手动收藏重要性文章。