ajax编程

传统方式校验用户名是否重复的设计分析javascript

RegUserUIhtml

Reguser.jspjava

RegUsernode

Main.jspjquery

注册程序员

校验用户名web

是否重复面试

VerifyUserNameajax

?返回怎样的sql

一个页面

VerifyUserName这个Action校验用户名后还要回到第一个jsp页面上,而第一个jsp页面是第一个action显示出来的,因此也能够在第一个action的基础上增长校验用户名的功能,这就没有作到职责单一了,思路没有单独作一个VerifyUserName的action来校验用户名清晰。

在校验用户名的action和jsp页面中要增长怎样的代码呢?因为显示结果是增长一条提示信息而已(用<div>将提示信息包起来, 有信息时显示信息,没信息时保持空白),因此在action中也就是要增长一条存储提示信息的代码,写完action后,问题就是jsp页面上如何实现将校验用户名是否存在的请求及参数提交给第一个action了,这时候必须写javascript函数了,并应掌握<html:rewrite>标签和document.location.href属性。

 

传统实现方式1 :在原来的主窗口中回显结果

问题:

1:回显的页面在原来网页效果的基础上增长一点内容,但是服务器的响应结果会覆盖掉窗口中原来显示的那个网页内容,若是服务器在响应时仅仅回送要新增的内容,则会覆盖掉原来的整个网页,怎样作才能看到原来网页效果的基础上增长一点内容?

2:怎样用javascript代码将请求发送给服务器,在发送请求时,怎样将文本框中填写的用户名做为参数传递给服务器。

3:怎样在<a>标签中触发javascript函数调用?触发函数时要注意返回值。

实现思路:

让VerifyUserName仍然返回Reguser.jsp,这时候须要在Reguser.jsp页面中增长一条有时候显示,有时候不显示的提示信息。或者是经过生成一段javascript代码,而后用一个弹出对话框来显示提示信息。先用浏览器直接测试 VerifyUserName。

在<a>标签中可使用javascript协议或onclick事件来触发javascript函数调用,先用静态数据测试一下,而后用window.location.href属性(replace方法)或模拟表单提交的方式向服务器发送请求,把两种方式都要作一下。

缺点:

要为电脑增长一个鼠标,因为浏览器的特色是买回鼠标就会覆盖掉原来的电脑,因此,要想显示出电脑加鼠标的效果,只能同时去买回一个电脑和鼠标。

比如舞台要切换帷幕同样,上一场的帷幕上贴的是花,当下一场要换成贴龙时,最快的方式不是把原来帷幕上的花揭下来再换上龙,而是作两个帷幕,直接把上一个帷幕收起,同时把下一个帷幕拉开。每次都送出一个帷幕,帷幕上贴不一样内容。

<html:base> 标签会害你的,例如,它生成的路径<base href=“http://localhost:8080/ajaxdemo/WEB-INF/user/RegUser.jsp”>,咱们网页中的相对路径全变成/Web-inf/user下面的了。虽然我在课堂上提早讲了,但在练习时,不少同窗仍是都犯了这个错误并找不出缘由来。

 

讲超连接的javascript协议时,先用这个代码说一下:<a href=“javascript:3”></a>,让你们明白返回值的而影响,也能够直接在浏览器地址栏输入javascript:window.document,浏览器将显示[object]。

 

对于onclick的讲解:

1.在保留<html:base/>标签时,用用<a href=‘<html:rewrite action=“#’ onclick=”verifyUserName()“>作,看到<html:base/>标签致使的错误后果。照理说,超连接这里用# 不该该发送请求的,但我把jsp页面放在web-inf里时,浏览器要发请求,有同窗没放在web-inf里,结果没发请求。

2.去掉<html:base/> 标签,看到正确的效果后,

3.增长超连接,将#改成空白,即改为以下形式:

<a href=“” onclick=“verifyUserName()”>校验用户名是否存在</a>

在这里说明清楚了若是不返回false,除了干事件处理代码,还干原来默认的行为,关于若是取消原来的行为,看完下面的实验后继续讲解。

4.增长超连接,让路径指向VerifyUserName,

<a href='<html:rewrite action="/VerifyUserName"/>' onclick="verifyUserName()">校验用户名是否存在</a>

这时候,即便用户名为zxx,也说没有重复,这是由于浏览器除了访问javascript中设置的VerifyUserName这个地址外,还执行了超连接原来的行为,又根据href的值再次发出了请求,一共发了两次请求,浏览器显示了后面的请求的结果。

5.经过3步和第4步的问题,引出了事件处理函数中的return false的做用,能够经过这个代码辅助说明:<a href="delAction?id=3" onclick="return confirm('真的吗?')">删除</a>

 

var url = '<html:rewrite action="/AjaxVerifyUserName?username="/>' + userName;

上面的为何要用单引号引发来才成为javascript的字符串,不然会看成变量处理,课堂上专门演示和说明了,练习时还有同窗犯这个错误,我以为关键是要告诉你们是检查javascript代码时,不能在jsp页面检查,而应该在其生成的网页内容中检查。作bs的界面时,最终的效果是浏览器显示网页而形成的,若是界面有问题,首先要查网页的源代码找出问题,而后才能推断出jsp在生成网页源代码时出现了什么问题,这对于不少人来讲都不知道。

 

对于第2个问题:javascript是一种对它所在的网页文档及网页上的各个元素进行操做的语言,便可以对网页上的文本框进行增删改查,如今要获取用户名文本框中的内容,你说怎么办?

 

预备的实验内容:

<form>

用户名:<input type="text" name="username" /><br>

密码:<input type="password" name="password" /><br>

确认密码:<input type="password" name="password2" /><br>

<input type="submit"  value="注册"/>

</form>

 

校验用户名是否存在

 

恭喜你,用户名未被注册!

很遗憾,用户名已被注册!

 

传统实现方式2 :用弹出的新窗口回显结果

实现方式:在弹出窗口中回显结果

弹出窗口演示

模态对话框演示

实现思路:

由弹出窗口打开一个网页的方式发出校验用户名的请求,回送的应该是一个网页,只是这个网页的内容很简单,可是,若是要有关闭按钮,必须加上相应的按钮和javascript代码。

模态对话框的好处在于避免了受浏览器显示新窗口的方式的差别的影响,并可要求用户必须关闭弹出窗口后才能进行其余操做。

特色:

