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 = `
${loadingText}
`; // 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 = `
${noMoreText}
`; 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 = `
${noMoreText}
`; 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 = `
${noMoreText}
`; if (window.infiniteScrollObserver) window.infiniteScrollObserver.disconnect(); } } catch (error) { console.error('Infinite scroll error:', error); sentinel.innerHTML = `
加载失败,点击重试
`; } finally { isLoading = false; } }; window.infiniteScrollObserver = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { loadNextPage(); } }, { rootMargin: '200px' }); window.infiniteScrollObserver.observe(sentinel); };