Robert Birming

Bear Blog plugins

One of my first requests when I started using Bear 1 back in early 2023 was a pagination feature. The list gets quite long once you’ve written a lot of posts.

So I was pretty happy to see that Herman has now added a Bear Blog plugins page. It’s described like this:

The following scripts are extensions for the Bear Blog platform. These are not official extensions to Bear and may need a bit of tweaking here and there.

The first plugin on the list is Pagination on blog. Yay! But it didn’t work right away. After some fiddling 2, I got it working (you can see it in action on my blog page).

Here’s the script I’m using in the footer:

<script>
document.addEventListener('DOMContentLoaded', () => {
  const list = document.querySelector('.blog-posts');
  if (!list) return; // bail if not on a list page

  const posts = Array.from(list.querySelectorAll('li'));
  const postsPerPage = 20;
  const totalPages = Math.max(1, Math.ceil(posts.length / postsPerPage));
  let currentPage = 1;

  // Build controls
  const nav = document.createElement('nav');
  nav.className = 'pagination';
  nav.innerHTML = `
    <button type="button" id="prevPage" aria-label="Previous page">Previous</button>
    <span id="pageInfo"></span>
    <button type="button" id="nextPage" aria-label="Next page">Next</button>
  `;
  list.insertAdjacentElement('afterend', nav);

  const prevBtn = nav.querySelector('#prevPage');
  const nextBtn = nav.querySelector('#nextPage');
  const pageInfo = nav.querySelector('#pageInfo');

  function showPage(page) {
    const start = (page - 1) * postsPerPage;
    const end = start + postsPerPage;

    posts.forEach((post, i) => {
      post.style.display = (i >= start && i < end) ? '' : 'none';
    });

    pageInfo.textContent = `Page ${page} of ${totalPages}`;
    prevBtn.disabled = page === 1;
    nextBtn.disabled = page === totalPages;
    currentPage = page;
  }

  prevBtn.addEventListener('click', () => {
    if (currentPage > 1) showPage(currentPage - 1);
    history.replaceState(null, '', `#page=${currentPage}`);
  });

  nextBtn.addEventListener('click', () => {
    if (currentPage < totalPages) showPage(currentPage + 1);
    history.replaceState(null, '', `#page=${currentPage}`);
  });

  // Start on hash page if present, like #page=3
  const match = location.hash.match(/page=(\d+)/);
  const startPage = match ? Math.min(totalPages, Math.max(1, parseInt(match[1], 10))) : 1;
  showPage(startPage);

  // Hide controls if not needed
  if (totalPages <= 1) nav.style.display = 'none';
});
</script>

And some basic styles to go with it:

<style>
/* Tiny defaults, tweak as you like */
.pagination {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: 1.2rem 0 0.4rem;
}
.pagination button[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}
</style>
  1. It was on another domain back then, and I haven’t moved all my posts over here yet. That’s why the list of blog posts isn’t very long at the moment.

  2. With a little (okay, a lot) of help from ChatGPT. If it breaks, don’t look at me.