服务器回送的结果给新窗口,不影响原始窗口。一个帷幕收起,同时把下一个帷幕拉开。每次都送出一个帷幕,帷幕上贴不一样内容。

<html:base> 标签会害你的,例如,它生成的路径<base href=“http://localhost:8080/ajaxdemo/WEB-INF/user/RegUser.jsp”>,咱们网页中的相对路径全变成/Web-inf/user下面的了。虽然我在课堂上提早讲了,但在练习时,不少同窗仍是都犯了这个错误并找不出缘由来。

 

讲超连接的javascript协议时,先用这个代码说一下:<a href=“javascript:3”></a>,让你们明白返回值的而影响,也能够直接在浏览器地址栏输入javascript:window.document,浏览器将显示[object]。

 

对于onclick的讲解:

1.在保留<html:base/>标签时,用用<a href=‘<html:rewrite action=“#’ onclick=”verifyUserName()“>作,看到<html:base/>标签致使的错误后果。照理说,超连接这里用# 不该该发送请求的,但我把jsp页面放在web-inf里时,浏览器要发请求,有同窗没放在web-inf里,结果没发请求。

2.去掉<html:base/> 标签,看到正确的效果后,

3.增长超连接,将#改成空白,即改为以下形式:

<a href=“” onclick=“verifyUserName()”>校验用户名是否存在</a>

在这里说明清楚了若是不返回false,除了干事件处理代码,还干原来默认的行为,关于若是取消原来的行为,看完下面的实验后继续讲解。

4.增长超连接,让路径指向VerifyUserName,

<a href='<html:rewrite action="/VerifyUserName"/>' onclick="verifyUserName()">校验用户名是否存在</a>

这时候,即便用户名为zxx,也说没有重复,这是由于浏览器除了访问javascript中设置的VerifyUserName这个地址外,还执行了超连接原来的行为,又根据href的值再次发出了请求,一共发了两次请求,浏览器显示了后面的请求的结果。

5.经过3步和第4步的问题,引出了事件处理函数中的return false的做用,能够经过这个代码辅助说明:<a href="delAction?id=3" onclick="return confirm('真的吗?')">删除</a>

 

var url = '<html:rewrite action="/AjaxVerifyUserName?username="/>' + userName;

上面的为何要用单引号引发来才成为javascript的字符串,不然会看成变量处理,课堂上专门演示和说明了,练习时还有同窗犯这个错误,我以为关键是要告诉你们是检查javascript代码时,不能在jsp页面检查,而应该在其生成的网页内容中检查。作bs的界面时,最终的效果是浏览器显示网页而形成的,若是界面有问题,首先要查网页的源代码找出问题,而后才能推断出jsp在生成网页源代码时出现了什么问题,这对于不少人来讲都不知道。

 

对于第2个问题:javascript是一种对它所在的网页文档及网页上的各个元素进行操做的语言,便可以对网页上的文本框进行增删改查,如今要获取用户名文本框中的内容,你说怎么办?

 

预备的实验内容:

<form>

用户名:<input type="text" name="username" /><br>

密码:<input type="password" name="password" /><br>

确认密码:<input type="password" name="password2" /><br>

<input type="submit"  value="注册"/>

</form>

 

校验用户名是否存在

 

恭喜你,用户名未被注册!

很遗憾,用户名已被注册!

 

Ajax的概念:

是asynchronous javascript and xml的简写。

不是一项具体的技术,而是几门技术的综合应用。

其核心只不过是要在javascript中调用一个叫XMLHttpRequest的javascript类,这个类能够与Web服务器使用HTTP协议进行交互,程序不经过浏览器发出请求,而是用这个特殊的JavaScript对象发送请求,再由这个JavaScript对象接收响应,并将响应结果用DOM编程方式挂到原来的网页上(见下页的图),从而使得javascript借助这个api类能够干出比较有意义的事情。

XMLHttpRequest对象在网络上的俗称为XHR对象。

 

Ajax的特色:

浏览器中显示一个页面后,这个页面之后一直不改变,全部的操做请求都由这个网页中的javascript代码发出,全部的结果都由javascript代码接受并增长到这个页面上,浏览器窗口中显示的网页始终都是初始的那个网页。(见下面两页的图)

加强用户体验:能够在用户浏览网页的同时与服务器进行异步交互和实现网页内容的局部更新,例如,126邮箱密码安全性判断和google suggest;能够按需取数据,改善页面显示速度,例如,树状菜单和babasport的首页(整合多个信息的页面);视觉流畅的定时刷新,例如,聊天室。(用下几页的图举例说明)

学习ajax和应用ajax的难点不在于XMLHttpRequest自己,而在于javascript和DOM编程,没有较好的javascript和DOM编程基础,你就很难作出有意义的ajax应用。

 

浏览器的普通交互方式

 

Ajax的交互方式

 

同步交互和异步交互

    举个例子:普通B/S模式(同步)       AJAX技术(异步)

       *  同步:提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事

       *   异步: 请求经过事件触发->服务器处理(这时浏览器仍然能够做其余事情)->处理完毕

     同步是指:发送方发出数据后,等接收方发回响应之后才发下一个

                        数据包的通信方式。  异步是指:发送方发出数据后,不等接收方发回响应,接着发送下

                       个数据包的通信方式

   

     易懂的理解:

     异步传输:   你传输吧,我去作个人事了,传输完了告诉我一声   同步传输:   你如今传输,我要亲眼看你传输完成,才去作别的事

 

AJAX案例之google suggest

AJAX案例之Google Maps

Ajax的应用场景:财富通网吧充值界面

Ajax的应用场景:密码安全性检测

Ajax的应用场景:RIA应用

Ajax的应用场景:邮箱系统

Ajax的应用场景:蓝源批发零售业连锁管理系统

 

究竟什么是Ajax

Ajax:一种不用刷新整个页面即可与服务器通信的办法

图1 Web的传统模型。客户端向服务器发送一个请求,服务器返回整个页面,如此反复

图2 在Ajax模型中,数据在客户端与服务器之间独立传输。服务器再也不返回整个页面

 

Ajax的实现方式

不用刷新整个页面即可与服务器通信的办法:

Flash

Java applet

框架:若是使用一组框架构造了一个网页,能够只更新其中一个框架,而没必要惊动整个页面

隐藏的iframe

