第3章 PHP与AJAX
各位看官是否用过Google提供的iGoogle服务(http://www.google.com/ig)?没有的话赶快试一下吧。重点不是说iGoogle服务多么强大,而是其页面上可自由拖动的栏目块,如图3-1所示。
图3-1 iGoogle首页
这种效果是怎么做出来的呢?
现在我们能够在浏览器中使用近似于桌面应用程序的效果,浏览网页时得到比10年前更好的用户体验,这得归功于越来越多的网站使用了一种名为AJAX的技术,它能够使客户端得到丰富的应用体验及交换操作,而用户不会感觉到有网页提交或刷新的过程,页面也不需要被重新加载,应用的数据交换都被隐藏。
事实上,AJAX并不是一种新的编程语言,而是一种用于创建更好、更快以及交互性更强的Web应用程序的技术:AJAX(Asynchronous JavaScript and XML,异步JavaScript和XML),是一种创建交互式网页应用的网页开发技术,这个术语源自描述从基于网页的Web应用到基于数据的应用的转换。它使用JavaScript的XMLHttpRequest对象来直接与服务器进行通信。通过这个对象,JavaScript可在不重载页面的情况下与Web服务器交换数据。AJAX在浏览器与Web服务器之间使用异步数据传输(HTTP请求),这样可使网页从服务器请求少量的信息,而不是整个页面。
在AJAX技术出现之前,Web站点往往强制用户进入“提交-等待-重新显示”的过程,用户的动作总是与服务器的“思考时间”同步。而AJAX提供与服务器异步通信的能力,从而使用户从“请求-响应”的循环中解脱出来。借助于AJAX,可以在用户单击按钮时,使用JavaScript和DHTML立即更新用户界面,并向服务器发出异步请求,以执行更新或查询数据库操作。当请求返回时,就可以使用JavaScript和CSS来相应地更新用户界面,而不是刷新整个页面。最重要的是,用户甚至不知道浏览器正在与服务器通信,这意味着Web站点看起来是即时响应的。
3.1 AJAX基本原理
当用户访问一个典型的支持AJAX的页面时,过程如图3-2所示。
图3-2 一个典型的AJAX调用
其中,AJAX的处理过程如图3-3所示。
图3-3 AJAX处理过程
图3-3中从事件触发开始,创建一个XMLHttpRequest对象,把HTTP方法(Get/Post)和目标URL以及请求返回后的回调函数设置到XMLHttpRequest对象,通过XMLHttpRequest向服务器发送请求,请求发送后继续响应用户的界面交互,只有等到请求真正从服务器返回的时候才调用callback()函数,对响应数据进行处理。
在此过程中,使用到了以下几种技术。
● XHTML和CSS:用来使表现标准化。
● DOM:动态地修改文档的内容和结构。
● XML:进行数据的交换和处理。
● JavaScript:对上述几种技术进行绑定,使其成为协同工作的整体。
3.1.1 XMLHttpRequest
XMLHttpRequest对象提供了对HTTP的完全访问,包括做出POST和HEAD请求以及普通的GET请求的能力。XMLHttpRequest可以同步或异步地返回Web服务器的响应,并且能够以文本或者一个DOM文档的形式返回内容。尽管名字叫做XMLHttpRequest,但它并不限于和XML文档一起使用——它可以接收任何形式的文本文档。
XMLHttpRequest得到了所有现在流行浏览器较好的支持。唯一的浏览器依赖性涉及XMLHttpRequest对象的创建。在IE 5和IE 6中,必须使用特定于IE的ActiveXObject()构造函数。
表3-1、表3-2和表3-3列出了标准的XMLHttpRequest方法、属性和句柄。
表3-1 标准XMLHttpRequest方法
表3-2 标准XMLHttpRequest属性
表3-3 标准XMLHttpRequest句柄
3.1.2 边学边练:简单的AJAX例子
下面这个例子使用简单的方式展现了AJAX的基本效果:无页面刷新更新局部内容。先来看看效果吧,图3-4是刚打开页面时的情况。
图3-4 刚打开页面时
单击“(单击这里查看作者)”链接,页面将会发生变化,但并没有刷新或跳转到另外一个页面,如图3-5所示。
图3-5 单击链接后的效果
实例演练
该实例由两个文件组成:simpleAjax.htm和data.xml,为了简单,没有编写任何PHP程序。这两个文件代码如下。
例3-1 更新页面中的局部内容,simpleAjax.htm文件的代码
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>一个简单的Ajax例子</title> <script type="text/javascript"><!-- function findAuthor(file){ var xmlObj = null; if(window.XMLHttpRequest){ //若浏览器支持XMLHttpRequest对象则新建XMLHttpRequest对象 xmlObj = new XMLHttpRequest(); } else if(window.ActiveXObject){ //若浏览器支持ActiveXObject对象则新建ActiveXObject对象 xmlObj = new ActiveXObject("Microsoft.XMLHTTP"); } else { return; } xmlObj.onreadystatechange = function(){ if(xmlObj.readyState == 4){ updateObj('author', xmlObj.responseXML.getElementsByTagName('name')[0].firstChild.data); } } xmlObj.open ('GET', file, true); xmlObj.send (''); } function updateObj(obj, data){ var textNode = document.createTextNode(data); document.getElementById(obj).appendChild(textNode); } //--></script> </head> <body> <h1>一个简单的Ajax例子</h1> <p id="obj">本页面由 <a id='link' href="data.xml" title="View the author." onclick="findAuthor('data.xml'); this.style.display='none'; return false">(单击这里查看作者)</a><span id="author" style="color: blue; fontweight: bolder"></span> 建立.</p> </body> </html>
例3-2 更新页面中的局部内容,data.xml文件的代码
<?xml version="1.0" encoding="UTF-8"?> <author> <name>Doctor@cqphp.com</name> </author>
AJAX的核心是XMLHttpRequest,本例通过下列代码创建了这个对象:
var xmlObj = null; if(window.XMLHttpRequest){ xmlObj = new XMLHttpRequest(); } else if(window.ActiveXObject){ xmlObj = new ActiveXObject("Microsoft.XMLHTTP"); } else { return; }
if(window.XMLHttpRequest)处理了浏览器是mozilla的情况,而if(window.ActiveXObject)则处理了浏览器为IE的情况,目的都是一个:创建一个XMLHttpRequest对象。创建这个对象的目的就是让XMLHttpRequest对象与服务器交互,而不影响用户正在浏览的页面。
创建好XMLHttpRequest对象之后,调用的函数将会监听该对象状态的变化,即onreadystatechange属性。XMLHttpRequest对象有5个状态:从0到4的整数,也就是说onreadystatechange调用的函数将会执行4次。本例中在该对象处于完成状态(readyState=4)时进行处理(此时服务器已经传回所有信息),为id为“author”的span对象添加了一个textNode节点,该textNode是data.xml文档中name节点的第一个子元素的值。
本例中使用了HTTP GET方法,当然也可以使用POST方法,但是要设定Content-Type值,即
xmlObj.setRequestHeader("Content-Type","application/x-www-formurlencoded")
xmlObj.open('GET',file,true)方法会建立与服务器的链接,GET指明了HTTP的调用方法;file指明了调用的url;true指明了该调用是异步处理,可省略,默认为true。
xmlObj.send(para)向服务器发出请求。是以POST向服务器发出request,其参数格式为name=namevalue&so=on。
3.1.3 使用jQuery实现动画效果
由John Resig创建的jQuery(http://jquery.com/)是一个开源项目,jQuery在一个紧凑的文件中提供了丰富多样的特性、简单易学的语法和稳健的跨平台兼容性。jQuery的宗旨是“写更少的代码,做更多的事情”。它是轻量级的JavaScript库(压缩后只有几十KB),这是其他的JavaScript库所不能及的,它兼容CSS3,还兼容各种浏览器(Internet Explorer 6.0+,Firefox 1.5+,Safari 2.0+,Opera 9.0+)。jQuery是一个快速的、简洁的JavaScript库,使用户能更方便地处理HTML documents、events、实现动画效果,并且方便地为网站提供AJAX交互。此外,成百上千为扩展jQuery而开发的插件更使得它几乎成为适用于各类客户端脚本编程的必备工具。
下边给出两个使用jQuery实现的动画实例:单击按钮提示与图片列表滑动。
1.单击按钮提示
该实例的功能较为简单:单击按钮后按钮消失,一个矩形伸展开来,在矩形中有一段提示文字。
单击按钮前的情况如图3-6所示。
图3-6 单击按钮前的情况
单击按钮后的效果如图3-7所示。
图3-7 单击按钮后的效果
该实例的代码如例3-3所示。
实例演练
例3-3 单击按钮提示
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>jQuery Animation</title> <script type="text/javascript" src="jquery-1.4.2.min.js"></script> <script type="text/javascript"> $(function(){ $('.jq-runCode').click(function(){ eval($(this).parent().find('code').text()); $(this).hide(); return false; }); }); </script> <style type="text/css"> body { text-align: center; } #wrap { text-align: left; margin: 0 auto; width: 500px; } pre { font-weight: normal; float: none; width: auto; margin: 0; margin-bottom: 10px; } pre code { display: block; padding: .8em .5em; font-size: 1em; border: 1px solid #AAA; } a.jq-runCode { font-size: 1.2em; padding: .3em 1.1em; color: #fff; font-weight: bold; text-decoration: none; text-transform: uppercase; border: 1px solid #000; background-color: #999; float: left; clear: left; margin: .1em 0; } p.neat { display: none; clear: both; margin: 1em 0; padding: 1em 15px; background: #F90; } p.surprise a { color: #fff; } span { font-size: 14px; } </style> </head> <body> <div id="wrap"> <span>jQuery代码:</span> <pre><code>$("p.neat").addClass("ohmy").show("slow");</code></pre> <a href="#" class="jq-runCode">运行代码</a> <p class="neat"><strong>恭喜!</strong> 上边的jQuery代码已经成功运行.</p> </div> </body> </html>
以上代码的重点在于自定义jQuery匿名函数、CSS样式表以及HTML代码三部分。
2.图片列表滑动
该实例实现以下效果:当鼠标在给出的5幅图片上划过,停留时所在的图片将变得较为清晰,连续过程仿佛图片列表产生滑动效果。
页面载入完毕后初始情形如图3-8所示。
图3-8 初始情形
当鼠标滑动到中间一幅图上并停止后,结果如图3-9所示。
图3-9 鼠标停止在中间图片上的效果
本实例代码如例3-4所示。
实例演练
例3-4 图片列表滑动
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>最新电影</title> <script type="text/javascript" src="jquery-1.4.2.min.js"></script> <script> $(document).ready(function(){ (function(){ $("#play_list").css("opacity","0.3"); //设置透明度 $("#play_list").children().clone().appendTo("#selector"); //复制元素 $("#selector").css({ left:$("#play_list").offset().left, top:$("#play_list").offset().top }); //定位选择器 $("#selector").scrollLeft(0); //回到第一帧 })(); var $intervalID = null $("#play_list a").mouseover(function(){ clearInterval(); var $move = $(this).offset().left - $(this).parent().offset().left; //目标滑动位置 var $left = $("#play_list").offset().left + $move; //目标左边距位置 var $m =$("#selector").scrollLeft(); //现滑动位置 var $l = parseInt($("#selector").css("left")); //现左边距位置 var $direct = ($l < $left)?1:-1; //滑动方向 var $multiple = ($left - $l ) *$direct / 12; //倍数 $intervalID = window.setInterval(function(){ $l+=$multiple*$direct; $m+=$multiple*$direct; if($l *$direct < $left*$direct){ $("#selector").css("left",$l).scrollLeft($m); }else{ $("#selector").css("left",$left).scrollLeft($move); clearInterval(); } }, 1); }); //清除前面的动画效果 function clearInterval(){ if($intervalID != null){ window.clearInterval($intervalID); $intervalID = null; } } }) </script> <style> a{text-decoration: none;} #selector img,#play_list img{width:120px; border:1px; } </style> </head> <body> <div id="selector" style="overflow:hidden; width:120px;white-space:nowrap;position:absolute;Z-index:100"></div> <div id="play_list"> <a href="http://kankan.xunlei.com/vod/movie/59/59280.shtml"> <img src="images/1.jpg" alt="《围捕》
;集中营的催泪童真" /> </a> <a href="http://kankan.xunlei.com/vod/movie/58/58869.shtml"> <img src="images/2.jpg" alt="《出水芙蓉》
;清凉夏日湿身爱情" /> </a> <a href="http://kankan.xunlei.com/vod/movie/58/58527.shtml"> <img src="images/3.jpg" alt="《第36个故事》
;桂纶镁清新爱情片" /> </a> <a href="http://kankan.xunlei.com/vod/movie/59/59240.shtml"> <img src="images/4.jpg" alt="《花园里的萤火虫》
;巨星云集温馨小品" /> </a> <a href="http://kankan.xunlei.com/vod/movie/59/59239.shtml"> <img src="images/5.jpg" alt="《开放日》
;北美热映恐怖猛片" /> </a> </div> </body> </html>
3.2 边学边练
在Web开发中,AJAX可以用于多种场合以增强用户体验、使Web应用更加人性化。以下给出两个常用的PHP+AJAX实例:搜索提示和无刷新登录。
3.2.1 实例一:搜索提示框
各位读者应该都用过Google和百度等搜索引擎,对于输入关键词后的提示功能是否记忆犹新呢(参见图3-10)?
图3-10 百度搜索的关键词提示功能
下面我们就在“2.4边学边练:编写PHP程序实现与MySQL交互——我的书架”节内容的基础上,实现与百度搜索效果类似的图书搜索功能。
假设此时“我的书架”中有以下几本书,如表3-4所示。
表3-4 图书信息
我们的目标是:当输入书名的一部分内容时能够实现与百度搜索效果类似的现有书籍名称提示。
先来看看完成后的程序界面,如图3-11所示。
图3-11 完成后的程序界面
当输入字母“d”时,出现了书名提示,如图3-12所示。
图3-12 输入字母“d”时出现书名提示
当输入“社区”时出现包含这两个字的书名提示,如图3-13所示。
图3-13 输入“社区”后出现包含这两个字的书名提示
是不是很吸引人?接下来给出实现这些功能的代码。
<?php require_once 'config.php'; if ($_GET['action'] == 'fetch') { $keywords = trim($_POST['keywords']); $keywords = iconv('UTF-8', 'GBK', $keywords); if ($keywords) { $keywords = strtoupper($keywords); //从数据库中查找所有书名包括$keyword的内容的数据 $rs = mysql_query("SELECT title FROM books WHERE UPPER(title) LIKE ('%$keywords%')"); while ($item = mysql_fetch_assoc($rs)) { echo "<li>$item[title]</li>"; } } exit; } ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>图书搜索</title> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript"> var curline = -1; var original = ''; function moveto(to) { if(!$('#suggestion_wrap:visible').size()){ request(); return; } var length = $('#suggestion li').removeClass('curline').size(); if(to<-1){ to = -1; } if(to>=length){ to = length - 1; } if(to>=0) { $('#keywords').val($('#suggestion li').eq(to).addClass('curline').text()); } else { $('#keywords').val(original); } curline = to; var obj = $('#keywords')[0]; if($.browser.msie) { var txt = obj.createTextRange(); txt.moveStart('character',obj.value.length); txt.collapse(true); txt.select(); } else { obj.selectionStart=obj.selectionEnd=obj.value.length; } } function request(){ original = $('#keywords').val(); $('#suggestion_wrap').hide(); $('#suggestion').empty().load('?action=fetch',{keywords:original},functio n(){ if(!$('#suggestion li').size()){ $('#suggestion_wrap').hide(); return; } $('#suggestion_wrap').show(); $('#suggestion li').each(function(i){ $(this).click(function() { choose(i); }); $(this).hover(function() { moveto(i); },function() { moveto(-1); }); }); }); } function move(step){ moveto(step+curline); } $(function(){ $('#keywords').keyup(function(event){ if( (event.keyCode>=48&&event.keyCode<=57) || (event.keyCode>=65&&event.keyCode<=90) || (event.keyCode>=65&&event.keyCode<=90) || (event.keyCode>=96&&event.keyCode<=107) || (event.keyCode>=109&&event.keyCode<=111) || (event.keyCode>=186&&event.keyCode<=222) || event.keyCode==8 || event.keyCode==32) { request(); } else { if(event.keyCode==38||event.keyCode==40){ move(event.keyCode==38?-1:1); return false; } else if (event.keyCode == 13||event.keyCode==108){ choose(curline); } } }); $(document).click(function(event){ var keyid =( event.target || event.srcElement).id; if(keyid!='keywords'&&keyid!='suggestion'){ $('#suggestion').empty(); $('#suggestion_wrap').hide(); curline = -1; } else if(keyid=='keywords'){ request(); } }); }); function choose(curline){ curline = -1; original = $('#suggestion li').eq(curline).text(); $('#suggestion').empty(); $('#suggestion_wrap').hide(); } </script> <style type="text/css"> body { text-align: center; } th,td { vertical-align: top; } th { line-height: 26px; } #wrap{ text-align: left; margin: 0 auto; padding-top: 10px; } #suggestion { padding: 0; margin: 0; } #keywords { background: none repeat scroll 0% 0% rgb(255, 255, 255); border-width: 1px; border-style: solid; border-color: rgb(204, 204, 204) rgb(153, 153, 153) rgb(153, 153, 153) rgb(204, 204, 204); color: rgb(0, 0, 0); font: 18px arial,sans-serif bold; height: 23px; margin: 0pt; padding: 0px; vertical-align: top; } #suggestion_wrap { background:none repeat scroll 0 0 white; border-color:-moz-use-text-color #E7E7E7 #E7E7E7 -moz-use-text-color; border-style:none solid solid none; border-width:0 1px 1px 0; display: none; } #suggestion { list-style: none; cursor: pointer; border-color:#A2BFF0 #558BE3 #558BE3 #A2BFF0; border-style:solid; border-width:1px; } #suggestion li { padding: 3px 0 1px 8px; } #suggestion li.curline{ background-color: #D5E2FF; } </style> </head> <body> <div id="wrap"> <table cellspacing="0" cellpadding="3"> <tr><th>请输入书名</th> <td><input type="text" autocomplete="off" size="30" id="keywords"/> <div id="suggestion_wrap"> <ul id="suggestion"></ul> </div> </td></tr> </table> <script type="text/javascript"> $("#keywords").focus(); </script> </div> </body> </html>
可以看到,本功能主要由从数据库中查询关键词的PHP程序和触发书名提示的JavaScript代码构成。
当在输入框中输入内容后,JavaScript脚本将会在后台访问“chap3-1.php?action=fetch”这个地址,并将输入框中的内容作为关键词从数据库中查找符合条件的记录,取得结果后显示在列表中以供用户选择。
需要注意的是:
1.SQL语句中的LIKE('%$keywords%')用于查询内容中包含PHP变量$keywords的数据,%在这里相当于通配符。
2.iconv()函数用于转换汉字编码,本例中因为数据库中保存的汉字是GBK编码,故将输入框提交的UTF-8编码的关键字转换后方可搜索。
3.2.2 实例二:无刷新登录
现在很多站点由于首页信息量较大,当用户登录时若采用传统的提交表单刷新页面的做法将会增加用户的等待时间,为了增强用户体验,多数站点采用了AJAX方式的登录过程。在AJAX方式的登录过程中,只有登录表单的局部刷新,避免了整个页面的全部刷新。
本例将模拟某网站的登录过程实现AJAX方式的局部刷新登录效果。照例,先来看一下完成后的界面,如图3-14所示。
图3-14 完成后的程序界面
当输入正确的用户名和密码并单击“登录”按钮后,页面没有刷新,但是登录框部分发生了变化,如图3-15所示。
图3-15 输入正确的用户名和密码后提示“登录成功”
当输入的用户名和密码不匹配时,登录框部分会给出“登录失败”的错误提示信息,如图3-16所示。
图3-16 当用户名和密码不匹配时提示“登录失败”
先给出数据库部分的表结构,为简单起见,只有用户名和密码两个字段,如表3-5所示。
表3-5 用户信息表users的结构
接下来看看实现上述功能的代码。
<?php require_once 'config.php'; if ($_GET['action'] == 'authlogin') { $username = trim($_POST['username']); $password = md5(trim($_POST['password'])); $rs = mysql_query("SELECT COUNT(*) FROM users WHERE username='$username' AND password='$password'"); $num = mysql_result($rs, 0); echo $num ? '登录成功! <script type="text/javascript">$("#loginform").hide("fast");</script>': '登录失败! '; exit; } ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=GBK"> <title>无刷新登录</title> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript"> $(function(){ $('#loginform').submit(function(){ $('#notice').show("fast").html('登录中...').load('?action=authlogin',$('#loginform').serializeArray()); return false; }); }); </script> <style type="text/css"> body { text-align: center; background-Figure-: url(chap3-2_bg.png); background-repeat: no-repeat; background-position: center top; } #wrap{ text-align: left; width: 250px; margin: 0 auto; position: relative; top: 100px; left: 145px; border-top: 1px solid #999; border-left: 1px solid #999; border-right: 2px solid #999; border-bottom: 2px solid #999; background-color: #FFF; } #notice { display: none; padding: 10px; font-size: 20px; font-weight: 700; } </style> </head> <body> <div id="wrap"> <div id="notice"></div> <form method="post" id="loginform"> <table> <tr><th>用户名</th><td><input type="text" name="username"/></td></tr> <tr><th>密码</th><td><input type="password" name="password"/></td></tr> <tr><td colspan="2"><input type="submit" value="登录"/></td></tr> </table> </form> </div> </body> </html>
本例与前例在PHP代码部分差异较小,而JavaScript部分的差异较大,可见,掌握了AJAX技术后灵活运用即可做出各种丰富多彩的效果,为Web应用增添活力。