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请求,如何添加请求头

植入部分

如果您觉得文章不错,可以通过赞助支持我。

如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。

标签: 知识, 代码段, 语法, jQuery

添加新评论