🎨 Optimised infinite scroll (#503)

no issue
- removed jQuery usage
- use the `<link rel="next">` tag provided by Ghost to determine the next page to fetch
This commit is contained in:
汪磊
2018-12-05 20:38:11 +08:00
committed by Kevin Ansfield
parent f695d69aeb
commit 3c2347c7f9
4 changed files with 74 additions and 103 deletions

View File

@ -1,19 +1,78 @@
/* global maxPages */
/**
* Infinite Scroll
*/
(function(window, document) {
// next link element
var nextElement = document.querySelector('link[rel=next]');
if (!nextElement) return;
// post feed element
var feedElement = document.querySelector('.post-feed');
if (!feedElement) return;
// 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 loading = false;
var lastScrollY = window.scrollY;
var lastWindowHeight = window.innerHeight;
var lastDocumentHeight = $document.height();
var lastDocumentHeight = document.documentElement.scrollHeight;
function onPageLoad() {
if (this.status === 404) {
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onResize);
return;
}
// append contents
var postElements = this.response.querySelectorAll('.post-card');
postElements.forEach(function (item) {
feedElement.appendChild(item);
});
// set next link
var resNextElement = this.response.querySelector('link[rel=next]');
if (resNextElement) {
nextElement.href = resNextElement.href;
} else {
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onResize);
}
// sync status
lastDocumentHeight = document.documentElement.scrollHeight;
ticking = false;
loading = false;
}
function onUpdate() {
// return if already loading
if (loading) return;
// return if not scroll to the bottom
if (lastScrollY + lastWindowHeight <= lastDocumentHeight - buffer) {
ticking = false;
return;
}
loading = true;
var xhr = new window.XMLHttpRequest();
xhr.responseType = 'document';
xhr.addEventListener('load', onPageLoad);
xhr.open('GET', nextElement.href);
xhr.send(null);
}
function requestTick() {
ticking || window.requestAnimationFrame(onUpdate);
ticking = true;
}
function onScroll() {
lastScrollY = window.scrollY;
@ -22,94 +81,12 @@ $(function ($) {
function onResize() {
lastWindowHeight = window.innerHeight;
lastDocumentHeight = $document.height();
lastDocumentHeight = document.documentElement.scrollHeight;
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('scroll', onScroll, { passive: true });
window.addEventListener('resize', onResize);
infiniteScroll();
});
requestTick();
})(window, document);