实现漂亮的链接提示效果,仿http://www.live.com/

最近在学Ajax,想起老师给我们演示的http://www.live.com/里面的一些效果,其中很喜欢这个链接提示效果,不禁思索着怎么实现这种效果。怀着好奇心开始查看它的源代码,可是看了半天还是找不到哪一段代码是实现这个效果的,只好自己尝试着做一下。

一、             思路分析

首先要把文章标题列表显示出来,其中包括了“更多”这个链接以及ID这个参数。

接下来通过javascript检查Dom的操作把几个“更多”找出来,并对这几个“更多”增加鼠标经过和鼠标移开事件。在事件处理里就是对 提示框 的操作。其中这个提示框的外观应该是先用CSS定义好的。我们所要处理的,就是这个提示框的可视化位置以及内容。对于位置,需要先获取当前“更多”的位置,然后再改变提示框的位置。而内容,首先要接收一个ID,然后根据这个ID发送到服务器并返回文章正文。
二、             功能实现

1、 提示框的外观

箭头可以用图像处理软件做出来,作为背景图片,调整一下位置就行了。

而阴影的效果是用相对定位来实现,在内容层以上增加一个层,作为,阴影层。所以阴影层是做为父元素的,可以这样理解,内容层本来是刚刚好填充满阴影层的,加上top:-3px;left:-3px(也就是向上和左移动3个像素),就露出了一部分阴影层,加上阴影层的背景颜色,就出现了阴影的效果。下面是HTML以及CSS

代码如下:

