CodeSky 代码之空

随手记录自己的学习过程

jQuery+PHP 大话ajax免刷新提交表单与切换页面

2015-03-01 23:24分类: PHP评论: 0

昨天折腾了一个晚上,今天在抽空折腾的东西,不记下来未免太浪费了,于是我决定花晚上把它写下来。

Ajax是目前最流行的优化用户体验的方法,但在过去,他往往代表着牺牲SEO以及抛弃浏览器后退前进功能,但是现在,已经不是这样了。

现在的Ajax完全可以做到兼备,所以你可以看到,基本上,在你提交表单之后再也不用跳转之后才告诉你你的输入是否正确,甚至在搜索时,一边输入字符,一边进行检索。

这些都是Ajax所带来的便利,但是鉴于我还没有前后端整站写过(只有使用前后框架[PHP MVC]以及前端的经历),所以对整个流程还不能说熟悉。这次,我们从后端写到前端,一点点来解刨Ajax的套路。

首先,我们前端的核心当然是$.ajax啦,没有他我们怎么实现ajax呢?在之前,我已经写过ajax的表单提交了,所以表单其实是很好上手的,相比之下,页面切换的处理要麻烦很多。

关于jQuery中的$.ajax,这里有w3school官方文档,w3school的参数不是很全,但是是中文的。中文文档不知道全不全:http://jquery.bootcss.com/jQuery.ajax/

好了,不多做介绍了,之后用到什么讲什么吧。

首先,我们用html建立一个传统意义上的表单,其中method="post"

1<form class="login" method="post">
2	<input type="password" placeholder="密码" name="password">
3	<input type="submit" value="提交">
4</form>
5

如果是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

1<?php
2	session_start();
3
4	// array_key_exists('password', $_POST)
5	if (isset($_POST['password'])) {
6		$pass = md5($_POST['password']);
7
8		if ($pass == md5('Kurama')) {
9			$_SESSION['login'] = true;
10			echo json_encode('200');
11		} else {
12			echo json_encode('403');
13		}
14	} else {
15		echo json_encode('404');
16	}
17?>
18

是怎么回事呢?我们照理写我们的PHP部分,其实是不干扰的,只要传出json_encode()转换为json对象就好了。

剩下的就跟PHP一样处理,包括写入session等部分。

如果要检测数组是否有某个值,也可以使用:array_key_exists('password', $_POST),其实我是嫌这货太长- -#

好了,表单讲完了,烦人的是,我们要开始试着做局部或者全局Ajax了,这个好麻烦呀,之前没有试过呀。

于是就有了下面的一大堆资料来学习,大致上是这样的。

首先我们来说说全局的ajax。

这一点其实并不难,我们需要在ajax里type使用html来获取,并且加入就可以了,大致Like that.

1var pajax = function(targetUrl, targetTitle) {
2	$.ajax({
3		dataType: 'html',
4		type: 'GET',
5		url: targetUrl,
6		headers: {
7			Pjax: true
8		},
9		success: function(data) {
10			$('body').contents().remove();
11			$('body').append(data);
12			console.log(data);
13			$('title').html(targetTitle);
14			window.history.pushState({type:'url', url: targetUrl, title: targetTitle}, 'Hello', targetUrl);
15		}
16	});		
17};
18

关于window.history的部分,之后在做解释。 我们可以看到,这样就把body里的部分移除了,然后在append过去,基本上是没什么问题的。

当然,这里我们需要PHP的配合,否则的话就会出现把全部页面重复加载到body里的问题。 在这里,我们在ajax时设定了一个header,之后就会用上了。

在PHP部分,通常我们会选择将网页分成三个部分,header.php, xxx.php, footer.php,这里,我们给header和footer加个判断(xxx.php存放的是差异部分,也是需要remove的部分)。

1<?php
2	if (!isset($_SERVER['HTTP_PJAX']))
3		include('common/header.php');
4?>
5

在这里,我们使用$_SERVER['HTTP_PJAX']的存在与否来检查是否要载入header和footer,于是,就能分成:可以正常直接访问页面,以及可以正常ajax加载了。

