投递文章投递文章 投稿指南投稿指南 RSS订阅RSS订阅

Ajax基础教程(阿斯利森版)第三章

来源:CSDN 发布时间:2008-05-15 收藏 投稿 字体:【

3.1.3  使用W3C DOM动态编辑页面

Web最初只是作为媒介向各处分发静态的文本文档,如今它本身已经发展为一个应用开发平台。遗留的企业系统通常通过纯文本的终端部署,或者作为客户—服务器应用部署,这些遗留系统正在被完全通过Web浏览器部署的系统所取代。

随着最终用户越来越习惯于使用基于Web的应用,他们开始有了新的要求,需要一种更丰富的用户体验。用户不再满足于完全页面刷新,即每次在页面上编辑一些数据时页面都会完全刷新。他们想立即看到结果,而不是坐等与服务器完成完整的往返通信。

你已经了解了解析服务器发送的XML消息是多么容易。W3C DOM提供了一些属性和方法,使你能轻松地遍历XML结构,并抽取所需的数据。

前面的例子对于服务器发送的XML响应并没有做多少有用的事情。在警告框中显示XML文档的值没有太大的实际意义。你真正想做到的是让用户享有丰富的客户体验,不再遭遇一般Web应用中常见的连续页面刷新问题。页面连续刷新不仅使用户不满意,还会浪费服务器上宝贵的处理器时间,因为页面刷新需要重新构建整个页面的内容,而且会不必要地使用网络带宽来传送刷新的页面。

当然,最好的解决办法是根据需要修改页面上已有的内容。如果页面上大多数数据没有改变,则不应刷新整个页面,只需要修改页面中信息有变化的部分。

以往,在Web浏览器的限制之下,这一点很难做到。浏览器只是一个工具,它解释特殊的标记(HTML),并根据一组预定的规则显示这些标记。Web以及Web浏览器原来只是为了显示静态的信息,如果不以新页面的形式从服务器请求新的数据,这些信息不会改变。

除了一些例外情况,当前的浏览器都使用W3C DOM来表示Web页面的内容。这样做可以确保在不同的浏览器上Web页面会以同样的方式呈现,同时在不同的浏览器上,用于修改页面内容的脚本也会有相同的表现。Web浏览器的W3C DOMJavaScript实现越来越成熟,这大大简化了在浏览器上动态创建内容的任务。原来总是要苦心积虑地解决浏览器间的不兼容性,如今这已经不太需要。表3-3列出了用于动态创建内容的DOM属性和方法。

3-3  动态创建内容时所用的W3C DOM属性和方法

属性/方法

描述

document.createElement(tagName)

文档对象上的createElement方法可以创建由tagName指定的元素。如果以串div作为方法参数,就会生成一个div元素

document.createTextNode(text)

文档对象的createTextNode方法会创建一个包含静态文本的节点

<element>.appendChild(childNode)

appendChild方法将指定的节点增加到当前元素的子节点列表(作为一个新的子节点)。例如,可以增加一个option元素,作为select元素的子节点

<element>.getAttribute(name)

<element>.setAttribute(name, value)

这些方法分别获得和设置元素中name属性的值

<element>.insertBefore(newNode, targetNode)

这个方法将节点newNode作为当前元素的子节点插到targetNode元素前面

<element>.removeAttribute(name)

这个方法从元素中删除属性name

<element>.removeChild(childNode)

这个方法从元素中删除子元素childNode

<element>.replaceChild(newNode, oldNode)

这个方法将节点oldNode替换为节点newNode

<element>.hasChildnodes()

这个方法返回一个布尔值,指示元素是否有子元素

关于浏览器的不兼容性

尽管当前Web浏览器中W3C DOMJavaScript的实现在不断改进,但还是存在一些特异性和不兼容性,这使得应用DOMJavaScript进行开发时很是头疼。

IEW3C DOMJavaScript实现最受限制。2000年初,一些统计称IE占据了整个浏览器市场95%的份额,由于没有竞争压力,Microsoft决定不完全实现各个Web标准。

这些特异问题大多都能得到解决,不过这样做会让脚本更是混乱不堪而且不合标准。例如,如果使用appendChild<tr>元素直接增加到<table>中,则在IE中这一行并不出现,但在其他浏览器中却会显示出来。对此的解决之道是,将<tr>元素增加到表的<tbody>元素中,这种解决办法在所有浏览器中都能正确工作。