<style type="text/css">
#LinkPanel
{position:absolute;z-index:100; font-size:12px; visibility:hidden; }
#LinkPanel .arrowhead
{ position:relative; top:30px; left:0px; width:20px; height:39px; background:url(../images/arrowhead_black.gif) left top no-repeat; float:left;padding:0px;margin:0px; }
#LinkPanel .shadow
{ background:#ccc;float:left; }
#LinkPanel .maincontent
{ width:400px;position:relative; top:-3px; left:-3px; background:#fff;border:1px solid #000;padding:5px; overflow:hidden;margin:0px;}
#LinkPanel .title
{ font-weight:bold; border-bottom:1px dashed #d3d3d3; padding-bottom:10px;}
#LinkPanel .content
{width:390px; overflow:auto;max-height:420px;height:420px;}
#LinkPanel .links
{ background:#EDF2FB; padding:10px; width:390px;}
#LinkPanel .links a
{ display:block; color:#0066A7;}
#linkPanel .links a:hover
{color:#0088E4; }
</style>
<div id="LinkPanel">
<div class='arrowhead'></div>
    
<div class='shadow'>
        
<div class='maincontent'><div class='title'></div>
            
<div class='content'>正在读取数据中</div>
            
<div class='links'>
                
<target='_blank' href=''>查看完整内容</a>
            
</div>
        
</div>
    
</div>
</div>

 2、 显示标题以及“更多”链接

数据库用Access,建一个article表,里面有id,title,content,posttimecategoryId等字段。

HTML页写上一个要显示内容的层,IDListPanel,然后就可以在javascript中用var List=document.getElementById("ListPanel")来获取要显示内容的层,用List.innerHTML来插入元素的内容。内容来自数据库,所以需要用到Ajax,先创建一个xmlhttprequest对象,然后发送一个请求到aspx页面,并返回一个JSON文本。在客户端用Eval(xmlhttp.responseText)就可以得到相应的对象,也就是article对象,有idtitle属性。

接下来可以用循环语句来遍历article里的每一条记录,并储存起来。最后赋值给List.innerHTML

 

//返回创建的XMLHttpRequest对象

function createxmlHttp()

{

     
var xmlHttp= false;

     
if(window.ActiveXObject)    

     
{

         xmlHttp
= new ActiveXObject("Microsoft.XMLHTTP");

     }


     
else if(window.XMLHttpRequest)

     
{

         xmlHttp
=new XMLHttpRequest();

     }


     
return xmlHttp;

}


//全局变量,获取文章列表的xmlhttprequest对象

var List_xmlhttp;

//获取文章列表数据函数,categoryId为分类ID,不填时获取全部文章

function GetList(categoryId)

{

    List_xmlhttp
=createxmlHttp();//创建XMLHttpRequest对象

    
var url;

    
if(categoryId==null)

        url
="Handler.aspx";

    
else

        url
="Handler.aspx?categoryId="+categoryId;

    List_xmlhttp.open(
"POST",url,true);

    List_xmlhttp.onreadystatechange
=ReturnList;

    List_xmlhttp.send(
null);

}


//readyState属性改变时的处理函数

function ReturnList()

{

    
var img="<ul><li class='li_img'><img src='images/ajax_loader.gif'/>请稍等,数据正在读取中</li></ul>";

    
var List=document.getElementById("ListPanel");

    
if(List_xmlhttp.readyState==1)//判断对象状态-初始化

    
{   

        List.innerHTML
=img;

    }
 

    
else if(List_xmlhttp.readyState==4)//判断对象状态-完成

    
{

        
if(List_xmlhttp.status==200)//信息成功返回,开始处理信息

        
{

              eval(List_xmlhttp.responseText);
//利用JSON获取article对象

              
var i;

              
var HTML;

              HTML
="<ul>";

              
//遍历article对象

              
for(i=0;i<article.length;i++)

                 HTML
+="<li><a class='title' href='#'>"+article[i].title+"</a> <a class='more'>[更多]</a></li>";

              HTML
+="</ul>";

              List.innerHTML
=HTML;//把数据填冲到文章列表区块

              InitLinkPanle();
//初始化提示框效果

              

            

        }


        
else

        
{

            List.innerHTML
="对不起,操作超时";

        }


    }


    

}

Handler.aspx.cs

using System;

using System.Web;

using System.Data.OleDb;

using System.Text;

public partial class Handler : System.Web.UI.Page

{

    
protected void Page_Load(object sender, EventArgs e)

    
{

        
if (string.IsNullOrEmpty(Request.QueryString["type"]))

            GetList();

        
else if(Request.QueryString["type"]=="article")

            GetArticle();

    }


 

 

    
private void GetList()

    
{

        

        OleDbConnection conn 
= new OleDbConnection("data source=" + HttpContext.Current.Server.MapPath("~/App_Data/db.mdb").ToString() + ";provider=microsoft.jet.oledb.4.0");

        conn.Open();

        
string sqlstr="";

        
if(string.IsNullOrEmpty(Request.QueryString["categoryId"]))

            sqlstr
="select top 10 id,title,posttime from article";

        
else

            sqlstr 
= "select top 10 id,title from article where categoryId=" + Request.QueryString["categoryId"];

        OleDbCommand cmd 
= new OleDbCommand(sqlstr, conn);

        OleDbDataReader dr 
= cmd.ExecuteReader();

        
string JSONText = GetJSON(dr, "article");

        Response.Write(JSONText);

        conn.Close();

        Response.End();

    }


    
private void GetArticle()

    
{

        OleDbConnection conn 
= new OleDbConnection("data source=" + HttpContext.Current.Server.MapPath("~/App_Data/db.mdb").ToString() + ";provider=microsoft.jet.oledb.4.0");

        conn.Open();

        OleDbCommand cmd 
= new OleDbCommand("select content from article where id="+Request.QueryString["id"], conn);

 

        Response.Write(cmd.ExecuteScalar().ToString());

        conn.Close();

        Response.End();

    }


 

    
private static string GetJSON(OleDbDataReader drValue, string objName)

    
{

        StringBuilder sb 
= new StringBuilder();

        sb.AppendLine(
"{");

        sb.Append(objName);

        sb.AppendLine(
"=[");

        
try

        
{

            
while (drValue.Read())

            
{

                sb.Append(
"{");

                
for (int i = 0; i < drValue.FieldCount; i++)

                
{

                    sb.AppendFormat(
"\"{0}\":\"{1}\",", drValue.GetName(i), drValue.GetValue(i));

                }


                sb.Remove(sb.ToString().LastIndexOf(
','), 1);

                sb.AppendLine(
"},");

            }


            sb.Remove(sb.ToString().LastIndexOf(
','), 1);

 

        }


        
catch

        
{

        }


        
finally

        
{

            drValue.Close();

        }


        sb.AppendLine(
" ]");

        sb.AppendLine(
" }");

        
return sb.ToString();

    }


}


 3、 通过javascript获取所有的“更多”

ListPanel填冲完内容后,ListPanel就可以得给下面这样的代码

<div id="ListPanel">
    
<ul>
        
<li><class='title' href='#'> ArticleTitle</a> <class='more'>[更多]</a></li>
        
    
</ul>
</div>

这个时候如果想获取“更多”,可以先获取列表
<ul>的列表项<li>的集合,接着遍历这个列表项集合,并从各个列表项中找到第二个链接,也就是“更多”这个链接,对其添加鼠标经过和鼠标移开事
//提示框效果初始化

function InitLinkPanle()

{  

var i;

//获取列表的集合

    
var liList=document.getElementById("ListPanel").getElementsByTagName("ul")[0].getElementsByTagName("li"); 

    
for(i=0;i<liList.length;i++)//遍历列表

    
{

        
var link=liList[i].getElementsByTagName("a")[1];//获取第2个链接,也就是"更多"链接

        link.id
=article[i].id;//利用JSON获取article对象的id并赋值给link的id属性,用于后面的使用

        link.title
=article[i].title;//利用JSON获取article对象的title并赋值给link的title属性,用于后面的使用

        
//为"更多"增加一个鼠标经过事件

        link.onmouseover
=function(){  //这里是对提示框的处理语句 };

     }


}



4、提示框位置、可视化

要设置提示框的位置首先要获取触发对象,也就是所在“更多”链接的位置。用getElementPos()函数

function getElementPos(elementId) {

 
var ua = navigator.userAgent.toLowerCase();

 
var isOpera = (ua.indexOf('opera'!= -1);

 
var isIE = (ua.indexOf('msie'!= -1 && !isOpera); // not opera spoof

// var el = document.getElementById(elementId);

var el = elementId;

 
if(el.parentNode === null || el.style.display == 'none'{

  
return false;

 }
      

 
var parent = null;

 
var pos = [];     

 
var box;     

 
if(el.getBoundingClientRect)    //IE

 
{         

  box 
= el.getBoundingClientRect();

  
var scrollTop = Math.max(document.documentElement.scrollTop, document.body.scrollTop);

  
var scrollLeft = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);

  
return {x:box.left + scrollLeft, y:box.top + scrollTop};

 }
else if(document.getBoxObjectFor)    // gecko    

 
{

  box 
= document.getBoxObjectFor(el); 

  
var borderLeft = (el.style.borderLeftWidth)?parseInt(el.style.borderLeftWidth):0

  
var borderTop = (el.style.borderTopWidth)?parseInt(el.style.borderTopWidth):0

  pos 
= [box.x - borderLeft, box.y - borderTop];

 }
 else    // safari & opera    

 
{

  pos 
= [el.offsetLeft, el.offsetTop];  

  parent 
= el.offsetParent;     

  
if (parent != el) 

   
while (parent) {  

    pos[
0+= parent.offsetLeft; 

    pos[
1+= parent.offsetTop; 

    parent 
= parent.offsetParent;

   }
  

  }
   

  
if (ua.indexOf('opera'!= -1 || ( ua.indexOf('safari'!= -1 && el.style.position == 'absolute' )) 

   pos[
0-= document.body.offsetLeft;

   pos[
1-= document.body.offsetTop;         

  }
    

 }
              

 
if (el.parentNode) 

    parent 
= el.parentNode;

   }
 else {

    parent 
= null;

   }


 
while (parent && parent.tagName != 'BODY' && parent.tagName != 'HTML'// account for any scrolled ancestors

  pos[
0-= parent.scrollLeft;

  pos[
1-= parent.scrollTop;

  
if (parent.parentNode) {

   parent 
= parent.parentNode;

  }
 else {

   parent 
= null;

  }


 }


 
return {x:pos[0], y:pos[1]};

}


如果使用了jquery,可以用里面的offset()方法。

获取了“更多”的位置后设置一下提示框的相对偏移就行了。 

 5、 提示框的内容

link.onmouseover=function()

{

 

    
var width = this.offsetWidth;//获取当前元素(也就是"更多")的宽度

    
var height=this.offsetHeight;//获取当前元素的高度

    
var pos=getElementPos(this);//获取当前对象的坐标

    pos.x
=pos.x+width;

    pos.y
=pos.y+height/2;

    
var LinkPanel=document.getElementById("LinkPanel");

    LinkPanel.style.top 
= pos.y-50+"px";//设置提示框的Y坐标

LinkPanel.style.left 
= pos.x-2+"px";//设置提示框的X坐标

GetArticleById(
this.id,this.title); //提示框内容

LinkPanel.style.visibility
="visible";//提示框可视化

}
;

这里也需要用到Ajax,因为只是返回某一篇文章的Content字段,所以没必要用到JSON

link.onmouseover=function(){}里面增加一条语句:GetArticleById(this.id,this.title);  //获取提示框内容

function GetArticleById(id,title)

{

    
var art_xmlhttp=createxmlHttp();//创建xmlhttprequest对象

    
var url="Handler.aspx?type=article&id="+id;

    art_xmlhttp.open(
"POST",url,true);

    art_xmlhttp.onreadystatechange
=function(){

             
if(art_xmlhttp.readyState==1)

                document.getElementById(
"LinkPanel").innerHTML="<div class='arrowhead'></div><div class='shadow'><div class='maincontent'><div class='title'>"+title+"</div><div class='content'>正在读取数据中</div><div class='links'><a href='view.aspx?id="+id+"'>查看完整内容</a></div></div></div>"

             
else if(art_xmlhttp.readyState==4)

             
{

                
if(art_xmlhttp.status==200)

                
{

                    document.getElementById(
"LinkPanel").innerHTML="<div class='arrowhead'></div><div class='shadow'><div class='maincontent'><div class='title'>"+title+"</div><div class='content'>"+art_xmlhttp.responseText+"</div><div class='links'><a href='view.aspx?id="+id+"'>查看完整内容</a></div></div></div>";

                }


                
else

                    document.getElementById(
"LinkPanel").innerHTML="<div class='arrowhead'></div><div class='shadow'><div class='maincontent'><div class='title'>"+title+"</div><div class='content'>对不起,操作超时</div><div class='links'><a href='view.aspx?id='"+id+"'>查看完整内容</a></div></div></div>";

             }


             

    }
;

    art_xmlhttp.send(
null);

}


 6、 文章分类

上面的做好了,接下来文章分类就很容易实现

Default.htm

<html>

<head>

    
<title>实现漂亮的链接提示效果,仿www.live.com</title>

    
<link href="style/css.css" rel="stylesheet" type="text/css" />

</head>

<body>

<div id="LinkPanel"></div>

<br /><br />

<div id="MainPanel">

<div id="ListPanel">

    
<ul>

        
<li><class='title' href='#'>article[i].title</a> <class='more' name='more'>[更多]</a></li>

    
</ul>

</div>

<div id="category"> 文章分类:<a>全部</a> | <a>分类一</a> | <a>分类二</a> </div>

</div>

<script language="JavaScript" type="text/javascript" src="js/js.js"></script>

</body>

</html>

 

//文章分类初始化

function InitCategory()

{

    

    
var categorylinks=document.getElementById("category").getElementsByTagName("a");

    categorylinks[
0].onclick=function(){GetList();this.style.color="#0093BB";categorylinks[1].style.color="#333333";categorylinks[2].style.color="#333333";};//全部

    categorylinks[
1].onclick=function(){GetList(1);this.style.color="#0093BB";categorylinks[2].style.color="#333333";categorylinks[0].style.color="#333333";};//分类一

    categorylinks[
2].onclick=function(){GetList(2);this.style.color="#0093BB";categorylinks[0].style.color="#333333";categorylinks[1].style.color="#333333";};//分类二

}


//初始化页面

function InitPage()

{

    GetList();
//获取全部文章列表

    InitCategory();
//文章分类初始化

}


InitPage();

演示地址:http://www.leoxu.cn/demo/linktooptip/default.htm
源码下载:
http://user.qzone.qq.com/175932810/blog/1210213256