网络编程
位置:首页>> 网络编程>> JavaScript>> ajax标签导航实例详解教程(4)

ajax标签导航实例详解教程(4)

作者:海啸 来源:无忧脚本 发布时间:2008-02-01 10:54:00 

标签:akax,标签,导航,XMLHTTP

DOM技巧篇(DOM基础知识)

讲到这里,我们就要了解一下DOM的一些基础知识了。

DOM(Document Object Model 文档对象模型)是HTML和XML的应用程序接口(API)。DOM将把整个页面规划成有节点层级构成的文档。HTML或XML页面的每一个部分都是一个节点的衍生物。

下载:苏沈小雨htmlDOM方法中文手册chm

光说可能还不怎么好理解,那么来看看我们ajax标签导航的DOM结构吧,如图五(截取的FIXFOX中的DOM图象):


DOM通过创建树来表示文档,从而使开发着对文档的内容和结构具有空前的控制力。用DOM API可以轻松地删除,添加和替换节点。

简单的了解了什么是DOM后(要想了解更多的Javascript DOM 编程知识,推荐大家看看《Javascript DOM 编程艺术》和《Javascript高级编程》这两本书,还有到作者的网站去看看,也可以直接到W3C去查询相关信息。),来看看我们这个程序里需要用到的DOM知识吧:document.getElementsByTagName()还有之前提到的$(i)函数,它们都是做什么用的呢?

<, P>document.getElementById("DOMId"):它返回的是以ID为表示的节点,而大家都知道id在页面中是唯一的,所以getElementById是在页面中搜索DOM节点最直接的方法,很常用。不过它只能查寻单个的DOM节点,我们要查询一组怎么办呢? 我们可以使用getElementsByTagName和getElementsByName。


从方法名字中的“Elements”我们也可以知道,这两个方法返回的是一组元素(数组)

getElementsByTagName:(核心[XML]DOM)用来返回一个包含所有tagName(标签名)特性等于某个指定值的元素的NodeList。

getElementsByName:(HTML DOM)用来获取所有name特性等于指定值的元素。但是这个方法在IE6和opera7.5中支持不是很好,会有错误产生,所以(个人)建议一般不要用。

OK,在对使用DOM来查寻节点的知识有了了解后,我们再来看这段代码:

/* ===========================================================
* 函数名称:$(i)
* 参数说明:i - 目标节点名称
* 函数功能:获取指定的目标DOM节点
* 返 回 值:返回要搜索的目标DOM节点
* 使用方法:$("frmSearch")
============================================================ */
function $(i){
      if(!document.getElementById)return false;
      if(typeof i==="string"){
              if(document.getElementById && document.getElementById(i)) {
                     // W3C DOM
               return document.getElementById(i);
         }
         else if (document.all && document.all(i)) {
                // MSIE 4 DOM
               return document.all(i);
         }
         else if (document.layers && document.layers[i]) {
                // NN 4 DOM.. note: this won't find nested layers
               return document.layers[i];
         } 
         else {
               return false;
         }
      }
      else{return i;}
}

这个函数主要是来查找指定的DOM节点的,主要是通过document.getElementById()方法,但是我们又看到了document.all()和document.layers[]方法,这个就是浏览器大战时期(各个浏览器对DOM标准支持不程度不同),各大浏览器提供商制定的各自的DOM支持规范而造成的,我们的CSS HACKS其实也是由于这个原因才会出现的。扯远了,document.all()是IE浏览器(IE5以上版本) * 有的查询节点的方法,而document.layers[i]则是其他浏览器(主要是NetScape的)的浏览器特有的。我们通过$(i)函数来统一调用,从而解决了浏览器兼容的问题。

而下面这里的代码:

// DOM节点(tabs)不存在或者浏览器不支持getElementsByTagName()函数不执行
if(!tabs || !document.getElementsByTagName) return false;

这里if(!tabs || !document.getElementsByTagName) return false;这么写是很有必要的,这里是一种预留退路的思想(在《Javascript DOM 编程艺术》一书一直灌输的思想),这么写在不支持getElementsByTagName()方法时我们的函数就不会执行,在不tabs($("news")和$("sports")),不存在的时候(可能是我们把参数名写错了),函数也不执行了,从而避免了弹出或显示脚本错误的信息。下面我们还将看到这一思想的体现,不过我们还是先来看看紧接的代码:


var theList = tabs.getElementsByTagName("li"); // 搜寻导航标签(ID为tabs)里的所有li标签
var theLink = tabs.getElementsByTagName("a");  // 搜寻导航标签(ID为tabs)里的所有a标签

