无刷新加载下一页方案解析
编辑前段时间移植了一个Ghost的主题的时候(就是我现在用的这个),发现这个下拉加载下一页特别有意思,只用了短短几行代码,且后端没有重写请求方法,就轻而易举的实现了,于是乎就简单分析了一下。以供有需要的人参考一下。
之前是这样做的
我之前做这种无刷新加载下一页都是用的字符串拼接,把html代码和新的数据拼接起来然后append进去。这种方法虽然可以实现功能,但是有很多缺点,比如如果修改了页面的样式,那么很可能就要重新写拼接的字符串,而且整个代码看起来非常不好看(我有代码洁癖)。就像下面代码一样,宛如一坨shit…
$.each(data,function (n,value) {
$('.post-list').append('' +
'<div class="col-lg-4 col-md-6 col-sm-6 col-xs-12 post-list-item" data-aos="fade-up">\n' +
'<div class="post-item-main">\n' +
'<a href="/archives/'+value.postUrl+'">\n' +
'<div class="post-item-thumbnail lazy" style="background-image: url(/halo/source/img/pic12.jpg)"></div>\n' +
'</a>\n' +
'<div class="post-item-info">\n' +
'<div class="post-info-title">\n' +
'<a href="/archives/'+value.postUrl+'"><span>'+value.postTitle+'</span></a><br>\n' +
'</div>\n' +
'<div>\n' +
'<span class="post-info-desc">\n' +
''+value.postSummary+'...' +
'</span>\n' +
'</div>\n' +
'<div class="post-info-other" style="text-align: right">\n' +
'<a href="#">MORE></a>\n' +
'</div>\n' +
'</div>\n' +
'</div>\n' +
'</div>'
);
});
新学到的方法
废话少说,先上代码,地址https://github.com/TryGhost/Casper/blob/master/assets/js/infinitescroll.js。
/* global maxPages */
var maxPages = parseInt(totalPages);
// Code snippet inspired by https://github.com/douglasrodrigues5/ghost-blog-infinite-scroll
$(function ($) {
var currentPage = 1;
var pathname = window.location.pathname;
var $document = $(document);
var $result = $('.post-feed');
var buffer = 300;
var ticking = false;
var isLoading = false;
var lastScrollY = window.scrollY;
var lastWindowHeight = window.innerHeight;
var lastDocumentHeight = $document.height();
function onScroll() {
lastScrollY = window.scrollY;
requestTick();
}
function onResize() {
lastWindowHeight = window.innerHeight;
lastDocumentHeight = $document.height();
requestTick();
}
function requestTick() {
if (!ticking) {
requestAnimationFrame(infiniteScroll);
}
ticking = true;
}
function sanitizePathname(path) {
var paginationRegex = /(?:page\/)(\d)(?:\/)$/i;
// remove hash params from path
path = path.replace(/#(.*)$/g, '').replace('////g', '/');
// remove pagination from the path and replace the current pages
// with the actual requested page. E. g. `/page/3/` indicates that
// the user actually requested page 3, so we should request page 4
// next, unless it's the last page already.
if (path.match(paginationRegex)) {
currentPage = parseInt(path.match(paginationRegex)[1]);
path = path.replace(paginationRegex, '');
}
return path;
}
function infiniteScroll() {
// sanitize the pathname from possible pagination or hash params
pathname = sanitizePathname(pathname);
// return if already loading
if (isLoading) {
return;
}
// return if not scroll to the bottom
if (lastScrollY + lastWindowHeight <= lastDocumentHeight - buffer) {
ticking = false;
return;
}
/**
* maxPages is defined in default.hbs and is the value
* of the amount of pagination pages.
* If we reached the last page or are past it,
* we return and disable the listeners.
*/
if (currentPage >= maxPages) {
window.removeEventListener('scroll', onScroll, {passive: true});
window.removeEventListener('resize', onResize);
return;
}
isLoading = true;
// next page
currentPage += 1;
// Load more
var nextPage = pathname + 'page/' + currentPage + '/';
$.get(nextPage, function (content) {
var parse = document.createRange().createContextualFragment(content);
var posts = parse.querySelectorAll('.post');
if (posts.length) {
[].forEach.call(posts, function (post) {
$result[0].appendChild(post);
});
}
}).fail(function (xhr) {
// 404 indicates we've run out of pages
if (xhr.status === 404) {
window.removeEventListener('scroll', onScroll, {passive: true});
window.removeEventListener('resize', onResize);
}
}).always(function () {
lastDocumentHeight = $document.height();
isLoading = false;
ticking = false;
});
}
window.addEventListener('scroll', onScroll, {passive: true});
window.addEventListener('resize', onResize);
infiniteScroll();
});
我们知道的是,很多CMS系统,博客系统(Wordpress,typecho,Hexo等)的下一页路径几乎都是/page/页码
,所以上面的方法几乎可以通用,就算不是,改改也是可以的。好了,下面详细来解析一下上面的代码吧。
获取总页数
在最上面有一行代码var maxPages = parseInt(totalPages);
是用于定义总页数的,不知道?那么想办法后端返回一个吧,这个有什么用呢?看下面:
if (currentPage >= maxPages) {
window.removeEventListener('scroll', onScroll, {passive: true});
window.removeEventListener('resize', onResize);
return;
}
就是说当当前页面大于等于总页数的的时候就停止执行下面的代码,而下面的代码则是加载下一页的,所以说需要有这个判断,当获取到最后一页的时候就停止获取。
加载下一页
可以看到,方法的最上面定义了一个变量var currentPage = 1;
,这个变量代表着第一页的页码,当页面滑动到最底部的时候就会调用infiniteScroll
方法,并且给currentPage
加上一,然后就请求下一页的数据。此时的请求路径也就是/page/2
。
解析请求的路径
我们看到这一段:
$.get(nextPage, function (content) {
var parse = document.createRange().createContextualFragment(content);
var posts = parse.querySelectorAll('.post');
if (posts.length) {
[].forEach.call(posts, function (post) {
$result[0].appendChild(post);
});
}
})
使用get方式请求下一页的数据,需要注意的是,虽然是使用ajax请求的,但是是请求的下一页整个页面的内容,所以请求获得的数据便是整个页面的html代码。所以这里使用到了querySelectorAll
这个方法,截取全部以post
类的html代码,并存入posts
变量中,然后在遍历posts
,得到单个文章列表项,最后再追加到页面中,整个过程一气呵成。
总结
整个获取下一页数据的基本流程为:
判断页面是否滑动到底部,如已滑动到底部,则调用加载页面的方法,并把
currentPage
在原基础上加1,并需要判断当前页码是否大于总页数,如大于,则不调用加载页面的方法。使用ajax请求下一页的数据。
解析请求下一页所获得的数据,并截取所有以
post
类的html代码。注意:并非所有单个文章列表项的class都为post,请按实际情况来。追加到当前页面的文章列表中。
- 5
- 8
-
分享