XMLHttpRequest:该对象是对 JavaScript 的一个扩展,可以使网页与服务器进行通讯。是建立 Ajax 应用的最佳选择。实际上一般把 Ajax 当成 XMLHttpRequest 对象的代名词

 

Ajax的工做原理

Ajax的核心是JavaScript对象XmlHttpRequest。

    该对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术。简而言之,XmlHttpRequest使您可使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。

AJAX采用异步交互过程。AJAX在用户与服务器之间引入一个中间媒介,从而消除了网络交互过程当中的处理—等待—处理—等待缺点。

用户的浏览器在执行任务时即装载了AJAX引擎。AJAX引擎用JavaScript语言编写,一般藏在一个隐藏的框架中。它负责编译用户界面及与服务器之间的交互。

AJAX引擎容许用户与应用软件之间的交互过程异步进行,独立于用户与网络服务器间的交流。如今,能够用Javascript调用AJAX引擎来代替产生一个HTTP的用户动做,内存中的数据编辑、页面导航、数据校验这些不须要从新载入整个页面的需求能够交给AJAX来执行。

使用AJAX,能够为JSP、开发人员、终端用户带来可见的便捷:

用户界面

AJAX引擎

服务器

用户界面

服务器

 

AJAX包含的技术

      AJAX:(Asynchronous JavaScript and XML)并非一项新技术,实际上是多种技术的综合,包括Javascript、XHTML和CSS、DOM、XML和XMLHttpRequest.

服务器端语言:服务器须要具有向浏览器发送特定信息的能力。Ajax与服务器端语言无关。

XML (eXtensible Markup Language,可扩展标记语言) 是一种描述数据的格式。AJAX 程序须要某种格式化的格式来在服务器和客户端之间传递信息,XML 是其中的一种选择

XHTML(eXtended Hypertext Markup Language,使用扩展超媒体标记语言)和 CSS(Cascading Style Sheet,级联样式单)标准化呈现;

DOM(Document Object Model,文档对象模型)实现动态显示和交互;

使用XMLHTTP组件XMLHttpRequest对象进行异步数据读取

使用JavaScript绑定和处理全部数据

 

AJAX的缺陷

AJAX不是完美的技术。也存在缺陷:

1    AJAX大量使用了Javascript和AJAX引擎,而这个取决于浏览器的支持。IE5.0及以上、Mozilla1.0、NetScape7及以上版本才支持,Mozilla虽然也支持AJAX,可是提供XMLHttpRequest的方式不同。因此,使用AJAX的程序必须测试针对各个浏览器的兼容性。

2   AJAX更新页面内容的时候并无刷新整个页面,所以,网页的后退功能是失效的;有的用户还常常搞不清楚如今的数据是旧的仍是已经更新过的。这个就须要在明显位置提醒用户“数据已更新”。

3    对流媒体的支持没有FLASH、Java Applet好。

4    一些手持设备(如手机、PDA等)如今还不能很好的支持Ajax。

 

XMLHttpRequest对象   XMLHttpRequest是XMLHTTP组件的对象,经过这个对象,AJAX能够像桌面应用程序同样只同服务器进行数据层面的交换,而不用每次都刷新界面,也不用每次将数据处理的工做都交给服务器来作;这样既减轻了服务器负担又加快了响应速度、缩短了用户等待的时间。

XMLHttpRequest最先是在IE5中以ActiveX组件的形式实现的。非W3C标准.

建立XMLHttpRequest对象(因为非标准因此实现方法不统一)

Internet Explorer把XMLHttpRequest实现为一个ActiveX对象

其余浏览器(Firefox、Safari、Opera…)把它实现为一个本地的JavaScript对象。

XMLHttpRequest在不一样浏览器上的实现是兼容的,因此能够用一样的方式访问XMLHttpRequest实例的属性和方法,而不论这个实例建立的方法是什么。

 

XMLHttpRequest对象初始化

function   createXmlHttpRequest(){

var xmlhttp = null;

try{

                   //Firefox, Opera 8.0+, Safari

           xmlhttp=new XMLHttpRequest();

                   }catch(e){//IEIE7.0如下的浏览器以ActiveX组件的方式来建立XMLHttpRequest对象

var MSXML =

['MSXML2.XMLHTTP.6.0','MSXML2.XMLHTTP.5.0',

'MSXML2.XMLHTTP.4.0','MSXML2.XMLHTTP.3.0',

'MSXML2.XMLHTTP','Microsoft.XMLHTTP'];

  for(var n = 0; n < MSXML.length; n ++){

    try{

      xmlhttp = new ActiveXObject(MSXML[n]);

      break;

    }catch(e){}}

  }

return xmlhttp;

}

 

XMLHttpRequest对象方法

方法

描述

abort()

中止当前请求

getAllResponseHeaders()

把http请求的全部响应首部做为键/值对返回

getResponseHeader("headerLabel")

返回指定首部的串值

open(“method”,”url”)

创建对服务器的调用,method参数能够是GET,POST。url参数能够是相对URL或绝对URL。这个方法还包括3个可选参数。

send(content)

向服务器发送请求

setRequestHeader("label", "value")

把指定首部设置为所提供的值。在设置任何首部以前必须先调用open()

 

XMLHttpRequest对象属性

发送请求--方法和属性介绍

利用XMLHttpRequest 实例与服务器进行通讯包含如下3个关键部分:

onreadystatechange 事件处理函数

open 方法

send 方法

 

onreadystatechange:

该事件处理函数由服务器触发,而不是用户

在 Ajax 执行过程当中,服务器会通知客户端当前的通讯状态。这依靠更新 XMLHttpRequest 对象的 readyState 来实现。改变 readyState 属性是服务器对客户端链接操做的一种方式。

每次 readyState 属性的改变都会触发 readystatechange事件

 

open(method, url, asynch)

XMLHttpRequest 对象的 open 方法容许程序员用一个Ajax调用向服务器发送请求。

method:请求类型,相似 “GET”或”POST”的字符串。若只想从服务器检索一个文件,而不须要发送任何数据,使用GET(能够在GET请求里经过附加在URL上的查询字符串来发送数据,不过数据大小限制为2000个字符)。若须要向服务器发送数据,用POST。

在某些状况下,有些浏览器会把多个XMLHttpRequest请求的结果缓存在同一个URL。若是对每一个请求的响应不一样,这就会带来很差的结果。把当前时间戳追加到URL的最后,就能确保URL的唯一性,从而避免浏览器缓存结果。

 