为什么要找出所有的li和a标签呢?呵呵,这个是由于我这里采取的设置样式的结构决定的(其实我也老是觉得不怎么好,不过这个思维个人比较好理解--头疼医头,脚疼医脚的方法)。看看我们上边的CSS样式,大家看到了,我们是用改变标签菜单(li)的样式来实现特别显示当前标签的。那么我这里就自然要获取所有的li标签,然后给他添加onclick来调用ajaxInject(ListName,tabId,tarObj,URL),从而改变标签的样式。看看我来实现这个功能的代码吧:

for(var j=0;j<theList.length;j++){
                 var theTab = theList[j];
                 if(theTab.parentNode!=tabs) continue;
                     
                 var theA = theLink[j];
                 // 屏蔽掉a标签默认的处理(打开新链接)事件
                 theA.onclick = function(){
                     return false;    
                 }
                 
                 // 为导航TAB菜单(li)设置onclick处理方法(函数)    
                 theTab.onclick = function(){
                  var theClass = this.className;
                  if(theClass!="current" && theClass!="first"){
                            var objId = this.getAttribute("id").split("-")[1]; // 当前选中标签(li)在菜单(ul)中的索引值
                            var tarObj = this.getAttribute("id").split("-")[0]; // 要显示信息的目标DOM节点ID值
                       var theURL = tarObj + "/" + tarObj + objId + ".htm"; // 要异步加载的URL地址
                         ajaxInject($(tarObj),objId,tarObj,theURL);    
                         return false;
                     }
               }
           } 

我们通过使用一个for循环来遍历所有的li和a标签(由于它们个数相同,所以索引的值相同),然后分别为它们设置onclick事件。先看我们给a标签添加的事件的代码:

var theA = theLink[j];
// 屏蔽掉a标签默认的处理(打开新链接)事件
theA.onclick = function(){
       return false;    
}

一个简单的return false,可别小看它哦,有几个作用哦,首先a标签是嵌套在li标签里的,我们点li时就一定会出发a标签的默认行为,打开链接的页面。而我们ajax标签导航就是希望不刷(打开)新页面,在指定的DOM节点显示信息。当然就不能让a标签的默认行为启动了,而简单的一个return false;就解决了这个问题。哦,这里还有个问题就是DOM事件的冒泡的顺序,详细的介绍大家可以在《Javascript高级编程》一书中查到。

那有些朋友会问,为什么要在里面加个a标签呢?反正你是改变的是li的样式,点了li,改变li的样式,然后刷新指定DOM节点的信息不就完成了ajax标签导航的功能(效果)了吗?

是啊,不过我在这里要提的就是刚才提到的一个预留退路的思想,如果想上面说的那样做了,当然是没有什么问题,但前提是用户的浏览器支持javascript脚本,或者说用户打开了执行脚本的权限。一旦用户的浏览器不支持javascript或者出于安全原因关闭了脚本执行功能。这个时候,当用户点li时是没有任何反映的。而我这里的处理就考虑到了当javascript执行不了的情况,这时候,用户点链接就可以打开我们原本要用ajax加载的内容了。

其实这里还有各个比较简单的方法来达成我提到的相同的效果,就是对li使用onmouseover事件,想想为什么?因为只要鼠标划过时,就触发了ajaxInject($(tarObj),objId,tarObj,theURL);改变了标签的样式,刷新了内容。当点击链接时,就可以弹出页面了。现在网易和雅虎中国就是这么处理的。其实我这么做主要是处于个人习惯,比较喜欢用onclick,还有就是这里有个分隔线的效果,看上去比网易的只用一个背景图片酷,当然我的效果是学的雅虎(不是雅虎中国)的。不过雅虎的标签样式的处理方式要比我现在的更巧妙,下次有时间再跟大家分析下雅虎的标签导航效果。

又扯远了,OK,接下来我们就是要改变样式和ajax刷新内容了。不过在这个之前,看看我做了什么准备。

<ul class="tabs" id="news">
         <li class="first" id="news-0"><a href="news/news0.htm">网站重构</a><span></span></li>
         <li id="news-1"><a href="news/news1.htm">CSS布局实录</a><span></span></li>
         <li id="news-2"><a href="news/news2.htm">海啸的地盘</a><span></span></li>
         <li id="news-3"><a href="news/news3.htm">Ajax高级编程</a><span></span></li>
</ul>

var objId = this.getAttribute("id").split("-")[1]; // 当前选中标签(li)在菜单(ul)中的索引值
var tarObj = this.getAttribute("id").split("-")[0]; // 要显示信息的目标DOM节点ID值
var theURL = tarObj + "/" + tarObj + objId + ".htm"; // 要异步加载的URL地址

