jQuery+PHP 大话ajax免刷新提交表单与切换页面
昨天折腾了一个晚上,今天在抽空折腾的东西,不记下来未免太浪费了,于是我决定花晚上把它写下来。
Ajax是目前最流行的优化用户体验的方法,但在过去,他往往代表着牺牲SEO以及抛弃浏览器后退前进功能,但是现在,已经不是这样了。
现在的Ajax完全可以做到兼备,所以你可以看到,基本上,在你提交表单之后再也不用跳转之后才告诉你你的输入是否正确,甚至在搜索时,一边输入字符,一边进行检索。
这些都是Ajax所带来的便利,但是鉴于我还没有前后端整站写过(只有使用前后框架[PHP MVC]以及前端的经历),所以对整个流程还不能说熟悉。这次,我们从后端写到前端,一点点来解刨Ajax的套路。
首先,我们前端的核心当然是$.ajax
啦,没有他我们怎么实现ajax呢?在之前,我已经写过ajax的表单提交了,所以表单其实是很好上手的,相比之下,页面切换的处理要麻烦很多。
关于jQuery中的$.ajax
,这里有w3school和官方文档,w3school的参数不是很全,但是是中文的。中文文档不知道全不全:http://jquery.bootcss.com/jQuery.ajax/
好了,不多做介绍了,之后用到什么讲什么吧。
首先,我们用html建立一个传统意义上的表单,其中method="post"
:
<form class="login" method="post">
<input type="password" placeholder="密码" name="password">
<input type="submit" value="提交">
</form>
如果是PHP的话,应该还有action,但我们用ajax传输,就不用这么写了。
然后我们用jQuery写个点击事件:
$('.login>input[type="submit"]').click(function() {
$.ajax({
type: 'POST',
url: 'core/login.php',
data: $('.login').serialize(),
dataType: 'json',
beforeSend: function() {
$('.tip').html('Loading');
},
success: function(data) {
if (data == '200') {
$('.tip').html('Success');
$('.tip').css({color: 'green'});
setTimeout(function(){
pajax('microblog.php', 'Hello');
}, 300);
} else if (data == '403') {
$('.tip').html('Error Password');
$('.tip').css({color: 'red'});
} else {
$('.tip').css({color: '#000'});
$('.tip').html('Develop Error');
}
},
error: function() {
$('.tip').html('Something Wrong');
}
});
return false;
});
呀,进度好像过快了,但就是这样。$('.login').serialize()
把login
传过来的值序列化为对象了。这里剩下的似乎都没什么好说的,唯一要说的是return false
,这一点在于取消默认行为,默认来说,使用submit之后,页面会有一个跳转(或者说刷新)操作,跳转到action,但我们不需要这个多余的动作,所以用它阻止冒泡。
然后开始写PHP部分,也就是上文要传输过去的core/login.php
。
<?php
session_start();
// array_key_exists('password', $_POST)
if (isset($_POST['password'])) {
$pass = md5($_POST['password']);
if ($pass == md5('Kurama')) {
$_SESSION['login'] = true;
echo json_encode('200');
} else {
echo json_encode('403');
}
} else {
echo json_encode('404');
}
?>
是怎么回事呢?我们照理写我们的PHP部分,其实是不干扰的,只要传出json_encode()
转换为json对象就好了。
剩下的就跟PHP一样处理,包括写入session等部分。
如果要检测数组是否有某个值,也可以使用:array_key_exists('password', $_POST)
,其实我是嫌这货太长- -#
好了,表单讲完了,烦人的是,我们要开始试着做局部或者全局Ajax了,这个好麻烦呀,之前没有试过呀。
于是就有了下面的一大堆资料来学习,大致上是这样的。
首先我们来说说全局的ajax。
这一点其实并不难,我们需要在ajax
里type使用html来获取,并且加入就可以了,大致Like that.
var pajax = function(targetUrl, targetTitle) {
$.ajax({
dataType: 'html',
type: 'GET',
url: targetUrl,
headers: {
Pjax: true
},
success: function(data) {
$('body').contents().remove();
$('body').append(data);
console.log(data);
$('title').html(targetTitle);
window.history.pushState({type:'url', url: targetUrl, title: targetTitle}, 'Hello', targetUrl);
}
});
};
关于window.history的部分,之后在做解释。
我们可以看到,这样就把body里的部分移除了,然后在append过去,基本上是没什么问题的。
当然,这里我们需要PHP的配合,否则的话就会出现把全部页面重复加载到body里的问题。
在这里,我们在ajax时设定了一个header,之后就会用上了。
在PHP部分,通常我们会选择将网页分成三个部分,header.php, xxx.php, footer.php,这里,我们给header和footer加个判断(xxx.php存放的是差异部分,也是需要remove的部分)。
<?php
if (!isset($_SERVER['HTTP_PJAX']))
include('common/header.php');
?>
在这里,我们使用$_SERVER['HTTP_PJAX']
的存在与否来检查是否要载入header和footer,于是,就能分成:可以正常直接访问页面,以及可以正常ajax加载了。
接下来,我们前面说到,关于SEO的问题,也是为了刷新访问正常,我们需要在Ajax之后改变地址栏,这也就是后面一大堆参考的攻略点了。
我们需要用到的也就是操作历史记录(pushstate&popstate),pushState
可以做到免刷新修改url,这点就是和window.location的不同之处了。
window.history.pushState({type:'url', url: targetUrl, title: targetTitle}, targetTitle, targetUrl);
这句话起到的作用也就在于此了。这里需要注意的是,传入的值都是相对于目标页面(也就是Ajax载入的页面)而言的,而不是原来的页面。所以我们这里用target来表示。
做到这里,还不够,因为我们还需要知道用户什么时候点击了后退前进按钮,这里需要使用到popstate监听:
window.addEventListener('popstate', function(e){
var url;
var title;
if (window.history.state){
var state = e.state;
url = state.url;
title = state.title;
type = state.type;
}
if (type != 'page') {
$.ajax({
dataType: 'html',
type: 'GET',
url: url,
headers: {
Pjax: true
},
success: function(data) {
$('body').contents().remove();
$('body').append(data);
console.log(data);
$('title').html(title);
}
});
} else {
page.pjax = true;
page.switchPage(state.pageNumber);
}
});
主要还是看这段:
if (window.history.state){
var state = e.state;
url = state.url;
title = state.title;
type = state.type;
}
这里储存了我们的保留数据:pushState的第一个参数。然后根据参数来判断就好了。
当然,这里有个坑:最开始直接访问的地址是没有window.history.state的,我本来想着保留第一次访问的数据,通过监听内判断有没有window.history.state来决定。
结果发现如果在别的地方我还要使用局部ajax的话,这种写法太渣了,根本不能用,于是我们这么做:
if (!history.state) {
window.history.replaceState({type: 'url', pageNumber: page.currentPage, title: 'microblog'}, 'Hello', 'microblog.php');
}
使用replaceState,我们覆盖历史记录,参数同pushState部分。计划通。
最后,我们来试试局部Ajax,其实理论上而言这会比全局简单,因为只要删除部分DOM,然后加上嘛~实际上还是在地址栏以及前进后退的问题。
如果我们不进行一些判断操作,那么很快就会陷入 读取数据 - back修改链接并写入相关信息 然后重复这个操作,于是如同上面的源码,我们通过设置一个pjax等不等于true来解决这个问题。同时,针对是局部还是全部加载,我们新设置了一个type属性。
对应判断:
Page.prototype.switchPage = function(targetPageNumber) {
this.getPageNumber(function() {
message.getMessage(targetPageNumber);
this.changePages(targetPageNumber);
var targetUrl = 'microblog.php#' + targetPageNumber;
console.log(this.pjax);
if (!this.pjax) {
window.history.pushState({type: 'page', pageNumber: targetPageNumber, title: 'MicroBlog', url: targetUrl}, 'MicroBlog', targetUrl);
}
this.pjax = false;
});
}
计划通在这里:
if (!this.pjax) {
window.history.pushState({type: 'page', pageNumber: targetPageNumber, title: 'MicroBlog', url: targetUrl}, 'MicroBlog', targetUrl);
}
this.pjax = false;
so,在此,我们实现了所有的Ajax免刷新操作,非常感谢各种文章提供的各种帮助wwww
感谢以下文章:
HTML5之pushstate、popstate操作history,无刷新改变当前url
使用ajax和history.pushState无刷新改变页面URL
jQuery Address 全站 AJAX (Deep Linking) 完整案例详解
HTML5 简介(三):利用 History API 无刷新更改地址栏
jquery ajax发送Post请求,如何添加请求头
植入部分
如果您觉得文章不错,可以通过赞助支持我。
如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。