Files

137 lines
5.5 KiB
JavaScript

window.initInfiniteScroll = function() {
// Configuration
const containerSelector = '.posts';
const itemSelector = '.post';
const paginationSelector = '.pagination';
const nextLinkSelector = '.pagination .page-next .next';
const loadingText = '加载中...';
const noMoreText = '没有更多了';
const container = document.querySelector(containerSelector);
if (!container) return; // Not on a page with posts
// Check if we already have an observer (to prevent duplicates on PJAX re-init)
if (window.infiniteScrollObserver) {
window.infiniteScrollObserver.disconnect();
window.infiniteScrollObserver = null;
}
// Remove existing sentinel if any
const existingSentinel = document.getElementById('infinite-scroll-sentinel');
if (existingSentinel) existingSentinel.remove();
// Find next link
let nextLink = document.querySelector(nextLinkSelector);
if (!nextLink) return; // No next page
// Hide default pagination
const pagination = document.querySelector(paginationSelector);
if (pagination) pagination.style.display = 'none';
// Create sentinel
const sentinel = document.createElement('div');
sentinel.id = 'infinite-scroll-sentinel';
sentinel.innerHTML = `<div class="infinite-scroll-loading" style="text-align:center; padding: 20px; color: #999;">${loadingText}</div>`;
// Insert after container
container.parentNode.insertBefore(sentinel, container.nextSibling);
let isLoading = false;
const loadNextPage = async () => {
if (isLoading) return;
// Re-check next link from the DOM (it should be updated after previous fetch)
nextLink = document.querySelector(nextLinkSelector);
if (!nextLink) {
sentinel.innerHTML = `<div class="infinite-scroll-end" style="text-align:center; padding: 20px; color: #999;">${noMoreText}</div>`;
if (window.infiniteScrollObserver) window.infiniteScrollObserver.disconnect();
return;
}
isLoading = true;
const url = nextLink.href;
try {
const response = await fetch(url);
if (!response.ok) throw new Error('Network response was not ok');
const text = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
// 1. Get new posts
const newPosts = doc.querySelectorAll(`${containerSelector} ${itemSelector}`);
// 2. Append posts with batch layout read/write to avoid thrashing
if (newPosts.length > 0) {
// Phase 1: append all and set initial opacity
newPosts.forEach(function(post) {
post.removeAttribute('uk-scrollspy-class');
post.style.opacity = 0;
container.appendChild(post);
});
// Phase 2: force single layout read for all new posts
var postArr = Array.prototype.slice.call(newPosts);
postArr.forEach(function(post) { void post.offsetWidth; });
// Phase 3: batch animate in
postArr.forEach(function(post) {
post.style.transition = 'opacity 0.5s ease';
post.style.opacity = 1;
});
}
// 3. Update pagination (crucial for next link)
const newPagination = doc.querySelector(paginationSelector);
const oldPagination = document.querySelector(paginationSelector);
if (oldPagination && newPagination) {
oldPagination.innerHTML = newPagination.innerHTML;
oldPagination.style.display = 'none'; // Keep hidden
} else if (oldPagination && !newPagination) {
oldPagination.remove();
sentinel.innerHTML = `<div class="infinite-scroll-end" style="text-align:center; padding: 20px; color: #999;">${noMoreText}</div>`;
if (window.infiniteScrollObserver) window.infiniteScrollObserver.disconnect();
isLoading = false;
return;
}
// 4. Re-init plugins for new content
if (typeof lazyLoadInit === 'function') {
lazyLoadInit({
offsetBottom: 800,
offsetTopm: 800,
showTime: 1100
});
}
if (window.ViewImage) {
ViewImage.init('.post-content img');
}
// 5. Check if there is a next page in the NEW content (double check)
const newNextLink = document.querySelector(nextLinkSelector);
if (!newNextLink) {
sentinel.innerHTML = `<div class="infinite-scroll-end" style="text-align:center; padding: 20px; color: #999;">${noMoreText}</div>`;
if (window.infiniteScrollObserver) window.infiniteScrollObserver.disconnect();
}
} catch (error) {
console.error('Infinite scroll error:', error);
sentinel.innerHTML = `<div class="infinite-scroll-error" style="text-align:center; padding: 20px; color: red; cursor: pointer;" onclick="initInfiniteScroll()">加载失败,点击重试</div>`;
} finally {
isLoading = false;
}
};
window.infiniteScrollObserver = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadNextPage();
}
}, {
rootMargin: '200px'
});
window.infiniteScrollObserver.observe(sentinel);
};