看看我文章前面部分罗列的东西,现在就在这里有了回应了

id="news"      - news就是我们的导航标签的ID;
id="news-0"    - news-0 通过”-“分开,我们就分别可以得到news(导航标签ID),0(标签[li]在导航标签中的索引值)

好现在就要改变标签的样式了

/* ===========================================================
* 函数名称:ajaxInject(ListName,tabId,tarObj,URL)
* 参数说明:ListName - 标签菜单DOM节点ID
*           tabId - 选中的标签(在ListName中的)索引值
*           tarObj - 要显示返回信息的目标DOM节点ID值
*           URL - 要异步处理的URL地址
* 函数功能:设置当前选中标签(li)的样式,
*           将返回信息写到指定DOM节点中。
* 返 回 值:无
* 使用方法:ajaxInject($(tarObj),objId,tarObj,theURL);
============================================================ */
function ajaxInject(ListName,tabId,tarObj,URL){
    if(!ListName || !document.getElementsByTagName) return false;
      var Tabs = ListName;
      var theLi = Tabs.getElementsByTagName("li");
      for(var i=0;i<theLi.length;i++){
            // 设置当前选中标签的样式
          if(i==tabId){
                if(i==0){
                     theLi[tabId].className = "first"; // 当选中第一项的样式
                }
                else{// 
                     theLi[tabId].className = "current"; // 选中其他项的样式
                }     
                var msgBox = tarObj+"Cnt"; 
                var loadstatustext="<div class='loading'><img src='img/loading.gif' alt='正在加载内容, 请稍候...' />正在加载内容, 请稍候...</div>";      
              $(msgBox).innerHTML = loadstatustext; // 加载信息时的提示信息
              var para = "?d=" + Math.random(); // URL后的参数,接Math.random()(一个随机数),目的是处理ajax的缓存问题
              var myAjax = ajaxUpdater(msgBox,"get",URL,para);
          }
          else{// 设置其他标签的样式
              theLi[i].className = "";
              if(tabId!=0){
                    theLi[tabId-1].className = "off"; // 当不是第一项时,隐藏选中项的前一项的分隔标签
                 }  
          }    
      }
}       

这里又跟前面的

id="newsCnt"   - newsCnt就是我们要写入信息的目标DOM节点;
class="first"  - first当前(第一个)标签的样式;

对应起来了。

for(var i=0;i<theLi.length;i++){
            // 设置当前选中标签的样式
          if(i==tabId){
                if(i==0){
                     theLi[tabId].className = "first"; // 当选中第一项的样式
                }
                else{
                     theLi[tabId].className = "current"; // 选中其他项的样式
                }
            }    
            else{// 设置其他标签的样式
              theLi[i].className = "";
              if(tabId!=0){
                    theLi[tabId-1].className = "off"; // 当不是第一项时,隐藏选中项的前一项的分隔标签
                 }   
          }  
}    

上面这段代码就是具体改变样式的,i==tabId比较当前标签的索引值(作用就是确认是否是选中的标签),相等了就给标签设置样式了。i==0表明第一项被选种(由于我的第一项的背景特殊的)给它加上“first”样式,其余项被选中则加上“current”样式。

接这就是处理分隔钱的样式了,跟设置背景大同小意,这里要说的是我们在写CSS的时候要把li(选中和失去焦点)的样式设置好。还是我之前提到的,YAHOO的做得很好,我记得网上也有关于滑动门技术CSS写法的介绍,大家可以看看是怎么来设置样式的,还有这里给大家推荐个小软件《CSS Tab Designer 2》。

恩,现在要刷新指定DOM节点的内容了,用一个简单的var myAjax = ajaxUpdater(msgBox,"get",URL,para);就解决问题了。不过我们看看在把信息写到指定DOM节点前,我做了什么:

var msgBox = tarObj+"Cnt"; 
var loadstatustext="<div class='loading'><img src='img/loading.gif' alt='正在加载内容, 请稍候...' />正在加载内容, 请稍候...</div>";      
$(msgBox).innerHTML = loadstatustext; // 加载信息时的提示信息

这里做了一个提示的处理,因为ajax是异步的加载,在获取很长(信息量很大)的内容时,会有一个延时,如果在这个期间不给任何提示信息的话,我们要刷新的DOM节点会出现白白的一片,这个当然是不美观的。所以在这之前,我们给用户一个提示信息,说明正在加载信息会显得更人性化些。当然,大家知道的网易的处理会更好些,做一个延迟的window.setTimeout效果。

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com