鼠标点击事件包括 4 个:click(单击)、dblclick(双击)、mousedown(按下)和 mouseup(松开)。其中 click 事件类型比较常用,而 mousedown 和 mouseup 事件类型多用在鼠标拖放、拉伸操作中。当这些事件处理函数的返回值为 false 时,会禁止绑定对象的默认行为。
示例
在下面示例中,当定义超链接指向自身时(多在设计过程中 href 属性值暂时使用 “#”或“?”表示),可以取消超链接被单击时的默认行为,即刷新页面。
<a name="tag" id="tag" href="#">a</a>
<script>
var a = document.getElementsByTagName("a"); //获取页面中所有超链接元素
for (var i = 0; i < a.length; i ++) { //遍历所有a元素
if ((new RegExp(window.location.href)).test(a[i].href)) {
//如果当前超链接href属性中包含本页面的URL信息
a[i].onclick = function () { //则为超链接注册鼠标单击事件
return false; //将禁止超链接的默认行为
}
}
}
</script>
当单击示例中的超链接时,页面不会发生跳转(即禁止页面发生刷新效果)。
mousemove 事件类型是一个实时响应的事件,当鼠标指针的位置发生变化时(至少移动一个像素),就会触发 mousemove 事件。该事件响应的灵敏度主要参考鼠标指针移动速度的快慢以及浏览器跟踪更新的速度。
示例
下面示例演示了如何综合应用各种鼠标事件实现页面元素拖放操作的设计过程。实现拖放操作设计需要解决以下几个问题。
定义拖放元素为绝对定位以及设计事件的响应过程,这个比较容易实现。
清楚几个坐标概念:按下鼠标时的指针坐标,移动中当前鼠标指针坐标,松开鼠标时的指针坐标,拖放元素的原始坐标,拖动中的元素坐标。
算法设计:按下鼠标时,获取被拖放元素和鼠标指针的位置,在移动中实时计算鼠标偏移的距离,并利用该偏移距离加上被拖放元素的原坐标位置,获得拖放元素的实时坐标。
如下图所示,其中变量 ox 和 oy 分别记录按下鼠标时被拖放元素的纵横坐标值,它们可以通过事件对象的 offsetLeft 和 offsetTop 属性获取。变量 mx 和 my 分别表示按下鼠标时,鼠标指针的坐标位置。而 event.mx 和 event.my 是事件对象的自定义属性,用它们来存储当鼠标移动时鼠标指针的实时位置。
当获取了上面 3 对坐标值之后,就可以动态计算拖动中元素的实时坐标位置,即 x 轴值为 ox+event.mx-mx,y 轴为 oy+event.my-my。当释放鼠标按钮时,就可以释放事件类型,并记下松开鼠标指针时拖动元素的坐标值,以及鼠标指针的位置,留待下一次拖放操作时调用。
鼠标经过包括移过和移出两种事件类型。当移动鼠标指针到某个元素上时,将触发 mouseover 事件;而当把鼠标指针移出某个元素时,将触发 mouseout 事件。如果从父元素中移到子元素中时,也会触发父元素的 mouseover 事件类型。
示例
在下面示例中,分别为 3 个嵌套的 div 元素定义了 mouseover 和 mouseout 事件处理函数,这样当从外层的父元素中移动到内部的子元素中时,将会触发父元素的 mouseover 事件类型,但是不会触发 mouseout 事件类型。
<div>
<div>
<div>盒子</div>
</div>
</div>
<script>
var div = document.getElementsByTagName("div");
// 获取3个嵌套的div元素
for(var i=0;i<div.length;i++){ // 遍历嵌套的div元素
div[i].onmouseover = function(e){ // 注册移过事件处理函数
this.style.border = "solid blue";
}
div[i].onmouseout = function(){ // 注册移出事件处理函数
this.style.border = "solid red";
}
}
</script>
当一个事件发生后,可以使用事件对象的 target 属性获取发生事件的节点元素。如果在 IE 事件模型中实现相同的目标,可以使用 srcElement 属性。
示例1
在下面示例中,当鼠标移过页面中的 div 元素时,会弹出提示对话框,提示当前元素的节名称。
<div>div元素</div>
<script>
var div = document.getElementsByTagName("div")[0];
div.onmouseover = function(e){ // 注册mouseover事件处理函数
var e = e || window.event; // 标准化事件对象,兼容DOM和IE事件模型
var o = e.target || e.srcElement; // 标准化事件属性,获取当前事件的节点
alert(o.tagName); // 返回字符串“DIV”
}
</script>
在 DOM 事件模型中还定义了 currentTarget 属性,当事件在传播过程中(如捕获和冒泡阶段)时,该属性值与 target 属性值不同。因此,一般在事件处理函数中,有你哥哥使用该属性而不是 this 关键词获取当前对象。
除了使用上面提到的通用事件属性外,如果想获取鼠标指针来移动某个元素,在 DOM 事件模型中可以使用 relatedTarget 属性获取当前事件对象的相关节点元素;而在 IE 事件模型中,可以使用 fromElement 获取 mouseover 事件中鼠标移到过的元素,使用 toElement 属性获取在 mouseout 事件中鼠标移到的文档元素。
示例2
在下面示例中,当鼠标移到 div 元素上时,会弹出“BODY”字符提示信息,说明鼠标指针是从 body 元素过来的;而移开鼠标指针时,又弹出“BODY”字符提示信息,说明离开 div 元素将要移到的元素。
<div>div元素</div>
<script>
var div = document.getElementsByTagName("div")[0];
div.onmouseover = function(e){
var e = e || window.event;
var o = e.relatedTarget || e.fromElement; //标准化事件属性,获取与当前事件相关的节点
alert(o.tagName);
}
div.onmouseout = function(e){
var e = e || window.event;
var o = e.relatedTarget || e.toElement; // 标准化事件属性,获取与当前事件相关的节点
alert(o.tagName);
}
</script>
当事件发生时,获取鼠标的位置是件很重要的事件。由于浏览器的不兼容性,不同浏览器分别在各自事件对象中定义了不同的属性,说明如下表所示。这些属性都是以像素值定义了鼠标指针的坐标,但是由于它们参照的坐标系不同,导致精确计算鼠标的位置比较麻烦。
MouseEvent.screenX
属性返回鼠标位置相对于屏幕左上角的水平坐标(单位像素),MouseEvent.screenY
属性返回垂直坐标。这两个属性都是只读属性。
// HTML 代码如下
//
function showCoords(evt) {
console.log(
'screenX value: ' + evt.screenX + '\n',
'screenY value: ' + evt.screenY + '\n'
);
}
MouseEvent.offsetX
属性返回鼠标位置与目标节点左侧的padding
边缘的水平距离(单位像素),MouseEvent.offsetY
属性返回与目标节点上方的padding
边缘的垂直距离。这两个属性都是只读属性。
/* HTML 代码如下
Hello
*/
var p = document.querySelector('p');
p.addEventListener(
'click',
function (e) {
console.log(e.offsetX);
console.log(e.offsetY);
},
false
);
上面代码中,鼠标如果在p
元素的中心位置点击,会返回150 150
。因此中心位置距离左侧和上方的padding
边缘,等于padding
的宽度(100像素)加上元素内容区域一半的宽度(50像素)。
MouseEvent.pageX
属性返回鼠标位置与文档左侧边缘的距离(单位像素),MouseEvent.pageY
属性返回与文档上侧边缘的距离(单位像素)。它们的返回值都包括文档不可见的部分。这两个属性都是只读。
/* HTML 代码如下
*/
document.body.addEventListener(
'click',
function (e) {
console.log(e.pageX);
console.log(e.pageY);
},
false
);
上面代码中,页面高度为2000像素,会产生垂直滚动条。滚动到页面底部,点击鼠标输出的pageY
值会接近2000。
MouseEvent.relatedTarget
属性返回事件的相关节点。对于那些没有相关节点的事件,该属性返回null
。该属性只读。
下表列出不同事件的target
属性值和relatedTarget
属性值义。
事件名称 | target 属性 | relatedTarget 属性 |
---|---|---|
focusin | 接受焦点的节点 | 丧失焦点的节点 |
focusout | 丧失焦点的节点 | 接受焦点的节点 |
mouseenter | 将要进入的节点 | 将要离开的节点 |
mouseleave | 将要离开的节点 | 将要进入的节点 |
mouseout | 将要离开的节点 | 将要进入的节点 |
mouseover | 将要进入的节点 | 将要离开的节点 |
dragenter | 将要进入的节点 | 将要离开的节点 |
dragexit | 将要离开的节点 | 将要进入的节点 |
下面是一个例子。
/*
HTML 代码如下
*/
var inner = document.getElementById('inner');
inner.addEventListener('mouseover', function (event) {
console.log('进入' + event.target.id + ' 离开' + event.relatedTarget.id);
}, false);
inner.addEventListener('mouseenter', function (event) {
console.log('进入' + event.target.id + ' 离开' + event.relatedTarget.id);
});
inner.addEventListener('mouseout', function (event) {
console.log('离开' + event.target.id + ' 进入' + event.relatedTarget.id);
});
inner.addEventListener("mouseleave", function (event){
console.log('离开' + event.target.id + ' 进入' + event.relatedTarget.id);
});
// 鼠标从 outer 进入inner,输出
// 进入inner 离开outer
// 进入inner 离开outer
// 鼠标从 inner进入 outer,输出
// 离开inner 进入outer
// 离开inner 进入outer
通过事件对象的 button 属性可以获取当前鼠标按下的键,该属性可用于 click、mousedown、mouseup 事件类型。不过不同模型的约定不同,具体说明如下表所示。
IE 事件模型支持位掩码技术,它能够侦测到同时按下的多个键。例如,同时按下左右键,则 button 属性值为 1+2=3;同时按下中键和右键,则 button 属性值为 2+4=6;同时按下左键和中键,则 button 属性值为 1+4=5;同时按下 3 个键,则 button 属性值为 1+2+4=7。
但是 DOM 模型不支持这种掩码技术,如果同时按下多个键,就不能够准确侦测。例如,按下右键(2)与同时按下左键和右键(0+2=2)的值是相同的。因此,对于 DOM 模型来说,这种 button 属性约定值存在很大的缺陷。不过,在实际开发中很少需要同时检测多个鼠标按钮问题,一般仅需要探测鼠标左键或右键单击行为。
示例
下面代码能够监测右键单击操作,并阻止发生默认行为。
document.onclick = function (e) {
var e = e || window.event; //标准化事件对象
if (e.button == 2) {
e.preventDefault();
return false;
}
}
当鼠标单击事件发生时,会触发很多事件:mousedown、mouseup、click、dblclick。这些事件响应的顺序如下:
mousedown → mouseup → click → mousedown → mouseup → click → dblclick
当鼠标在对象间移动时,首先触发的事件是 mouseout,即在鼠标移出某个对象时发生。接着,在这两个对象上都会触发 mousemove 事件。最后,在鼠标进入对象上触发 mouseover 事件。
CORS 是一个 W3C 标准,全称是“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨域的服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能。
整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与普通的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感知。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨域通信。
CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
(1)请求方法是以下三种方法之一。
HEAD
GET
POST
(2)HTTP 的头信息不超出以下几种字段。
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是不同时满足上面两个条件,就属于非简单请求。一句话,简单请求就是简单的 HTTP 方法与简单的 HTTP 头信息的结合。
这样划分的原因是,表单在历史上一直可以跨域发出请求。简单请求就是表单请求,浏览器沿袭了传统的处理方式,不把行为复杂化,否则开发者可能转而使用表单,规避 CORS 的限制。对于非简单请求,浏览器会采用新的处理方式。
对于简单请求,浏览器直接发出 CORS 请求。具体来说,就是在头信息之中,增加一个Origin字段。
下面是一个例子,浏览器发现这次跨域 AJAX 请求是简单请求,就自动在头信息之中,添加一个Origin字段。
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
上面的头信息中,Origin字段用来说明,本次请求来自哪个域(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
如果Origin指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为 HTTP 回应的状态码有可能是200。
如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
上面的头信息之中,有三个与 CORS 请求相关的字段,都以Access-Control-开头。
(1)Access-Control-Allow-Origin
该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。
(2)Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送 Cookie。默认情况下,Cookie 不包括在 CORS 请求之中。设为true,即表示服务器明确许可,浏览器可以把 Cookie 包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送 Cookie,不发送该字段即可。
(3)Access-Control-Expose-Headers
该字段可选。CORS 请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个服务器返回的基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader(‘FooBar’)可以返回FooBar字段的值。
上面说到,CORS 请求默认不包含 Cookie 信息(以及 HTTP 认证信息等)。如果需要包含 Cookie 信息,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。
Access-Control-Allow-Credentials: true
另一方面,开发者必须在 AJAX 请求中打开withCredentials属性。
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
否则,即使服务器同意发送 Cookie,浏览器也不会发送。或者,服务器要求设置 Cookie,浏览器也不会处理。
但是,如果省略withCredentials设置,有的浏览器还是会一起发送 Cookie。这时,可以显式关闭withCredentials。
xhr.withCredentials = false;
需要注意的是,如果要发送 Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie 依然遵循同源政策,只有用服务器域名设置的 Cookie 才会上传,其他域名的 Cookie 并不会上传,且(跨域)原网页代码中的document.cookie也无法读取服务器域名下的 Cookie。
非简单请求是那种对服务器提出特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为“预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。这是为了防止这些新增的请求,对传统的没有 CORS 支持的服务器形成压力,给服务器一个提前拒绝的机会,这样可以防止服务器大量收到DELETE和PUT请求,这些传统的表单不可能跨域发出的请求。
下面是一段浏览器的 JavaScript 脚本。
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
上面代码中,HTTP 请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header。
浏览器发现,这是一个非简单请求,就自动发出一个“预检”请求,要求服务器确认可以这样请求。下面是这个“预检”请求的 HTTP 头信息。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
“预检”请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。
除了Origin字段,“预检”请求的头信息包括两个特殊字段。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法,上例是PUT。
(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段,上例是X-Custom-Header。
预检请求的回应
服务器收到“预检”请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
上面的 HTTP 回应中,关键的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
Access-Control-Allow-Origin: *
如果服务器否定了“预检”请求,会返回一个正常的 HTTP 回应,但是没有任何 CORS 相关的头信息字段,或者明确表示请求不符合条件。
OPTIONS http://api.bob.com HTTP/1.1
Status: 200
Access-Control-Allow-Origin: https://notyourdomain.com
Access-Control-Allow-Method: POST
上面的服务器回应,Access-Control-Allow-Origin字段明确不包括发出请求的http://api.bob.com。
这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
服务器回应的其他 CORS 相关字段如下。
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
(1)Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次“预检”请求。
(2)Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在“预检”中请求的字段。
(3)Access-Control-Allow-Credentials
该字段与简单请求时的含义相同。
(4)Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
一旦服务器通过了“预检”请求,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。
下面是“预检”请求之后,浏览器的正常 CORS 请求。
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
上面头信息的Origin字段是浏览器自动添加的。
下面是服务器正常的回应。
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
上面头信息中,Access-Control-Allow-Origin字段是每次回应都必定包含的。
CORS 与 JSONP 的使用目的相同,但是比 JSONP 更强大。JSONP 只支持GET请求,CORS 支持所有类型的 HTTP 请求。JSONP 的优势在于支持老式浏览器,以及可以向不支持 CORS 的网站请求数据。
在 JavaScript 中,XMLHttpRequest 是客户端的一个 API,它为浏览器与服务器通信提供了一个便捷通道。现代浏览器都支持 XMLHttpRequest API,如 IE 7+、Firefox、Chrome、Safari 和 Opera。
XMLHttpRequest 用于在后台与服务器交换数据。创建 XMLHttpRequest 对象的方法如下:
var xhr = new XMLHttpRequest ();
IE 5.0 版本开始以 ActiveX 组件形式支持 XMLHttpRequest,IE 7.0 版本开始标准化 XMLHttpRequest。不过所有浏览器实现的 XMLHttpRequest 对象都提供相同的接口和用法。
在 JavaScript 中,使用 XMLHttpRequest 对象的 open() 方法可以建立一个 HTTP 请求。用法如下:
xhr.open(method, url, async, username, password);
其中 xhr 表示 XMLHttpRequest 对象,open() 方法包含 5 个参数,说明如下:
method:HTTP 请求方法,必须参数,值包括 POST、GET 和 HEAD,大小写不敏感。
url:请求的 URL 字符串,必须参数,大部分浏览器仅支持同源请求。
async:指定请求是否为异步方式,默认为 true。如果为 false,当状态改变时会立即调用 onreadystatechange 属性指定的回调函数。
username:可选参数,如果服务器需要验证,该参数指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。
password:可选参数,验证信息中的密码部分,如果用户名为空,则该值将被忽略。
建立连接后,可以使用 send() 方法发送请求。用法如下:
xhr.send(body);
参数 body 表示将通过该请求发送的数据,如果不传递信息,可以设置为 null 或者省略。
发送请求后,可以使用 XMLHttpRequest 对象的 responseBody、responseStream、responseText 或 responseXML 属等待接收响应数据。
在 JavaScript 中,发送 GET 请求简单、方便,适用于简单字符串,不适用于大容量或加密数据。实现方法:将包含查询字符串的 URL 传入 open() 方法,设置第 1 个参数值为 GET 即可。服务器能够通过查询字符串接收用户信息。
示例
下面示例以 GET 方式向服务器传递一条信息 callback=functionName。
<input name="submit" type="button" id="submit" value="向服务器发出请求" />
<script>
window.onload = function () { //页面初始化
var b = document.getElementsByTagName("input")[0];
b.onclick = function () {
var url = "server.php?callback=functionName"; //设置查询字符串
var xhr = createXHR(); //实例化XMLHttpRequest 对象
xhr.open("GET", url, false); //建立连接,要求同步响应
xhr.send(null); //发送请求
console.log(xhr.responseText); //接收数据
}
}
</script>
在服务器端文件(server.php)中输入下面的代码,获取查询字符串中 callback 的参数值,并把该值响应给客户端。
<?php
echo $_GET["callback"];
?>
在浏览器中预览页面,当单击提交按钮时,在控制台显示传递的参数值。
查询字符串通过问号?作为前缀附加在 URL 的末尾,发送数据是以连字符&连接的一个或多个名值对。
在 JavaScript 中,POST 请求允许发送任意类型、长度的数据,多用于表单提交,以 send() 方法进行传递,而不以查询字符串的方式进行传递。POST 字符串与 GET 字符串的格式相同。格式如下:
send("name1=value1&name2=value2...");
GET 和 POST 方法都是以名值对的字符串格式发送数据的。
对象信息
下面是一个包含 3 个名值对的 JSON 类型数据。
{ user : "css8", pass : "123456", email : "[email protected]" }
将 JSON 数据转换为串行格式化显示如下。
'user="css8" & pass="123456" & email="[email protected]"'
数组信息
下面是一组有序的 JSON 信息,包含多个值。
[{name : "user", value : "css8"} , {name : "pass", value : "123456"), {name : "email", value : "[email protected]"}]
将上面数据转换为串行格式显示如下。
'user="css8" & pass="123456" & email="[email protected]"'
在 JavaScript 中,使用 readyState 属性可以实时跟踪异步响应状态。当该属性值发生变化时,会触发 readystatechange 事件,调用绑定的回调函数。readyState 属性值说明如表所示。
考虑到各种特殊情况,更安全的方法是同时监测 HTTP 状态码,只有当 HTTP 状态码为 200 时,才说明 HTTP 响应顺利完成。
使用 abort() 方法可以中止正在进行的请求。用法如下:
xhr.onreadystatechange = function () {}; //清理事件响应函数
xhr.abort(); //中止请求
在调用 abort() 方法前,应先清除 onreadystatechange 事件处理函数,因为 IE 和 Mozilla 在请求中止后也会激活这个事件处理函数。如果给 onreadystatechange 属性设置为 null,则 IE 会发生异常,所以为它设置一个空函数。
XMLHttpRequest 对象通过 responseText、responseBody、responseStream 或 responseXML 属性获取响应信息,说明如下表所示,它们都是只读属性。
在实际应用中,一般将格式设置为 XML、HTML、JSON 或其他纯文本格式。具体使用哪种响应格式,可以参考下面几条原则。
如果向页面中添加大块数据,选择 HTML 格式会比较方便。
如果需要协作开发,且项目庞杂,选择 XML 格式会更通用。
如果要检索复杂的数据,且结构复杂,那么选择 JSON 格式更加轻便。
设计响应信息为 HTML 字符串,然后使用 innerHTML 把获取的字符串插入到网页中。
示例
在服务器端设计响应信息为 HTML 结构代码。
<table border="1" width="100%">
<tr><td>RegExp.exec()</td><td>通用的匹配模式</td></tr>
<tr><td>RegExp.test()</td><td>检测一个字符串是否匹配某个模式</td></tr>
</table>
然后在客户端可以这样接收响应信息。
<input name="submit" type="button" id="submit" value="向服务器发出请求" />
<script>
window.onload = function () { //页面初始化
var b = document.getElementsByTagName("input")[0];
b.onclick = function () {
var xhr = createXHR(); //实例化XMLHttpRequest对象
xhr.open("GET", "server.xml", true); //建立连接,要求异步响应
xhr.onreadystatechange = function () { //绑定响应状态事件监听函数
if (xhr.readyState == 4) { //监听readyState状态
if (xhr.state == 200 || xhr.status == 0) { //监听HTTP状态码
var o = document.getElementById("grid");
o.innerHTML = xhr.responseText; //直接插入到页面中
}
}
}
xhr.send(); //发送请求
}
}
</script>
在某些情况下,HTML 字符串可能为客户端解析响应信息节省了一些 JavaScript 脚本,但是也带来了一些问题。
响应信息中包含大量无用的字符,响应数据会变得很臃肿。因为 HTML 标记不含有信息,完全可以把它们放置在客户端,由 JavaScript 脚本负责生成。
响应信息中包含的 HTML 结构无法有效利用,对于 JavaScript 脚本来说,它们仅仅是一堆字符串。同时结构和信息混合在一起,也不符合标准化设计原则。
设计相应为 JavaScript 代码,与 JSON 数据不同,它是可执行的命令或脚本。
示例
在服务器端请求文件中包含下面一个函数。
function () {
var d = new Date();
return d.toString();
}
然后在客户端执行下面的请求。
<input name="submit" type="button" id="submit" value="向服务器发出请求" />
<script>
window.onload = function () { //页面初始化
var b = document.getElementsByTagName("input")[0];
b.onclick = function () {
var xhr = createXHR(); //实例化XMLHttpRequest对象
xhr.open("GET", "server.xml", true); //建立连接,要求异步响应
xhr.onreadystatechange = function () { //绑定响应状态事件监听函数
if (xhr.readyState == 4) { //监听readyState状态
if (xhr.state == 200 || xhr.status == 0) { //监听HTTP状态码
var info = xhr.responseText;
var o = eval("(" + info + ")" + "()"); //用eval()把字符串转换为脚本
console.log(o); //返回客户端当前信息
}
}
}
xhr.send(); //发送请求
}
}
</script>
使用 eval() 方法时,在字符串前后附加两个小括号:一个是包含函数结构体的,一个是表示调用函数的。不建议直接使用 JavaScript 代码作为响应格式,因为它不能传递更丰富的信息,同时 JavaScript 脚本极易引发安全隐患。
使用 responseText 可以获取 JSON 格式的字符串,然后使用 eval() 方法将其解析为本地 JavaScript 脚本,再从该数据对象中读取信息。
示例
在服务器端请求文件中包含下面 JSON 数据。
{ user : "css8", pass : "123456", email : "[email protected]" }
然后在客户端执行下面的请求。把返回 JSON 字符串转换为对象,然后读取属性值。
<input name="submit" type="button" id="submit" value="向服务器发出请求" />
<script>
window.onload = function () { //页面初始化
var b = document.getElementsByTagName("input")[0];
b.onclick = function () {
var xhr = createXHR(); //实例化XMLHttpRequest对象
xhr.open("GET", "server.xml", true); //建立连接,要求异步响应
xhr.onreadystatechange = function () { //绑定响应状态事件监听函数
if (xhr.readyState == 4) { //监听readyState状态
if (xhr.state == 200 || xhr.status == 0) { //监听HTTP状态码
var info = xhr.responseText;
var o = eval("(" + info + ")"); //调用eval()把字符串转换为本地脚本
console.log(info); //显示JSON对象字符串
console.log(o.user); //读取对象属性值,返回字符串“css8”
}
}
}
xhr.send(); //发送请求
}
}
</script>
eval() 方法在解析 JSON 字符串时存在安全隐患。如果 JSON 字符串中包含恶意代码,在调用回调函数时可能会被执行。解决方法:先对 JSON 字符串进行过滤,屏蔽掉敏感或恶意代码。不过,确信所响应的 JSON 字符串是安全的,没有被人恶意攻击,那么可以使用 eval() 方法解析 JSON 字符串。
对于简短的信息,可以使用纯文本格式进行响应。但是纯文本信息在传递过程中容易丢失,且没有办法检测信息的完整性。
示例
服务器端响应信息为字符串“true”,则可以在客户端这样设计。
var xhr = createXHR(); //实例化XMLHttpRequest对象
xhr.open("GET", "server.txt", true); //建立连接,要求异步响应
xhr.nreadystatechange = function () { //绑定响应状态事件监听函数
if (xhr.readyState == 4) { //监听readyState函数
if (xhr.status == 200 || xhr.status == 0) { //监听HTTP状态码
var info = xhr.responseText;
if (info == "true") console.log("文本信息传输完整"); //检测信息是否完整
else console.log("文本信息可能存在丢失");
}
}
}
xhr.send(); //发送请求
HTTP 请求和响应都包含一组头部消息,获取和设置头部消息可以使用下面两个方法。
getAllResponseHeaders():获取响应的 HTTP头部消息。
getResponseHeader(“Header-name”):获取指定的 HTTP 头部消息。
示例
下面示例将获取 HTTP 响应的所有头部消息。
var xhr = createXHR();
var url = "server.txt";
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(xhr.getAllResponseHeaders());
}
}
xhr.send(null);
如果要获取指定的某个头部消息,可以使用 getResponseHeader() 方法,参数为获取头部的名称。例如,获取 Content-Type 头部的值,可以这样设计。
console.log(xhr.getResponseHeader(“Content-Type”));
除了可以获取这些头部消息外,还可以使用 setResponseHeader() 方法在发送请求中设置各种头部消息。用法如下:
xhr.setResponseHeader("Header-name", "value");
其中 Header-name 表示头部消息的名称,value 表示消息的具体值。例如,使用 POST 方法传递表单数据,可以设置如下头部消息。
xhr.setResponseHeader("Content-Type", "application/x-www-form-urlencoded");
什么是 Cookie?
Cookie 是一些数据, 存储于你电脑上的文本文件中。
当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息。
Cookie 的作用就是用于解决 “如何记录客户端的用户信息”:
当用户访问 web 页面时,他的名字可以记录在 cookie 中。
在用户下一次访问该页面时,可以在 cookie 中读取用户访问记录。
Cookie 以名/值对形式存储,如下所示:
username=John Doe
当浏览器从服务器上请求 web 页面时, 属于该页面的 cookie 会被添加到该请求中。服务端通过这种方式来获取用户的信息。
JavaScript 可以使用 document.cookie 属性来创建 、读取、及删除 cookie。
JavaScript 中,创建 cookie 如下所示:
document.cookie="username=John Doe";
您还可以为 cookie 添加一个过期时间(以 UTC 或 GMT 时间)。默认情况下,cookie 在浏览器关闭时删除:
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT";
您可以使用 path 参数告诉浏览器 cookie 的路径。默认情况下,cookie 属于当前页面。
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
在 JavaScript 中, 可以使用以下代码来读取 cookie:
var x = document.cookie;
Note document.cookie 将以字符串的方式返回所有的 cookie,类型格式: cookie1=value; cookie2=value; cookie3=value;
在 JavaScript 中,修改 cookie 类似于创建 cookie,如下所示:
document.cookie="username=John Smith; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";
旧的 cookie 将被覆盖。
删除 cookie 非常简单。您只需要设置 expires 参数为以前的时间即可,如下所示,设置为 Thu, 01 Jan 1970 00:00:00 GMT:
document.cookie = “username=; expires=Thu, 01 Jan 1970 00:00:00 GMT”;
注意,当您删除时不必指定 cookie 的值。
document.cookie 属性看起来像一个普通的文本字符串,其实它不是。
即使您在 document.cookie 中写入一个完整的 cookie 字符串, 当您重新读取该 cookie 信息时,cookie 信息是以名/值对的形式展示的。
如果您设置了新的 cookie,旧的 cookie 不会被覆盖。 新 cookie 将添加到 document.cookie 中,所以如果您重新读取document.cookie,您将获得如下所示的数据:
cookie1=value; cookie2=value;
显示所有 Cookie 创建 Cookie 1 创建 Cookie 2 删除 Cookie 1 删除 Cookie 2
如果您需要查找一个指定 cookie 值,您必须创建一个JavaScript 函数在 cookie 字符串中查找 cookie 值。
在以下实例中,我们将创建 cookie 来存储访问者名称。
首先,访问者访问 web 页面, 他将被要求填写自己的名字。该名字会存储在 cookie 中。
访问者下一次访问页面时,他会看到一个欢迎的消息。
在这个实例中我们会创建 3 个 JavaScript 函数:
设置 cookie 值的函数
获取 cookie 值的函数
检测 cookie 值的函数
首先,我们创建一个函数用于存储访问者的名字:
function setCookie(cname,cvalue,exdays)
{
var d = new Date();
d.setTime(d.getTime()+(exdays*24*60*60*1000));
var expires = "expires="+d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
函数解析:
以上的函数参数中,cookie 的名称为 cname,cookie 的值为 cvalue,并设置了 cookie 的过期时间 expires。
该函数设置了 cookie 名、cookie 值、cookie过期时间。
然后,我们创建一个函数用于返回指定 cookie 的值:
function getCookie(cname)
{
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++)
{
var c = ca[i].trim();
if (c.indexOf(name)==0) return c.substring(name.length,c.length);
}
return "";
}
函数解析:
cookie 名的参数为 cname。
创建一个文本变量用于检索指定 cookie :cname + “=”。
使用分号来分割 document.cookie 字符串,并将分割后的字符串数组赋值给 ca (ca = document.cookie.split(’;’))。
循环 ca 数组 (i=0;i 如果找到 cookie(c.indexOf(name) == 0),返回 cookie 的值 (c.substring(name.length,c.length)。 如果没有找到 cookie, 返回 “”。 最后,我们可以创建一个检测 cookie 是否创建的函数。 如果设置了 cookie,将显示一个问候信息。 如果没有设置 cookie,将会显示一个弹窗用于询问访问者的名字,并调用 setCookie 函数将访问者的名字存储 365 天: 完整实例检测 cookie 值的函数
function checkCookie()
{
var username=getCookie("username");
if (username!="")
{
alert("Welcome again " + username);
}
else
{
username = prompt("Please enter your name:","");
if (username!="" && username!=null)
{
setCookie("username",username,365);
}
}
}
实例function setCookie(cname,cvalue,exdays){
var d = new Date();
d.setTime(d.getTime()+(exdays*24*60*60*1000));
var expires = "expires="+d.toGMTString();
document.cookie = cname+"="+cvalue+"; "+expires;
}
function getCookie(cname){
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name)==0) { return c.substring(name.length,c.length); }
}
return "";
}
function checkCookie(){
var user=getCookie("username");
if (user!=""){
alert("欢迎 " + user + " 再次访问");
}
else {
user = prompt("请输入你的名字:","");
if (user!="" && user!=null){
setCookie("username",user,30);
}
}
}