11<div class="onload-container" data-pagination-scope style="text-align: center; margin: 20px 0;">
22 <?php
3- // Prepare variables
43 $ page = $ this ->pagination ->page ;
54 $ totalPages = $ this ->pagination ->pageCount ;
65 $ isLast = $ page >= $ totalPages ;
1716 </div>
1817
1918 <?php if (!$ isLast ): ?>
20- <a <?= $ linkAttributes?> href="<?= $ nextUrl?> " class="onload-trigger" data-page="<?= $ nextPage?> " data-mode="append" data-target="[data-pagination-append]" data-history="push" style="display:none;">Next</a>
19+ <a <?= $ linkAttributes?>
20+ href="<?= $ nextUrl?> "
21+ class="onload-trigger"
22+ data-page="<?= $ nextPage?> "
23+ data-mode="append"
24+ data-target="[data-pagination-append]"
25+ data-history="none"
26+ data-pagination="ajax"
27+ style="display:none;">Next</a>
2128 <?php endif ; ?>
2229</div>
30+
2331<script>
24- // Infinite scroll (onloading view)
2532(function(){
26- if(window.__TAME_PAGINATION_ONLOADING_INITED__) return; // Guard against multiple inits
27- window.__TAME_PAGINATION_ONLOADING_INITED__ = true;
28-
29- function closestAnchor(el){
30- while(el && el !== document){ if(el.tagName === 'A') return el; el = el.parentNode; }
31- return null;
32- }
33+ if(window.__TAME_PAGINATION_SCROLL_INITED__) return;
34+ window.__TAME_PAGINATION_SCROLL_INITED__ = true;
3335
3436 function setupInfinite(scope){
35- var link = scope.querySelector('.onload-trigger');
37+ let link = scope.querySelector('.onload-trigger');
3638 if(!link) return;
3739
38- var sentinel = document.createElement('div') ;
39- sentinel.setAttribute('data-onload-sentinel', '' );
40- sentinel.style.cssText = 'height: 1px;' ;
40+ const targetSelector = link.getAttribute('data-target') || '[data-pagination-content]' ;
41+ const container = document.querySelector(targetSelector );
42+ if(!container) return ;
4143
42- var targetSelector = link.getAttribute('data-target') || '[data-pagination-content]';
43- var container = document.querySelector(targetSelector);
44- if(!container){ return; }
44+ // sentinel inside scope, not after container
45+ let sentinel = document.createElement('div');
46+ sentinel.setAttribute('data-onload-sentinel','');
47+ sentinel.style.cssText = 'height:1px;';
48+ scope.appendChild(sentinel);
4549
46- // place sentinel after container to detect end of list
47- container.parentNode.insertBefore(sentinel, container.nextSibling );
50+ let loading = false;
51+ let loadedPages = new Set( );
4852
49- var loading = false;
50- var observer = new IntersectionObserver(function(entries){
53+ let observer = new IntersectionObserver(function(entries){
5154 entries.forEach(function(entry){
5255 if(entry.isIntersecting && !loading){
56+ let page = link.getAttribute('data-page');
57+ if(loadedPages.has(page)) return;
58+
5359 loading = true;
54- link.click(); // reuse existing click/AJAX logic
55- setTimeout(function(){ loading = false; }, 50);
60+ loadedPages.add(page);
61+
62+ loadPage(link, container, scope).then(function(result){
63+ scope = result.newScope;
64+ link = scope.querySelector('.onload-trigger');
65+ let stillHasNext = result.stillHasNext;
66+
67+ if(stillHasNext){
68+ sentinel.remove();
69+ sentinel = document.createElement('div');
70+ sentinel.setAttribute('data-onload-sentinel','');
71+ sentinel.style.cssText = 'height:1px;';
72+ scope.appendChild(sentinel);
73+ observer.observe(sentinel);
74+ loading = false;
75+ } else {
76+ observer.disconnect();
77+ sentinel.remove();
78+ }
79+ }).catch(function(){
80+ window.location.href = link.getAttribute('href');
81+ });
5682 }
5783 });
58- }, { rootMargin: '0px 0px 200px 0px' });
84+ }, { threshold: 1.0 });
5985
6086 observer.observe(sentinel);
87+ }
6188
62- // Cleanup if controls are replaced
63- scope.addEventListener('DOMNodeRemoved', function(ev){
64- if(ev.target === scope){ try{ observer.disconnect(); }catch(_e){} }
89+ function loadPage(a, container, scope){
90+ return new Promise(function(resolve, reject){
91+ let href = a.getAttribute('href');
92+ let mode = a.getAttribute('data-mode') || 'replace';
93+
94+ fetch(href, { headers: { 'X-Requested-With': 'XMLHttpRequest' } })
95+ .then(res => res.text())
96+ .then(html => {
97+ let parser = new DOMParser();
98+ let doc = parser.parseFromString(html, 'text/html');
99+
100+ let newContainer = doc.querySelector(a.getAttribute('data-target'));
101+ let newScope = doc.querySelector('[data-pagination-scope]');
102+ if(!newContainer || !newScope) return reject();
103+
104+ if(mode === 'append'){
105+ while(newContainer.firstChild){
106+ container.appendChild(newContainer.firstChild);
107+ }
108+ } else {
109+ container.innerHTML = newContainer.innerHTML;
110+ }
111+
112+ scope.replaceWith(newScope);
113+
114+ // update "showing"
115+ let newShowing = doc.querySelector('[data-pagination-showing]');
116+ let curShowing = document.querySelector('[data-pagination-showing]');
117+ if(newShowing && curShowing){
118+ curShowing.innerHTML = newShowing.innerHTML;
119+ }
120+
121+ let nextLink = newScope.querySelector('.onload-trigger');
122+ resolve({ newScope: newScope, stillHasNext: !!nextLink });
123+ })
124+ .catch(reject);
65125 });
66126 }
67127
68- // Delegate click to reuse loading view behavior with history
69- document.addEventListener('click', function(e){
70- var a = closestAnchor(e.target);
71- if(!a) return;
72- if(a.classList.contains('onload-trigger')){
73- // Ensure it is treated as AJAX pagination
74- a.setAttribute('data-pagination', 'ajax');
75- }
76- });
77-
78- // Initialize for current scope
79- document.querySelectorAll('[data-pagination-scope]').forEach(function(scope){
80- if(scope.closest('.onload-container')){ setupInfinite(scope); }
81- });
82-
83- // After AJAX replacement, re-init when new scope appears
84- var mo = new MutationObserver(function(){
85- document.querySelectorAll('[data-pagination-scope]').forEach(function(scope){
86- if(scope.closest('.onload-container') && !scope.__onloadInited){
87- scope.__onloadInited = true;
88- setupInfinite(scope);
89- }
90- });
91- });
92- mo.observe(document.documentElement, { childList: true, subtree: true });
128+ document.querySelectorAll('[data-pagination-scope]').forEach(setupInfinite);
93129})();
94- </script>
130+ </script>
0 commit comments