url:路径字符串,指向你所请求的服务器上的那个文件。能够是绝对路径或相对路径。

asynch:表示请求是否要异步传输,默认值为true(异步)。指定true,在读取后面的脚本以前,不须要等待服务器的相应。指定false,当脚本处理过程通过这点时,会停下来,一直等到Ajax请求执行完毕再继续执行。

var url = "GetAndPostExample?timeStamp=" + new Date().getTime();

 

send(data)

open 方法定义了 Ajax 请求的一些细节。send 方法可为已经待命的请求发送指令

data:将要传递给服务器的字符串。

若选用的是 GET 请求,则不会发送任何数据, 给 send 方法传递 null 便可:request.send(null);

当向send()方法提供参数时,要确保open()中指定的方法是POST,若是没有数据做为请求体的一部分发送,则使用null.

完整的 Ajax 的 GET 请求示例:

使用get请求时send方法参数时null,若是传值的话,服务器也接受不到

 

setRequestHeader(header,value)

当浏览器向服务器请求页面时,它会伴随这个请求发送一组首部信息。这些首部信息是一系列描述请求的元数据(metadata)。首部信息用来声明一个请求是 GET 仍是 POST。

Ajax 请求中,发送首部信息的工做能够由 setRequestHeader完成

参数header: 首部的名字;  参数value:首部的值。

若是用 POST 请求向服务器发送数据,须要将 “Content-type” 的首部设置为 “application/x-www-form-urlencoded”.它会告知服务器正在发送数据,而且数据已经符合URL编码了。

该方法必须在open()以后才能调用

完整的 Ajax 的 POST 请求示例:

 

用 XMLHttpRequest 的方法可向服务器发送请求。在 Ajax 处理过程当中,XMLHttpRequest 的以下属性可被服务器更改:

readyState

status

responseText

responseXML

 

readyState

readyState 属性表示Ajax请求的当前状态。它的值用数字表明。

0 表明未初始化。 尚未调用 open 方法

1 表明正在加载。 open 方法已被调用,但 send 方法尚未被调用

2 表明已加载完毕。send 已被调用。请求已经开始

3 表明交互中。服务器正在发送响应

4 表明完成。响应发送完毕

每次 readyState 值的改变,都会触发 readystatechange 事件。若是把 onreadystatechange 事件处理函数赋给一个函数,那么每次 readyState 值的改变都会引起该函数的执行。

readyState 值的变化会因浏览器的不一样而有所差别。可是,当请求结束的时候,每一个浏览器都会把 readyState 的值统一设为 4

 

status

服务器发送的每个响应也都带有首部信息。三位数的状态码是服务器发送的响应中最重要的首部信息,而且属于超文本传输协议中的一部分。

经常使用状态码及其含义:

404 没找到页面(not found)

403 禁止访问(forbidden)

500 内部服务器出错(internal service error)

200 一切正常(ok)

304 没有被修改(not modified)(服务器返回304状态,表示源文件没有被修改 )

在 XMLHttpRequest 对象中,服务器发送的状态码都保存在 status 属性里。经过把这个值和 200 或 304 比较,能够确保服务器是否已发送了一个成功的响应

 

responseText

XMLHttpRequest 的 responseText 属性包含了从服务器发送的数据。它是一个HTML,XML或普通文本,这取决于服务器发送的内容。

当 readyState 属性值变成 4 时, responseText 属性才可用,代表 Ajax 请求已经结束。

 

responseXML

若是服务器返回的是 XML, 那么数据将储存在 responseXML 属性中。

只用服务器发送了带有正确首部信息的数据时, responseXML 属性才是可用的。 MIME 类型必须为 text/xml

 

AJAX开发框架

AJAX实质上也是遵循Request/Server模式,因此这个框架基本的流程是:

对象初始化

发送请求

服务器接收

服务器返回

客户端接收

修改客户端页面内容。

只不过这个过程是异步的。

 

A、初始化XMLHttpRequest对象

function   createXmlHttpRequest(){

var xmlhttp = null;

try{

                   //Firefox, Opera 8.0+, Safari

           xmlhttp=new XMLHttpRequest();

                   }catch(e){//IEIE7.0如下的浏览器以ActiveX组件的方式来建立XMLHttpRequest对象

var MSXML =

['MSXML2.XMLHTTP.6.0','MSXML2.XMLHTTP.5.0',

'MSXML2.XMLHTTP.4.0','MSXML2.XMLHTTP.3.0',

'MSXML2.XMLHTTP','Microsoft.XMLHTTP'];

  for(var n = 0; n < MSXML.length; n ++){

    try{

      xmlhttp = new ActiveXObject(MSXML[n]);

      break;

    }catch(e){}}

  }

return xmlhttp;

}

 

B、指定响应处理函数

指定当服务器返回信息时客户端的处理方式。只要将相应的处理函数名称赋给XMLHttpRequest对象的onreadystatechange属性就能够了.好比:

        XMLHttpReq.onreadystatechange = processResponse;

注意:这个函数名称不加括号,不指定参数。也能够用Javascript函数直接量方式定义响应函数。好比:

       XMLHttpReq.onreadystatechange = function() { };

// 处理返回信息的函数

function   processResponse() {

 

}

 

C、发出HTTP请求

向服务器发出HTTP请求了。这一步调用XMLHttpRequest对象的open和send方法。

     http_request.open('GET', 'http://www.example.org/some.file', true);

     http_request.send(null)

按照顺序,open调用完毕以后要调用send方法。send的参数若是是以Post方式发出的话,能够是任何想传给服务器的内容。

注意:若是要传文件或者Post内容给服务器,必须先调用setRequestHeader方法,修改MIME类别。以下:

http_request.setRequestHeader(“Content-Type”,”application/x-www-form-urlencoded”);

    这时资料则以查询字符串的形式列出,做为send的参数,例如:

    name=value&anothername=othervalue&so=on

 

发出Http请求的代码

//发送请求

function sendRequest(){

//获取文本框的值

var chatMsg=input.value;

var url="chatServlet.do?charMsg="+chatMsg;

//创建对服务器的调用

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

//设置MiME类别,若是用 POST 请求向服务器发送数据,

//须要将"Content-type" 的首部设置为 "application/x-www-form-urlencoded".

//它会告知服务器正在发送数据,而且数据已经符合URL编码了。

XMLHttpReq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

//状态改变的事件触发器,客户端的状态改变会触发readystatechange事件,

//onreadystatechange会调用相应的事件处理函数

XMLHttpReq.onreadystatechange=processResponse;

//发送数据

XMLHttpReq.send(null);

}

 