接下来,我们前面说到,关于SEO的问题,也是为了刷新访问正常,我们需要在Ajax之后改变地址栏,这也就是后面一大堆参考的攻略点了。

我们需要用到的也就是操作历史记录(pushstate&popstate),pushState可以做到免刷新修改url,这点就是和window.location的不同之处了。

1window.history.pushState({type:'url', url: targetUrl, title: targetTitle}, targetTitle, targetUrl);
2

这句话起到的作用也就在于此了。这里需要注意的是,传入的值都是相对于目标页面(也就是Ajax载入的页面)而言的,而不是原来的页面。所以我们这里用target来表示。

做到这里,还不够,因为我们还需要知道用户什么时候点击了后退前进按钮,这里需要使用到popstate监听:

1window.addEventListener('popstate', function(e){  
2  	var url;
3  	var title;
4	if (window.history.state){  
5		var state = e.state;
6		url = state.url;
7		title = state.title;
8		type = state.type;
9	}
10	if (type != 'page') {
11		$.ajax({
12			dataType: 'html',
13			type: 'GET',
14			url: url,
15			headers: {
16				Pjax: true
17			},
18			success: function(data) {
19				$('body').contents().remove();
20				$('body').append(data);
21				console.log(data);
22				$('title').html(title);
23			}
24		});
25	} else {
26		page.pjax = true;
27		page.switchPage(state.pageNumber);
28	}
29});  
30

主要还是看这段:

1	if (window.history.state){  
2		var state = e.state;
3		url = state.url;
4		title = state.title;
5		type = state.type;
6	}
7

这里储存了我们的保留数据:pushState的第一个参数。然后根据参数来判断就好了。

当然,这里有个坑:最开始直接访问的地址是没有window.history.state的,我本来想着保留第一次访问的数据,通过监听内判断有没有window.history.state来决定。

结果发现如果在别的地方我还要使用局部ajax的话,这种写法太渣了,根本不能用,于是我们这么做:

1		if (!history.state) {
2			window.history.replaceState({type: 'url', pageNumber: page.currentPage, title: 'microblog'}, 'Hello', 'microblog.php');
3		}
4

使用replaceState,我们覆盖历史记录,参数同pushState部分。计划通。

最后,我们来试试局部Ajax,其实理论上而言这会比全局简单,因为只要删除部分DOM,然后加上嘛~实际上还是在地址栏以及前进后退的问题。 如果我们不进行一些判断操作,那么很快就会陷入 读取数据 - back修改链接并写入相关信息 然后重复这个操作,于是如同上面的源码,我们通过设置一个pjax等不等于true来解决这个问题。同时,针对是局部还是全部加载,我们新设置了一个type属性。

对应判断:

1Page.prototype.switchPage = function(targetPageNumber) {
2	this.getPageNumber(function() {
3		message.getMessage(targetPageNumber);
4		this.changePages(targetPageNumber);
5		var targetUrl = 'microblog.php#' + targetPageNumber;
6		console.log(this.pjax);
7		if (!this.pjax) {
8			window.history.pushState({type: 'page', pageNumber: targetPageNumber, title: 'MicroBlog', url: targetUrl}, 'MicroBlog', targetUrl);
9		}
10		this.pjax = false;
11	});
12}
13

计划通在这里:

1		if (!this.pjax) {
2			window.history.pushState({type: 'page', pageNumber: targetPageNumber, title: 'MicroBlog', url: targetUrl}, 'MicroBlog', targetUrl);
3		}
4		this.pjax = false;
5

so,在此,我们实现了所有的Ajax免刷新操作,非常感谢各种文章提供的各种帮助wwww

感谢以下文章: HTML5之pushstate、popstate操作history,无刷新改变当前url 使用ajax和history.pushState无刷新改变页面URL jQuery Address 全站 AJAX (Deep Linking) 完整案例详解 HTML5 简介(三):利用 History API 无刷新更改地址栏 jquery ajax发送Post请求,如何添加请求头

评论 (0)