132 lines
5.3 KiB
JavaScript
132 lines
5.3 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
|
||
|
|
if (newPosts.length > 0) {
|
||
|
|
newPosts.forEach(post => {
|
||
|
|
// Remove UIkit scrollspy attributes to prevent interference and use manual animation
|
||
|
|
post.removeAttribute('uk-scrollspy-class');
|
||
|
|
post.style.opacity = 0;
|
||
|
|
container.appendChild(post);
|
||
|
|
|
||
|
|
// Trigger reflow
|
||
|
|
void post.offsetWidth;
|
||
|
|
|
||
|
|
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);
|
||
|
|
};
|