Numbered Pagination
A numbered page strip that collapses long runs to an ellipsis.
6 फ्रेमवर्कमध्यम
An append-on-demand list that announces its new result count.
<!--
The status line is the whole point of this component. Appending rows to a list
is a silent DOM mutation: a sighted user sees the page grow, a screen-reader
user hears nothing at all and has no way to know the button worked. The
aria-live region turns "12 more rows appeared somewhere above you" into a
sentence.
It must be in the DOM on first paint and stay there - a live region injected
at the same moment its text changes is not announced by most screen readers,
which is the single most common way this pattern is got wrong.
-->
<div class="feed">
<ul class="feed__list" id="feed-list">
<li class="feed__item"><span class="feed__title">Quarterly revenue report</span><span class="feed__meta">Finance</span></li>
<li class="feed__item"><span class="feed__title">Onboarding checklist</span><span class="feed__meta">People</span></li>
<li class="feed__item"><span class="feed__title">Incident post-mortem</span><span class="feed__meta">Engineering</span></li>
<li class="feed__item"><span class="feed__title">Brand guidelines</span><span class="feed__meta">Design</span></li>
</ul>
<p class="feed__status" id="feed-status" role="status" aria-live="polite">Showing 4 of 12 results</p>
<nav aria-label="Pagination">
<button class="feed__more" id="feed-more" type="button">Load more</button>
</nav>
</div>
<script>
(function () {
var PAGE_SIZE = 4;
var TOTAL = 12;
var list = document.getElementById('feed-list');
var status = document.getElementById('feed-status');
var button = document.getElementById('feed-more');
var REMAINING = [
{ title: 'Security review', meta: 'Engineering' },
{ title: 'Pricing experiment', meta: 'Growth' },
{ title: 'Support playbook', meta: 'Success' },
{ title: 'Hiring plan', meta: 'People' },
{ title: 'Roadmap Q3', meta: 'Product' },
{ title: 'Vendor audit', meta: 'Finance' },
{ title: 'Accessibility audit', meta: 'Design' },
{ title: 'Data retention policy', meta: 'Legal' }
];
var shown = PAGE_SIZE;
button.addEventListener('click', function () {
// aria-busy + disabled while the request is in flight, so a second click
// cannot queue a duplicate page. Swap the timeout for your fetch.
button.disabled = true;
button.setAttribute('aria-busy', 'true');
button.textContent = 'Loading…';
window.setTimeout(function () {
var next = REMAINING.splice(0, PAGE_SIZE);
next.forEach(function (row) {
var li = document.createElement('li');
li.className = 'feed__item';
var title = document.createElement('span');
title.className = 'feed__title';
title.textContent = row.title;
var meta = document.createElement('span');
meta.className = 'feed__meta';
meta.textContent = row.meta;
li.appendChild(title);
li.appendChild(meta);
list.appendChild(li);
});
shown += next.length;
status.textContent = 'Showing ' + shown + ' of ' + TOTAL + ' results';
if (shown >= TOTAL) {
// Kept in the DOM rather than removed: yanking the element the user
// just activated drops their focus to <body> and loses their place.
button.textContent = 'All results loaded';
button.setAttribute('aria-disabled', 'true');
button.removeAttribute('aria-busy');
return;
}
button.disabled = false;
button.removeAttribute('aria-busy');
button.textContent = 'Load more';
}, 600);
});
})();
</script>| Prop | टाइप | डिफ़ॉल्ट | विवरण |
|---|---|---|---|
itemsआवश्यक | Result[] | - | रेंडर की जाने वाली आइटम की सूची। |
totalआवश्यक | number | - | सभी पृष्ठों में कुल कितने आइटम हैं। |
loading | boolean | false | true होने पर स्पिनर दिखाता है और इनपुट रोक देता है। |
onLoadMore | () => void | - | अगला बैच माँगे जाने पर कॉल होता है। |
ariaLabel | string | 'Pagination' | बटन का एक्सेसिबल नाम। सिर्फ़ आइकन वाले बटन के लिए ज़रूरी है। |
className | string | - | रूट एलिमेंट पर मर्ज होने वाली अतिरिक्त क्लासेज़। |
The status line is the component, not an accessory to it. Appending rows is a silent DOM mutation: a sighted user watches the list grow, a screen-reader user hears nothing and has no way to tell whether the button did anything at all - so "Showing 12 of 48 results" is the entire feedback loop for them. It is rendered unconditionally rather than only once results arrive, because a live region that mounts at the same moment its text changes is usually not announced; the browser has to have been watching it beforehand. That single detail is the most common way this pattern is got wrong. When the list is exhausted the button stays in the DOM and relabels rather than unmounting - removing the element a user just activated drops their focus to `<body>` and loses their place on the page.