Skip to content

Commit 3e9f687

Browse files
update
1 parent ca95dbd commit 3e9f687

File tree

2 files changed

+90
-54
lines changed

2 files changed

+90
-54
lines changed
Lines changed: 89 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
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;
@@ -17,78 +16,115 @@
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>

tests/testLoop.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
->where('amount', '>', 0)
1717
->join('user', 'user.user_id', '=', 'wallet.user_id')
1818
->latest('date')
19-
->paginate(6);
19+
->paginate(7);
2020
?>
2121

2222

0 commit comments

Comments
 (0)