D、处理服务器返回的信息

处理响应处理函数都应该作什么。

  首先,它要检查XMLHttpRequest对象的readyState值,判断请求目前的状态。参照前文的属性表能够知道,readyState值为4的时候,表明服务器已经传回全部的信息,能够开始处理信息并更新页面内容了。以下:

if (http_request.readyState == 4) {

    // 信息已经返回,能够开始处理

} else {

    // 信息尚未返回,等待

}

  服务器返回信息后,还须要判断返回的HTTP状态码,肯定返回的页面没有错误。全部的状态码均可以在W3C的官方网站上查到。其中,200表明页面正常。

if (http_request.status == 200) {

      // 页面正常,能够开始处理信息

} else {

    // 页面有问题

}

 

XMLHttpRequest对成功返回的信息有两种处理方式:

responseText:将传回的信息当字符串使用;

responseXML:将传回的信息当XML文档使用,能够用DOM处理。

//处理返回信息的函数

function processResponse(){

   if(XMLHttpReq.readyState==4){ //判断对象状态 4表明完成

           if(XMLHttpReq.status==200){ //信息已经成功返回,开始处理信息

                  document.getElementById("chatArea").value=XMLHttpReq.responseText;

          }

   }

      }

 

数据格式摘要

在服务器端 AJAX 是一门与语言无关的技术。在业务逻辑层使用何种服务器端语言均可以。

从服务器端接收数据的时候,那些数据必须以浏览器可以理解的格式来发送。服务器端的编程语言通常以以下 3 种格式返回数据:

XML

JSON

HTML

 

XML格式

优势:

XML 是一种通用的数据格式。

没必要把数据强加到已定义好的格式中,而是要为数据自定义合适的标记。

利用 DOM 能够彻底掌控文档。

缺点:

若是文档来自于服务器,就必须得保证文档含有正确的首部信息。若文档类型不正确,那么 responseXML 的值将是空的。

当浏览器接收到长的 XML 文件后, DOM 解析可能会很复杂

 

JSON格式

JSON(JavaScript Object  Notation)一种简单的数据格式,比xml更轻巧。JSON是JavaScript原生格式,这意味着在JavaScript中处理JSON数据不须要任何特殊的API或工具包。

JSON的规则很简单:对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每一个“名称”后跟一个“:”(冒号);“‘名称/值’对”之间使用“,”(逗号)分隔。

   规则以下:

       1)映射用冒号(“:”)表示。名称:值

       2)并列的数据之间用逗号(“,”)分隔。名称1:值1,名称2:值2

       3) 映射的集合(对象)用大括号(“{}”)表示。{名称1:值1,名称2:值2}

       4) 并列数据的集合(数组)用方括号(“[]”)表示。

         [

           {名称1:值,名称2:值2},

           {名称1:值,名称2:值2}

         ]

      5  元素值可具备的类型:string, number, object, array, true, false, null

 

JSON 示例

JSON 用冒号(而不是等号)来赋值。每一条赋值语句用逗号分开。整个对象用大括号封装起来。可用大括号分级嵌套数据。

对象描述中存储的数据能够是字符串,数字或者布尔值。对象描述也可存储函数,那就是对象的方法。

 

解析JSON

JSON 只是一种文本字符串。它被存储在 responseText 属性中

为了读取存储在 responseText 属性中的 JSON 数据,须要根据 JavaScript 的 eval 语句。函数 eval 会把一个字符串看成它的参数。而后这个字符串会被看成 JavaScript 代码来执行。由于 JSON 的字符串就是由 JavaScript 代码构成的,因此它自己是可执行的

代码实例:

处理JSON

例子一:

<script language="JavaScript">

     var people ={"firstName": "Brett", "lastName":"McLaughlin",

                                                        "email": "brett@newInstance.com" };

     alert(people.firstName);

     alert(people.lastName);

     alert(people.email);

</script>

 

例子二:

<script language="JavaScript">

      var people =[

                     {"firstName": "Brett","email": "brett@newInstance.com" },

                     {"firstName": "Mary","email": "mary@newInstance.com" }

                ];

 

    alert(people[0].firstName);

    alert(people[0].email);

    alert(people[1].firstName);

    alert(people[1].email);

</script>

 

例子三:

<script language="JavaScript">

     var people ={

            "programmers":

              [

                {"firstName": "Brett", "email": "brett@newInstance.com" },

                {"firstName": "Jason", "email": "jason@servlets.com" }

              ]

};

 

window.alert(people.programmers[0].firstName);

window.alert(people.programmers[1].email);

</script>

 

例子四:

<script language="JavaScript">

    var people ={

            "programmers": [

            { "firstName": "Brett", "email": "brett@newInstance.com" },

            { "firstName": "Jason",  "email": "jason@servlets.com" },

            { "firstName": "Elliotte", "lastName":"Harold", "email": "elharo@macfaq.com" }

           ],

          "authors": [

            { "firstName": "Isaac",  "genre": "science fiction" },

            { "firstName": "Tad", "genre": "fantasy" },

            { "firstName": "Frank",  "genre": "christian fiction" }

           ],

          "musicians": [

            { "firstName": "Eric",  "instrument": "guitar" },

            { "firstName": "Sergei", "instrument": "piano" }

           ]};

    window.alert(people.programmers[1].firstName);

    window.alert(people.musicians[1].instrument);

</script>

 

例子五

<script language="JavaScript">

      var people ={

             "username":"mary",

             "age":"20",

             "info":{"tel":"1234566","celltelphone":788666},

             "address":[

                     {"city":"beijing","code":"1000022"},

                     {"city":"shanghai","code":"2210444"}

              ]

        };

 

      window.alert(people.username);

      window.alert(people.info.tel);

      window.alert(people.address[0].city);

</script>

 

JSON 小结

优势:

做为一种数据传输格式,JSON 与 XML 很类似,可是它更加灵巧。

JSON 不须要从服务器端发送含有特定内容类型的首部信息。

缺点:

语法过于严谨

代码不易读

eval 函数存在风险

 

解析 HTML

HTML 由一些普通文本组成。若是服务器经过 XMLHttpRequest 发送 HTML, 文本将存储在 responseText 属性中。