关于setAttribute方法,IE也有麻烦。IE不能使用setAttribute正确地设置class属性。对此有一个跨浏览器的解决方法,即同时使用setAttribute("class", "new- ClassName") setAttribute("className","newClassName")。另外,在IE中不能使用setAttribute设置style属性。最能保证浏览器兼容的技术不是<element>.setA-
ttribute("style, "font-weight:bold;")
,而是<element>.style.cssText = "font
- weight:bold;"

本书中的例子会尽可能地遵循W3C DOMJavaScript标准,不过如果必须确保大多数当前浏览器的兼容性,可能也会稍稍偏离标准。

下面的例子展示了如何使用W3C DOMJavaScript来动态创建内容。这个例子是假想的房地产清单搜索引擎,点击表单上的Search(搜索)按钮,会使用XMLHttpRequest对象以XML格式获取结果。使用JavaScript处理响应XML,从而生成一个表,其中列出搜索到的结果(见图3-3)。

3-3  使用W3C DOM方法和JavaScript动态创建搜索结果

服务器返回的XML很简单(见代码清单3-5)。根节点properties包含了得到的所有property元素。每个property元素包含3个子元素:addresspricecomments

代码清单3-5  dynamicContent.xml

<?xml version="1.0" encoding="UTF-8"?>

<properties>

    <property>

        <address>812 Gwyn Ave</address>

        <price>$100,000</price>

        <comments>Quiet, serene neighborhood</comments>

    </property>

    <property>

        <address>3308 James Ave S</address>

        <price>$110,000</price>

        <comments>Close to schools, shopping, entertainment</comments>

    </property>

    <property>

        <address>98320 County Rd 113</address>

        <price>$115,000</price>

        <comments>Small acreage outside of town</comments>

    </property>

</properties>

具体向服务器发送请求并对服务器响应做出回应的JavaScript与前面的例子是一样的。不过,从handleReadyStateChange函数开始有所不同。假设请求成功地完成,接下来第一件事就是调用clearPreviousResults函数,将以前搜索所创建的内容删除。

clearPreviousResults函数完成两个任务:删除出现在最上面的Results”标题文本,并从结果表中清除所有行。首先使用hasChildNodes方法查看可能包括标题文本的span元素是否有子元素。应该知道,只有hasChildNodes方法返回true时才存在标题文本。如果确实返回true,则删除span元素的第一个(也是惟一的)子节点,因为这个子节点表示的就是标题文本。

clearPreviousResults的下一个任务是在显示搜索结果的表中删除所有行。所有结果行都是tbody节点的子节点,所以先使用document.getElementById方法得到该tbody节点的引用。一旦有了tbody节点,只要这个tbody节点还有子节点(tr元素)就进行迭代处理。每次迭代时都会从表体中删除childNodes集合中的第一个子节点。当表体中再没有更多的表行时,迭代结束。

搜索结果表在parseResults函数中建立。这个函数首先创建一个名为results的局部变量,这是使用XMLHttpRequest对象的responseXML属性得到的XML文档。

使用getElementsByTagName方法来获得XML文档中包含所有property元素的数组,然后将这个数组赋给局部变量properties。一旦有了property元素的数组,可以迭代处理数组中的各个元素,并获得propertyaddresspricecomments

var properties = results.getElementsByTagName("property");

for(var i = 0; i < properties.length; i++) {

    property = properties[i];

    address = property.getElementsByTagName("address")[0].firstChild.nodeValue;

    price = property.getElementsByTagName("price")[0].firstChild.nodeValue;

    comments = property.getElementsByTagName("comments")[0].firstChild.nodeValue;

 

    addTableRow(address, price, comments);

}

下面来仔细分析这个循环,因为这正是parseResults函数的核心。在for循环中,首先得到数组中的下一个元素,并把它赋给局部变量property。接下来,对于你感兴趣的各个子元素(addresspricecomments),分别获得它们的节点值。

请考虑address元素,这是property元素的一个子元素。首先在property元素上调用getElementsByTagName方法来得到单个address元素。getElementsByTagName方法返回一个数组,不过因为你知道有且仅有一个address元素,所以可以使用[0]记法来引用这个元素。

沿着XML结构继续向下,现在有了address标记的引用,你需要得到它的文本内容。记住,文本实际上是父元素的一个子节点,所以可以使用firstChild属性来访问address元素的文本节点。有了文本节点后,可以引用文本节点的nodeValue属性来得到文本。

采用同样的办法来得到pricecomments元素的值,并把各个值分别赋给局部变量pricecomments。再将addresspricecomments传递给名为addTableRow的辅助函数,它会用这些结果数据具体建立一个表行。

addTableRow函数使用W3C DOM方法和JavaScript建立一个表行。使用document.cre-
ateElement
方法创建一个row对象,之后,再使用名为createCellWithText的辅助函数分别为addresspricecomments值创建一个cell对象。createCellWithText函数会创建并返回一个以指定的文本作为单元格内容的cell对象。

createCellWithText函数首先使用document.createElement方法创建一个td元素,然后使用document.createTextNode方法创建一个包含所需文本的文本节点,所得到的文本节点追加到td元素。这个函数再把新创建的td元素返回给调用函数(addTableRow)。

addTableRow函数对addresspricecomments值重复调用createCellWithText函数,每一次向tr元素追加一个新创建的td元素。一旦向row(行)增加了所有cell(单元格),这个row就将被增加到表的tbody元素中。

就这么多!你已经成功地读取了服务器返回的XML文档,而且动态创建了一个结果表。代码清单3-6显示了这个例子完整的JavaScript和可扩展HTML代码。

代码清单3-6  dynamicContent.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"

  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<title>Dynamically Editing Page Content</title>

 

<script type="text/javascript">

var xmlHttp;

 

function createXMLHttpRequest() {

    if (window.ActiveXObject) {

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

    }

    else if (window.XMLHttpRequest) {

        xmlHttp = new XMLHttpRequest();

    }

}

 

function doSearch() {

    createXMLHttpRequest();

    xmlHttp.onreadystatechange = handleStateChange;

    xmlHttp.open("GET", "dynamicContent.xml", true);

    xmlHttp.send(null);

}

 

function handleStateChange() {

    if(xmlHttp.readyState == 4) {

        if(xmlHttp.status == 200) {

            clearPreviousResults();

            parseResults();

        }

    }

}

 

function clearPreviousResults() {

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

    if(header.hasChildNodes()) {

        header.removeChild(header.childNodes[0]);

    }

 

    var tableBody = document.getElementById("resultsBody");

    while(tableBody.childNodes.length > 0) {

        tableBody.removeChild(tableBody.childNodes[0]);

    }

}

 

function parseResults() {

    var results = xmlHttp.responseXML;

 

    var property = null;

    var address = "";

    var price = "";

    var comments = "";

 

    var properties = results.getElementsByTagName("property");

    for(var i = 0; i < properties.length; i++) {

        property = properties[i];

        address = property.getElementsByTagName("address")[0].firstChild.nodeValue;

        price = property.getElementsByTagName("price")[0].firstChild.nodeValue;

        comments = property.getElementsByTagName("comments")[0]

                                                                  .firstChild.nodeValue;

        addTableRow(address, price, comments);

    }

    var header = document.createElement("h2");

    var headerText = document.createTextNode("Results:");

    header.appendChild(headerText);

    document.getElementById("header").appendChild(header);

 

    document.getElementById("resultsTable").setAttribute("border", "1");

}

 

function addTableRow(address, price, comments) {

    var row = document.createElement("tr");

    var cell = createCellWithText(address);

    row.appendChild(cell);

 

    cell = createCellWithText(price);

    row.appendChild(cell);

 

    cell = createCellWithText(comments);

    row.appendChild(cell);

 

    document.getElementById("resultsBody").appendChild(row);

}

 

function createCellWithText(text) {

    var cell = document.createElement("td");

    var textNode = document.createTextNode(text);

    cell.appendChild(textNode);

 

    return cell;

}

</script>

</head>

 

<body>

  <h1>Search Real Estate Listings</h1>

 

  <form action="#">

    Show listings from

        <select>

            <option value="50000">$50,000</option>

            <option value="100000">$100,000</option>

            <option value="150000">$150,000</option>

        </select>

        to

        <select>

            <option value="100000">$100,000</option>

            <option value="150000">$150,000</option>

            <option value="200000">$200,000</option>

        </select>

    <input type="button" value="Search" onclick="doSearch();"/>

  </form>

 <span id="header">

 

  </span>

 

  <table id="resultsTable" width="75%" border="0">

    <tbody id="resultsBody">

    </tbody>

  </table>

</body>

</html>

最新评论:
评论内容:请自觉遵守互联网相关政策法规。
用户名: 密码: 匿名 注册
热门文章
随机推荐
About iTtang - 联系方法  - 专题列表 - 友情链接  -  高级搜索   -  帮助中心  -