描述 嵌入在你的web页面中的导航元素能够降低你的搜索引擎评价排名并且降低你的网站的响应性能。本文作者想同你一起探讨如何使用AJAX技术来解决这两个问题。
许多设计良好的web站点都包含大量的与实际内容相联系的可导航信息。用于导航的HTML标记能影响你的搜索引擎评价,而且能够改进访问者的页面下载体验感。在本文中,你会看到如何使用AJAX来创建更为集中的更快速加载的web页面。
一、 分离导航与内容
让我们使用一个例子作为开始。请考虑你现在阅读的文章,它有下列一些内容:
· 一个其上有一些预定义的到Informit的各个部分的链接的页眉。
· 在页眉下有一行,把本文放到Informit的目录结构之中(实际上,这是唯一的与目录相关的可导航元素)。
· 一个位于右首的侧栏,其中有一些连接到流行文章和推荐内容的链接。
· 一个页脚,还有一些永远不会改变的链接。
上面的"混合"很可能会影响搜索引擎索引你的数据的方式:
· 因为搜索引擎不能区别内容与导航文本,所以它们会把它们在你的页面中找到的一切进行索引。既然目录中的关键字与不相关的可导航关键字混合在一起,那么内容之间的关联大大减少。一些搜索引擎宣称,它们能够在被搜索到的页面(例如,固定的页眉和页脚)中发现重复的文本并且删除它们。但是,不要依赖这种可能性;即使它们实现了这种技术,也很可能不会一直可靠。
· web页面中的外向链接影响你的内容的页面评价。尽管这可能提高流行的文章的评价排名(因为许多页面都链接到它们),但是所有的页面都链接到的内容(例如,隐私策略)通常得到最高的页面评价-这可能不是你一直关心的问题。在我的一个web站点中,最高的评价页面是用于把消息发送到web管理员的表单-而不是你想让用户首先在Google上找到的内容。
注意
即使你不使用站点地图,你也可以通过Google的站点地图用户接口来了解一下你的页面的页面排名情况。
添加到一个web页面的可导航元素还可能影响使用低速互联网存取的用户,而如果web页面不使用DIV元素(Informit使用之)而使用表格时更是如此。在这种情况中,在把它显示给用户之前,整个表格必须被加载到一些web浏览器中。
传统地,web设计者一般都使用框架集或通过构建整套的导航架构(广泛使用JavaScript代码)来实现导航与内容的分离。这两条途径都存在其缺点;因此,许多大型网站避免使用框架集就不足为奇了。
借助于在AJAX框架中使用的技术,你可以为这一问题提供一种方案:
· 每一个web页面仅包含可导航元素和实际内容的占位符。
· 在web页面通过嵌入式框架(IFRAME)或使用XmlHttpRequest对象加载后,再装载这些可导航元素。
· 然后,可导航元素的内容被合并到web页面内容中,从而产生一个不嵌入任何帧的干净的页面。
在使用这种方法重新设计你的web页面之前,你需要考虑下列问题:
· 搜索引擎仅将看到初始的web页面。你必须确保,该页面中包含到相关页面或到一个网站地图的链接。Informit网站中文章页眉上方的导航线和在页面的文章信息部分中的链接正好可以较好地实现这一目的。
· 在他们的浏览器中禁止调用JavaScript的访问者将具有与搜索引擎相同的页面视图。你必须确保他们有受限的视图不会给你的网站的功能造成较大影响。
· 你可能想在你的页面上保留一些静态内容。例如,Informit标识和版权信息必须一直显示给所有的访问者。
当你确定好应该把你的导航结构哪些部分依附到页面上以及哪些部分应该与之分开之后,你就可以开始下一步骤了。
实现分离内容和导航的第一步是,在web页面上创建将插入可导航元素的占位符。对于每一个可导航元素的连续区域,你应该创建一个具有唯一id的独立DIV元素;这样以来,以后你可以在你的JavaScript代码中标识它。为了防止过度晃动,在创作页面期间,目录上方或右边的DIV元素的尺寸应该调整到非常接近你的可导航元素的实际大小;这样,当你使用希望的HTML代码来代替它们时内容就不会移动位置。为此,一种最巧妙的方法是把一个空的适当大小的DIV元素插入到该占位符处。
对于Informit网站来说,其页面结构已经是良好设计的,且DIV元素已经非常到位,如图1所示。
图1一篇Informit文章的页面结构 |
你仅需要从可导航DIV元素中删除内容并且插入一个空框,页眉将会出现在这里(为了简短起见,我们将忽略把公司标识和版权信息嵌入到每一个页面中的讨论)。下面是相应的代码:
<div id="header"> <div style="height: 100px; width: 100%"></div> </div> <div id="contentArticle"> <div id="firstCol"> ... article content .... </div> <div id="secondCol" ></div> </div> <div id="footer"></div> |
注意
如果你的web页面使用表格来实现所希望的页面布局,那么请不要把表格单元格重用作占位符;而把DIV元素放到表格单元格内比较好一些。
已经被从web页面中删除的可导航的元素必须被重新创建为独立的页面。你应该使用静态HTML文件来表达静态内容(这将允许缓冲内容,不管你使用什么样的web服务器)和在加载它们的web页面上创建显示基于动态元素的服务器端脚本。对于Informit来说,每一个web页面都有唯一一个文章标识符(在URL中的"p="参数);因此,你需要创建一个能够接受文章标识符并创建右边的栏目的服务器端脚本。在大多数情况中,你可以重用创建嵌入的可导航元素的服务器端代码。
在重新设计这些web页面后,接下来,你就可以实现本方案中的AJAX部分了。与通常一样,你可以使用嵌入式框架(IFRAME元素)工作,也可以选用一个XmlHttpRequest对象。
如果你关心浏览器兼容性的话,你应该使用嵌入式框架。一些老式的浏览器支持IFRAME元素,但不支持XmlHttpRequest对象。当然,使用这种方式还有如下一些理由:
· 被加载到一个IFRAME中的内容在装载的过程中被显示于浏览器中,这向终端用户显示一个可视化进程。
· 页面缓冲总是使用加载到一个IFRAME中的内容工作。一些版本的Opera还不能较好地使用XmlHttpRequest对象处理经缓冲的响应。
为此,我们可以把一个空IFRAME插入到每一个DIV容器中,并且在每一个IFRAME后添加一个简短的JavaScript语句,如下所示:
<div id="header"> <div style="height: 100px; width: 100%"></div> <iframe id="header_iframe" style="height: 0px;"></iframe> <script>loadIframe("header","/navigation/header.html")</script> </div> |
IFRAME的id应该等于以_iframe为后缀的占位符的id。loadIframe函数使用了两个参数:占位符的id和要加载到其中的URL。
技巧
如果你想在下载过程中使得IFRAME内容可见,那么你应该使用一个适当大小的IFRAME元素来替换在占位符内的空的DIV框。然而,如果你想使IFRAME保持不可见,那么你应该使用style属性来把它的高度设置为0以克服一些浏览器中的错误。
启动装载过程的loadIframe函数是很简单的:
function loadIframe(id,url) { try { var iframeObj = document.getElementById(id+"_iframe"); iframeObj.src = url ; } catch (err) { alert("cannot load "+url+" into "+id) ; } } |
注意
本文中所有示例代码都假定,浏览器兼容文档对象模型(DOM)。
然而,还没有一种机制来通知请求页面所希望的内容已经被加载到占位符IFRAME中。因此,被装载的内容必须通知父页面(经由一个JavaScript调用)可以使用该内容了。实现这一操作的最好时机是,在页面加载完成以后。因此,在IFRAME内容中的BODY标志应该包含一个onLoad事件:
<body onload="contentLoaded('header')" style="margin: 0px 0px; padding: 0px 0px"> |
技巧
加载到IFRAME中的内容的body部分应该总是有零边距和填充空白;否则,当把它集成到父页面中时,它将会轻微地迁移。
在IFRAME的上下文中执行的contentLoaded函数将提取body部分相应的HTML内容并且把它传递到一个在父页面上下文中执行的函数,此函数将使用它来填充相应的占位符:
contentLoaded在IFRAME上下文的上下文中执行:
function contentLoaded(parentID) { var myContent = document.body.innerHTML ; parent.copyContent(parentID,myContent); } |
copyContent在父web页面的上下文中执行:
function copyContent(id,content) { try { var placeholder = document.getElementById(id) ; placeholder.innerHTML = content; } catch (err) { alert("Cannot copy HTML content into "+id); } } |
现在,细心的读者应该感到疑惑,为什么这么复杂?在IFRAME元素中加载导航元素不是更简单一些吗?事实证明,对于此方法还要加一些防止误解的说明为好:
· IFRAME具有固定的高度和宽度。如果内容彼此超出,则内容将被剪掉或者IFRAME要加上滚动条。然而,被复制到一个在父页面中的DIV元素中的HTML标记其大小却总是保持自动调整大小。
· 当在一个IFRAME中时,在导航内容中的链接(一个元素)将装载IFRAME中的新页面,除非你把target="_parent"添加到每一个链接之后。
· 依附到导航元素的JavaScript事件处理器将在IFRAME的上下文中工作(如果还保留这个上下文的话)。如果你把导航内容移动主页面上,那么事件处理器能够存取在主页面中定义的函数和变量。
如果你的用户主要使用Internet Explorer的较新版本或基于Gecko的浏览器(Mozilla,Firefox,Netscape 7),那么你可以决定使用XmlHttpRequest对象来把其它内容下载到你的web页面中。第一步非常类似于前面描述的方式。对于每一个占位符,你需要一个JavaScript函数调用来启动加载过程:
<div id="header"> <div style="height: 100px; width: 100%"></div> <script>loadContent("header","/navigation/header.html")</script> </div> |
然而,loadContent函数是根本不同的:它创建了一个新的XmlHttpRequest对象,然后把一个事件处理器指派给它,并且异步启动装载过程:
function loadContent(id,url) { try { var rq = new XMLHttpRequest() ; rq.open("GET", url, true); rq.onreadystatechange = function() { contentLoaded(rq,url,id) } rq.send(null); } catch (err) { alert("cannot load "+url+" into "+id) ; } } |
注意
每一种主流浏览器家族都以一种不同的方式实现了XmlHttpRequest对象。处理这种兼容性问题的最容易的方法是,使用一个包装器库,例如Sarissa。我们在本文中示例中就使用了这种库。
回调函数contentLoaded负责检查XmlHttpRequest对象是否已经准备好及完成状态(如果请求已完成的话),并且从响应中提取HTML标记。提取HTML代码(除非你使用XHTML,这种情况下,你可以使用XMLDOM接口)的最容易的方法是,使用字符串处理函数来查找<body>和</body>标志之间的文本:
function contentLoaded(rq,url,id) { try { if (rq.readyState != 4) { return; } if (rq.status != 200) { alert("failed to load "+url); return; } var txt = rq.responseText ; //查找<body>标记的开始位置 var startBodyTag = txt.indexOf("<body") //查找<body>标记的结束,跳过任何属性 var endOfStartTag = txt.indexOf(">",startBodyTag+1) //查找</body>标记 var endBodyTag = txt.indexOf("</body") if (endBodyTag == -1) { endBodyTag = txt.length ; } //提取实际内容 var bodyContent = txt.substring(endOfStartTag+1,endBodyTag) if (bodyContent) { var placeholder = document.getElementById(id) ; placeholder.innerHTML = bodyContent; } } catch (err) { alert("cannot load "+url+" into "+id) ; } } |
与前面描述的基于IFRAME的方法相比,使用XmlHttpRequest对象具有下列好处:
· 代码更干净,并且不依赖于页面的上下文切换。
· XmlHttpRequest对象使你能够检测和处理错误(通过它的readyState和status属性)。而使用IFRAME加载内容时,如果出现错误,则只能显示非常粗略的错误提示,这主要是因为缺乏对回调函数的调用。
· 你能够实现内容元素的平行装载(如在这一节中显示的)或顺序化装载请求以最小化带宽利用。
五、 小结
在本文中,你学习了怎样实现把你的web页面内容与包围该内容的可导航元素分离开来。分离导致更为集中地描述搜索引擎要搜索的页面内容,并且也减少了用户使用低速互联网存取的加载时间(既然是在可导航元素被下载之前把实际内容显示给用户)。
当重新设计你的web页面来利用这种方案时,切记,一些基本格式的导航必须保留在页面上以便允许搜索引擎和决定禁止使用JavaScript的用户在你的网站的页面之间进行导航。
你可以使用嵌入式框架(IFRAME)或使用在最现代浏览器中实现的XmlHttpRequest对象来实现可导航元素的延迟装载。IFRAME方法能够为较老式的浏览器所支持;因此,它可能是你要考虑使用的方法-如果你非常关心向后兼容问题的话。另一方面,XmlHttpRequest对象的使用使你能够更为紧密地控制装载过程并能够检测和处理下载错误。