没必要从 responseText 属性中读取数据。它已是但愿的格式,能够直接将它插入到页面中。

插入 HTML 代码最简单的方法是更新这个元素的 innerHTML 属性。

 

HTML 小结

优势:

从服务器端发送的 HTML 代码在浏览器端不须要用 JavaScript 进行解析。

HTML 的可读性好。

HTML 代码块与 innerHTML 属性搭配,效率高。

缺点:

若须要经过 AJAX 更新一篇文档的多个部分,HTML 不合适

innerHTML 并不是 DOM 标准。

 

对比小结

若应用程序不须要与其余应用程序共享数据的时候, 使用 HTML 片断来返回数据时最简单的

若是数据须要重用, JSON 文件是个不错的选择, 其在性能和文件大小方面有优点

当远程应用程序未知时, XML 文档是首选, 由于 XML 是 web 服务领域的 “世界语”

 

案例:省份与城市的联动下拉列表框

效果演示。

 

联动下拉:纯静态数据的html方式

实验步骤:

演示程序运行的效果。

编写一个静态province.html页面,其中使用一个二维数组来装载全部数据,而后分析和编码实现省份与城市的联动下拉列表框。

编写一个静态jsonProvince.html页面,其中使用JSON对象方式来装载全部数据,而后分析和编码实现此种数据格式下的省份与城市的联动下拉列表框。

对于静态网页显示省份和城市,首先要考虑用什么样的数据结构来存储全部信息,每一个选项信息包括名称和id,若是单独用一个数组保存各个省份,再用一个数组保存各个省份下的城市,是能够的,可是须要在两个地方维护数据,要保持数据的同步。提示:因为学员基础的缘故,对于此部分的讲解,必定要写一些示意代码来进行说明。

与其如此,还不如用一个数组把省份和省份下的城市所有保存起来。

 

没有惟一和最好的数据存储结构,只有最适合你和你认为不错的数据存储结构,首先考虑的是把功能实现出来,其次才是考虑哪一种方式更优雅和方便些。在讲课时,对于二维数组和json数据结构,都是先把写好的静态province.html页面中的各个函数删除掉,而后让同窗们本身去完成这些函数,这样同窗们的学习兴致很高,确实也能让同窗们立刻就感到有收获和有进步。

 

在编写触发的显示某个省份下面的城市的js函数时,先简单地alert一下选中的省份的值。

 

//下面代码在firefox下不成功!

//document.getElementById("provinceId").add(optProvince);

//document.getElementById("provinceId").options.remove(i);

 

//下面代码在firefox下成功!

document.getElementById("provinceId").options.add(optProvince);

document.getElementById("provinceId").remove(i);

 

联动下拉:动态生成数据的方式

实验步骤:

编写一个ListProvinceAction的Action和相应的province.jsp页面,留出数据待填充,你们清晰看到后面的任务就是生成出数据。

建立表明省份与城市的province和City实体类,而后将相应的实体对象存放在一个单例的MemoryDao中,用一个ArrayList集合存储全部Province对象,Province对象中保存有一个City对象的集合,在MemoryDao中构建出各个对象及关系。

建立ProvinceService类获取全部省份列表和CityService类获取某个省份下的全部城市。

在ListProvinceAction中编写拼凑出JSP页面所须要的那个数组字符串。

编写一个ListJsonProvinceAction的Action和相应的jsonProvince.jsp页面,以生成使用JSON对象来装载全部数据,为了方便拼凑JSON格式的字符串,在各个实体对象中覆盖toString方法返回自身的JSON格式字符串,这要比在外面最后统一转换成一个JSON字符串的作法要优雅不少,这种分而治之的思想使得程序健壮且易于维护。

编写一个ListJsonProvince2Action的Action,快速演示和说明一下如何在其内部用JSONObject与JSONArray工具里来完成json字符串的生成工做。

使用StringBuilder拼凑字符串时,讲解了其与StringBuffer的区别,线程安全比如人走路,若是老是考虑怕与别人或汽车相撞,那走路速度慢,若是不花精力去注意别人,只管埋头走路,速度确定要快。若是知道是单行通道走,那么就能够不去考虑别人,只管本身一我的埋头走路好了。

 

当要将ArrayList中的元素转换成js的json格式的字符串输出时,不须要本身再去拼凑字符串,能够直接调用ArrayList的toString方法,该方法输出的字符串正好就符合json数组的语法规范。

 

联动下拉:用Ajax方式实现的思路分析

传统方式与Ajax实现方式的对比与选择:

比如饭馆上菜的方式:一种是先让用户等较长时间,最后一会儿将全部的菜所有上上来,通常的火锅店都是这么作的,还有一种方式就是作好一盘菜就上一盘菜,用户等待的时间较短,但送餐服务员要跑好屡次,通常的家常菜饭店都是这么作的。

如何选择:传统方式是一会儿把全部数据搞到手,之后只是用js把到手的数据显示出来便可,第一次获得数据的时间比较长,若是这个时间长得影响了用户的感觉,那就考虑用ajax进行改进,不然,能够直接使用传统方式。

若是将传统方式改造为ajax方式的总结:将原有的一个jsp页面改成两个jsp页面来实现,第一个页面为那些固定不变的内容和javascript代码,第二个页面为那些要改变的区域的代码,所以原来用一个action或servlet实现的代码要改变为用两个action或servlet来实现,这称为二步视图法。

实现的思路剖析:

第一次要得到一个页面,浏览器之后一直显示这个页面,之后的每一个操做都触发这个页面内部的一个javascript函数,再由这个javascript函数去发请求和处理回应结果。

第一次得到的页面应该包含什么信息?页面的初始内容和javascript函数。

之后的每次请求要得到什么样的结果?

若是本应用只显示几个省份及各个省份下面的少数几个城市,则能够用传统方式。若是本应用是要显示出全国全部省份下面的全部城市,涉及的数据量就比较大了,若是要从数据库中一会儿查出31个省和它们下面的全部城市,一般要涉及32条sql语句,花费的时间较长;若是改成Ajax按需取数据的方式,即选择哪一个省份后,再去取该省份下的城市,这样,第一次展示的速度就较快。给你们看看腾讯的网吧充值界面图。

 

联动下拉:用json数据传输格式的Ajax方式实现

步骤:

