1+ <div class="onload-container" data-pagination-scope style="text-align: center; margin: 20px 0;">
2+ <?php
3+ // Prepare variables
4+ $ page = $ this ->pagination ->page ;
5+ $ totalPages = $ this ->pagination ->pageCount ;
6+ $ isLast = $ page >= $ totalPages ;
7+ $ nextPage = min ($ totalPages , $ page + 1 );
8+ $ nextUrl = $ this ->pagination ->createUrl ($ nextPage );
9+ ?>
10+
11+ <div class="onload-status" aria-live="polite" aria-atomic="true" style="margin:10px 0;">
12+ <?php if (!$ isLast ): ?>
13+ <span class="onload-text">Loading on scroll...</span>
14+ <?php else : ?>
15+ <span class="onload-text">No more content to load.</span>
16+ <?php endif ; ?>
17+ </div>
18+
19+ <?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>
21+ <?php endif ; ?>
22+ </div>
23+ <script>
24+ // Infinite scroll (onloading view)
25+ (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+
34+ function setupInfinite(scope){
35+ var link = scope.querySelector('.onload-trigger');
36+ if(!link) return;
37+
38+ var sentinel = document.createElement('div');
39+ sentinel.setAttribute('data-onload-sentinel', '');
40+ sentinel.style.cssText = 'height: 1px;';
41+
42+ var targetSelector = link.getAttribute('data-target') || '[data-pagination-content]';
43+ var container = document.querySelector(targetSelector);
44+ if(!container){ return; }
45+
46+ // place sentinel after container to detect end of list
47+ container.parentNode.insertBefore(sentinel, container.nextSibling);
48+
49+ var loading = false;
50+ var observer = new IntersectionObserver(function(entries){
51+ entries.forEach(function(entry){
52+ if(entry.isIntersecting && !loading){
53+ loading = true;
54+ link.click(); // reuse existing click/AJAX logic
55+ setTimeout(function(){ loading = false; }, 50);
56+ }
57+ });
58+ }, { rootMargin: '0px 0px 200px 0px' });
59+
60+ observer.observe(sentinel);
61+
62+ // Cleanup if controls are replaced
63+ scope.addEventListener('DOMNodeRemoved', function(ev){
64+ if(ev.target === scope){ try{ observer.disconnect(); }catch(_e){} }
65+ });
66+ }
67+
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 });
93+ })();
94+ </script>
0 commit comments