前言

折腾typecho也有好一段时间了,没什么好写的就干脆水篇教程(x
虽然给typecho用上pjax技术的文章也有很多大佬也写过了,不过我还是想写下自己使用的jquery-pjax的一些内容和经验

项目地址jquery-pjax

正文

引入资源

先在页面中引入jQuery和jquery-pjax

<script src="path/to/jquery.js"></script>
<script src="path/to/jquery.pjax.js"></script>

注意引入的jQuery的版本,推荐用2.0.1或更高,我使用的jQuery为3.3.1版本,jquery-pjax为2.0.1版本
你的所有pjax相关代码都应在引入jquery-pjax.jsscript标签之后

基本使用

$(document).pjax(selector, [container], options)
  1. selector:需要绑定的点击事件的元素的选择器
  2. container:唯一pjax容器的选择器。
  3. options:
选项默认值说明
timeout650ajax超时时间(毫秒),超时后强制刷新整个页面
pushtrue使用pushState在浏览器中添加历史记录
replacefalse替换URL地址但不添加浏览器历史记录
maxCacheLength20容器元素缓存内容的最大值(次)
version stringfunction,返回当前pjax版本
scrollTo0浏览器滚动条的垂直滚动位置。设为false时禁止滚动
type"GET"参考 $.ajax
dataType"html"参考 $.ajax
container 被替换内容元素的CSS选择器
urllink.hrefstringfunction,返回ajax请求响应的URL
targetlinkpjax 事件 中relatedTarget属性的最终值
fragment css选择器,提取ajax响应内容中指定的内容片段

我的代码:

$(document).pjax(
    'a[href^="' + THEME_CONFIG.SITE_URL + '"]:not(a[target="_blank"], [no-pjax],a[rel~="nofollow"])', 
    {
        container: '#pjax-container',
        fragment: '#pjax-container',
        timeout: 8000
    })

其中THEME_CONFIG.SITE_URL是站点的URL地址,选择器的详细用法可以参考jQuery 参考手册 - 选择器

sendcomplete事件

pjax提供了两个事件可以使得我们可以在刷新局部内容的时候显示一些过渡动画,比如我现在用的NProgress

事件参数说明
pjax:sendxhr, optionspjax通过链接点击已经开始之后触发
pjax:completexhr, textStatus, options无论结果如何,都在ajax响应完成后触发

示例代码:

$(document).on('pjax:send', function () {
            NProgress.start();
        }).on('pjax:complete', function () {
            NProgress.done();
        });

你当然可以使用别的过渡动画

一些JS的重载

pjax在每次请求数据的时候只会获取你所设置的容器内的数据,所以必须在pjax结束之后回调函数里重新加载一些特定的js函数。
我的代码:(已简化一些判断和稍作修改使得容易阅读)

//#header和#body的动画
$('#header').removeClass('slideOutUp').addClass('slideInDown');
$('#body').removeClass('fadeOut').addClass('fadeIn');

fancybox();             //图片灯箱效果
hitokoto();             //一言的重新获取
lazyload();             //图片懒加载
hljs.init();            //highlight.js代码高亮
commentPlus.init();     //评论的一些增强功能,比如表情
postOther.init();       //文章内一些增强功能
closeNav();             //关闭导航栏
/** 重载部分统计代码 来自QQ爹博客*/
/** baidu */
if (typeof _hmt !== 'undefined') {
    _hmt.push(['_trackPageview', location.pathname + location.search]);
}
/** google */
if (typeof ga !== 'undefined') {
    ga('send', 'pageview', location.pathname + location.search);
}

重载代码是根据自己的实际情况进行编写的,不同的情况的代码一般不一样,上面只是给出了一个例子

完整代码

以下是我的博客使用的pjax的完整代码,为方便阅读和参考已做部分修改

var doPjaxStartAction = function () {
    $("#header").toggleClass('slideOutUp');
    $("#body").toggleClass('fadeOut');
    $("#wrapper").hide();
}
var doPjaxCompleteAction = function () {
    ('#header').removeClass('slideOutUp').addClass('slideInDown');
    $('#body').removeClass('fadeOut').addClass('fadeIn');

    fancybox();             //图片灯箱效果
    hitokoto();             //一言的重新获取
    lazyload();             //图片懒加载
    hljs.init();            //highlight.js代码高亮
    commentPlus.init();     //评论的一些增强功能,比如表情
    postOther.init();       //文章内一些增强功能
    closeNav();             //关闭导航栏
    /** 重载部分统计代码 来自QQ爹博客*/
    /** baidu */
    if (typeof _hmt !== 'undefined') {
        _hmt.push(['_trackPageview', location.pathname + location.search]);
    }
    /** google */
    if (typeof ga !== 'undefined') {
        ga('send', 'pageview', location.pathname + location.search);
    }
}
$(document).pjax('a[href^="' + THEME_CONFIG.SITE_URL + '"]:not(a[target="_blank"], [no-pjax],a[rel~="nofollow"])',
        {
            container: '#pjax-container',
            fragment: '#pjax-container',
            timeout: 8000
        }).on('pjax:send', function () {
            NProgress.start();
            doPjaxStartAction();
        }).on('pjax:complete', function () {
            NProgress.done();
            doPjaxCompleteAction();
        })

解决评论无法回复

/var/Widget/Archive.php文件1720行至1792行左右可以看到如下评论回复的相关的js代码

$header .= "<script type=\"text/javascript\">
(function () {
    window.TypechoComment = {
        dom : function (id) {
            return document.getElementById(id);
        },
    
        create : function (tag, attr) {
            var el = document.createElement(tag);
        
            for (var key in attr) {
                el.setAttribute(key, attr[key]);
            }
        
            return el;
        },
        reply : function (cid, coid) {
            var comment = this.dom(cid), parent = comment.parentNode,
                response = this.dom('" . $this->respondId . "'), input = this.dom('comment-parent'),
                form = 'form' == response.tagName ? response : response.getElementsByTagName('form')[0],
                textarea = response.getElementsByTagName('textarea')[0];
            if (null == input) {
                input = this.create('input', {
                    'type' : 'hidden',
                    'name' : 'parent',
                    'id'   : 'comment-parent'
                });
                form.appendChild(input);
            }
            input.setAttribute('value', coid);
            if (null == this.dom('comment-form-place-holder')) {
                var holder = this.create('div', {
                    'id' : 'comment-form-place-holder'
                });
                response.parentNode.insertBefore(holder, response);
            }
            comment.appendChild(response);
            this.dom('cancel-comment-reply-link').style.display = '';
            if (null != textarea && 'text' == textarea.name) {
                textarea.focus();
            }
            return false;
        },
        cancelReply : function () {
            var response = this.dom('{$this->respondId}'),
            holder = this.dom('comment-form-place-holder'), input = this.dom('comment-parent');
            if (null != input) {
                input.parentNode.removeChild(input);
            }
            if (null == holder) {
                return true;
            }
            this.dom('cancel-comment-reply-link').style.display = 'none';
            holder.parentNode.insertBefore(response, holder);
            return false;
        }
    };
})();
</script>
";

注意在1739行和1774行:

$this->respondId  

不能回复是因为respondId输出不对,因为在pjax获取另一个页面的时候这个ID没有被修改过来。
所以比较简单的方法是将上面那段代码插入到comments.php文件中(同时也在pjax的容器内),这样每次pjax进入不同的文章时,这个ID就会正确输出。

其他

开启pjax需要将设置->评论->开启反垃圾保护的勾选取消。这里推荐一个反垃圾评论的插件SmartSpam

后记

除了jquery-pjax,这里再推荐一个pjax的项目MoOx/pjax
与前者相比,后者的部分优点在于

  • 不仅限于一个容器
  • 不依赖于jQuery且体积特别小

这个pjax项目也是我之前用的,后来还是换成了现在的这个。
当然比较有名的还有Instantclick