编写AjaxListProvince的Action和ajaxProvince.jsp页面,在一个下拉列表框中列出全部省份,编写触发和发送获取某个省份下面的城市的请求消息的代码,先简单的alert一下响应结果。

根据客户端的请求信息,分析和编写一个ListCities的Action调用CityService类获取某个省份下的全部城市,Action返回一个包含有某个省份下的全部City的json格式的数组。

在ajaxProvince.jsp页面中编写处理返回结果的javascript代码,使用javascript本身的eval方法处理json字符串。

改进为用prototype本身的json支持来处理返回的json串,包括扩展的String.evalJSON()方法和transport.responseJSON属性这两种方式。

因为本例子程序返回的城市对象很简单,可使用一个map来表示某个省份下的全部City信息,同时简化客户端的javascript代码。

编写jsp页面,由于没有涉及<html:form>表单,因此没法使用struts的html:optionCollections标签来生成下拉列表框的option选项,只能使用jstl标签。

 

注意:改成ajax方式来实现时,传递给事件处理函数中的参数再也不是选择项的索引号,而是选择项的值了,由于每次都要从服务器端来获取某个省份下的城市集合,传给服务器的参数是省份的id值。

浏览器一上来要发两次请求,第一次获得省份,第二次获得当前选中的省份的城市信息,而不是一次就获得省份和第一个省份的城市信息,一个模块专门负责省份,不要让它既处理省份,又处理城市。

 

学员在作第二步时,作好ListCityAction后,先用浏览器测试一下,而后用ajax调用,在ajax调用的回调函数中,先alert一下返回的这个串,最后再把这个串转换成为json对象。

在baidu中搜素”jquery select 添加选项”

学员写的一段代码:

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

               var op=$("<option>").attr('id',data[i][1]).html(data[i][0]);   

               $("#provinceId").append(op);

            }

 

分别用jquery和protoye作一遍。Jquery的json支持作一遍。Onchange事件用prototype和jquery提供的方式来注册。对于清楚下拉列表框中的全部选项,可使用jquery对象的empty函数或者让其innerHTML为空。用jquery时,最好是把事件处理的注册代码放在js中写,而不是放在html中,所谓的内容与行为相分离,术语借鉴了内容与表现相分离的说法。

有学员提到了如何本身实现缓存,借此机会正好把缓存给实现了一下:

能够用数组方式实现,用其id值做为数组元素的索引,若是id=99,测试数组的长度一下就变成了100,全部这样不合理,代码以下:

var data=[];

data[pid] = cities;

alert(data.length);

因而想到改成用对象方式来实现:

var data={};

data[pid] = cities;

for(var a in data)

{

alert(a + “:” + data[a]);

}

后来用数组进行循环,发现实际上也只有添加进去的几个元素,这里数组与对象的区别彷佛就是对象没有length属性。

 

有问题:$("#provinceId").change(fillCity($(this).val()));

正确: $("#provinceId").change(function(){

         fillCity($(this).val());

});

 

用jquery.each方法迭代出来元素是htmlelement类型,而不是jquery对象。可使用nodeName来查看htmlelement元素名。

 

联动下拉:用xml数据传输格式的Ajax方式实现

步骤:

编写一个ListCity2的Action,返回一个包含有某个省份下的全部City的xml文档。

直接用浏览器访问ListCity2,测试查看返回的xml文档内容是否正确。

客户端页面改成ajaxProvince2.jsp,在其中编写解析xml文档内容和将结果显示在下拉列表框中的代码,而且将AjaxListProvince配置为/ AjaxListProvince2.do

今天用jquery作时,只要在发送请求时,将dataType:’xml’,那么,jquery就回将xml文档转换成Document对象,并将该Document对象做为参数传递给回调函数,也就是说,此时的回调函数接收的参数就是Document对象。

/*拼出以下的数据格式,该如何作呢?

*

<cities>

<city>

<id></id>

<name></name>

</city>

<city>

<id></id>

<name></name>

</city>

</cities>

*/

 

Jsp就是拼凑大段字符串的技术,是模板技术,大量固定不变的串中偶尔要夹杂一点变化的数据,这就是jsp的用武之地。

jsp就是为了方便拼凑大量文本串而推出的技术,咱们为什么放着这么好的东西不用呢?

不须要写AjaxListProvince2Action,只须要把AjaxListProvinceAction再配置一遍,这正是mvc的优势,控制器和模型不变,可轻松改变视图(view)

 

联动下拉:返回整个下拉列表框的HTML代码

步骤:

编写一个ListCity3的Action,返回一个包含有某个省份下的全部City的下拉列表框的html代码。

直接用浏览器访问ListCity3,测试查看返回的html代码是否正确。

客户端页面改成AjaxProvince3.jsp 和在其中定义一个<div>元素来容纳服务器返回的下拉列表框,而且将AjaxListProvince配置为/ AjaxListProvince3.do。

为何有了前面两种很是优雅的作法,还要讲那些不优雅和很土的作法呢?由于外面有些公司用的就是土方法,真正用优雅方式的是很牛的好公司,这样的好公司并很少,外面的公司为何要用土方法呢?比如你读了小学一年级就能够挣钱了,之后一直忙于挣钱,就不必去读博士了,因此一直会用小学一年级的方式工做下去。虽然你读博士后,掌握了只有博士能挣到的钱的本领,挣钱的方式可能更优雅,但有时候,博士也要用小学一年级的方式去挣钱,例如,有的博士是房产公司的经理,但他挣钱的方式很原始,彻底是小学一年级水平的方式,只要会送礼和会喝酒就能够,读博士掌握的本领全用不上也是可能的。

 

你们对有的Action用response.getWriter()方法直接输出结果,对有的Action却跳转到一个jsp页面上的关系与区别老是搞不太清楚,问这问题的人都是属于基础比较不错,已经开动了脑筋的学员。看来你们对jsp的根本做用和工做原理了解得仍是不够好。

 

返回的html代码片断前面有几个空格和换行,致使显示的城市下拉列表框与省份下来列表框之间有很大的空袭,我是让返回的html代码前时尽可能去掉前面的空格,这时候应该用prototype或jquery提供的去掉两端空格的方法来完成。

 

联动下拉:返回向下拉列表框填充选项的js代码

步骤:

编写一个ListCity4的Action,返回一个用于将某个省份下的全部City添加进下拉列表框的javascript代码。

直接用浏览器访问ListCity4,测试查看返回的javascript代码是否正确。

客户端页面改成AjaxProvince4.jsp 和使用eval方法执行服务器端返回的javascript代码,而且将AjaxListProvince配置为/ AjaxListProvince4.do。

这种方式既不优雅,也不简单,但有人用,我以为彻底是无用,毫无价值。

今天用jquery作时,首先用eval(“(“ + msg + “)”)执行时,ie老是报告错误,说缺乏),换成firefox查看,提示错误以下:

missing ) in parenthetical

  document.getElementById('cityId').options.add(new Option('武汉',3));\n

原来是在返回的字符串最后多了个\n,把整个串用()括起来后,就至关于一个完整的字符串中间换行铡成两段,这种语法是不行的。

因而,去掉(),即代码改成eval(msg),再运行,结果就正常了。

最后读jquery的Ajax函数的文档,发现dataType选项有一个设置值为script,因而设置了这个选项,发现city的填充效果被double,

这说明将dataType设置为script后,jquery会自动帮咱们执行javascript代码,在这种状况下,不写success回调函数也能够看到运行效果。

-------------------------------------------------------------------------------------------

最后要给你们总结一下,这样你们思路就不混乱了:对于用4种不一样方式返回城市,第一步显示省份的页面须要作几个?返回省份信息的前置Action须要写几个。

对于第二步返回城市信息,除了json以外的3种方式须要作几个页面?须要作几个返回城市信息的Action?为何返回json数据时的Action与返回其余数据时的Action不一样?

 

案例扩展:多级地区的动态展示

实验步骤:

演示和分析程序运行的效果。(每选择一个区域,则显示一个包含有其全部子区域的下拉列表框,并清除其余不相关的下拉列表框;当选择了某个没有子区域的选项后,再也不显示出新的下拉列表框,而是显示该区域的网吧)

分析数据库表结构该如何设计,并执行预备的脚本文件建立表结构和数据。

分析JSP页面该如何设计:

第一个返回的页面应该包含哪些内容?

之后每次请求得到的结果是什么?返回的结果怎样展示在返回的第一个页面中?动态生成出下拉列表框和将下拉列表框追加到其余列表框后面,或者提早预约义若干span元素,将新增的下拉列表框添加到相应的span元素中。若是要实现用表格来显示某个区域的网吧,服务器端返回的要么是select元素,要么是table元素,客户端要判断结果的类型。

编码实现:先用span的方式实现;再用prototype的删除和添加功能。

 

我是一个通用的产品,要卖给不一样的公司,不一样公司的级别分类的层次是不同,咱们一般应该容许无限极分类,还有书的分类,这样的系统的数据库该如何设计。是设计无限个表,仍是设计一个表。无限级分类应用不少,例如论坛的版面,帖子的回复等等。

 

id,name,parentid

1,  集团,0 

2.  一公司, 1

3. 二公司, 1

4.三公司,1,3  

 

查询集团下的直接子部门:select * from dept where parentid=0;

  查询一公司下的直接子部门:select * from dept where parentid=1;

为一公司添加子部门:insert into detp values(..,1)

  父亲能够知道本身的全部孩子,孩子能够知道本身的父亲,这就很知足需求了嘛!

 

网吧为何要有一个字段指向所属的区别?由于要列出一个区域下的全部网吧,若是没这需求,固然也就不须要那个字段了。

 

这里的第一步不须要显示出全部省份,由于显示省份的方式和显示子地区的方式彻底同样,因此,显示省份信息借助第二部操做来完成,只是第一步作完后当即进入第二步弄出省份。

 

先不作网吧部分,等作完地区后,让学员看看使用dao查询网吧只是复制工做,环境搭建只是一次性的工做,只要环境搭建好了,开发过程就是这样的拷贝和复制。

对于采用span元素的实现方式,每一个下拉列表框都要用同一个函数来实现,这个函数须要接受两个参数:要显示的地区的父级id(即列表框要显示哪一个id地区下的子地区),获得显示的结果放在哪一个span元素里。先彻底用手工编写方式分析服务器每次返回的下拉列表框的html代码怎样?再分析用jsp如何生成,因为返回的结果既要有某个id的子地区,又要在生成的下拉列表框的onchange事件中指定下一级地区列表框所放置的span元素,因此,传递给服务器的参数也要有两个。若是不给服务器传递当前span元素的id值,而是等服务器返回一个结果后,而后客户端再用js代码来指定下一个span元素的id值,这样更好,由于服务器端不用考虑客户端的页面状况了,即服务器端的代码不用随客户端页面的改动(地区级别)而改变,这中方式能够留给同窗们本身作。对于这种状况,js代码中还要判断服务器返回的是<select>仍是<table>,是table则往网吧的<span>中填写。

每次添加新的下拉列表框前,都是删除原来的当前要生成的下拉列表框之后的全部列表框和表格,再更新当前要生成的下拉列表框。这里操做的参数是指当前要生成的下拉列表框所在的span元素的id。

 

对于采用prototype的添加和删除功能,获得全部弟弟并删除,增长新弟弟。有一点奇怪的是,使用$(sibling).remove方法,没法完成删除功能,但使用Element.remove(sibling)却能够,其中具体的差异在哪,尚未找出来。另外每次生成一个下拉列表框,而后再删除,再添加,下次生成的下拉列表框与上次生成的下拉列表框有必定的距离,这就是由于返回的<select>标签前面有空格致使的。也能够利用Prototype提供了Element.cleanWhiteSpace方法来清除全部空格。

 

每次添加新的下拉列表框前,若是不是第一个(经过调用函数时,有无传递当前元素这个参数来判断),则删除当前发生事件的下拉列表框之后的弟弟(包括全部后续列表框和表格),再新增一个下拉列表框,若是是第一个,则直接新加。这里的参数是当前正在发生事件的下拉列表框。

 

设计多级分类,可使用字符串类型的id,用id的值来表示级别和隶属关系,例如,001,001001,001002,001001001等。这是咱们十年前老是乐意当着一个经验向你们讲解的知识,如今因为掌握的新技术太多了,都忘记讲这些之前的经验了,多是人都有点喜新厌旧的习惯吧。其实,对于新手来讲,咱们这些老多年前的经验仍然是颇有价值的。

 

一道面试题:把span1变成span2,把span2变成span3,把span3变成span4,把span5变成span6

相关文章
相关标签/搜索