Didin
Didin

Reputation: 49

Implement Pagination in JavaScript with limit and offset

I have a huge data being returned from a fetch api call. I want to limit the displayed data to 10 per page and have more data returned when the next page button is clicked. How can I implement that?

limit is set to 10 and offset is set to 0. The maximum data that can be returned per page is 150.


<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button class = B1 id="photos">View photos</button>
<div id="showResults"></div>
<div>
  <nav aria-label="Page navigation example">
    <ul class="pagination">
     
      <li class="page-item">
        <button class="page-link" id="nextButton">Next</button>
      </li>
    </ul>
  </nav>
</div>

<script> 
let limit = 10;
let offset = 0;
const showPhoto = (key, value) => {
 const pre_x = document.createElement("pre");
 const dt_x = document.createElement("dt");
 const dd_x = document.createElement("dd")
 dt_x.textContent = key;
 pre_x.appendChild(dt_x);

  {
      dd_x.textContent = value;
   }
   pre_x.appendChild(dd_x);
 return pre_x;
};

const structurePhotos = (obj) => {
 const dl = document.createElement("dl");
 for (let k in obj) {
   let j = obj[k];
   if (typeof obj[k] === "object") {
     j = JSON.stringify(obj[k], null, 2);
   }
dl.appendChild(showPhoto(k, j));
  }
 return dl;
};


function getPhotos(url) {
 fetch(url)
   .then((res) => (res.ok ? res.json() : Promise.reject(res)))
   .then((data) => {

     if (Array.isArray(data)) {
       data.forEach((photo) => {
         showResults.append(
          structurePhotos(photo),
         );
       });
     } 
   })
   .catch(console.error);
}

const photos = document.getElementById("photos");
photos.addEventListener(
 "onclick",
 getPhotos(`https://jsonplaceholder.typicode.com/photos`)
);

</script>

</body>

</html>

limit is set to 10 and offset is set to 0. The maximum data that can be returned per page is 150.

Upvotes: 1

Views: 8721

Answers (3)

Shiplu
Shiplu

Reputation: 516

My situation was the same as yours. I was also looking for a pagination system where I will be able to paginate my data along with index and offset. But couldn't get any satisfactory solution out there. Finally, I made it myself. Maybe it's gonna help others.

const listEl = document.querySelector(".list");
const pageIndexEl = document.querySelector(".page-index");
const prevBtn = document.querySelector(".prev");
const nextBtn = document.querySelector(".next");

const items = Array.from(listEl.children);
const itemPerPage = 5;
const navPerPage = 3;
const pagingItems = pagingItemsArr();
let currentActive = 0;


nextBtn.addEventListener("click", e => {
  currentActive++;
  if (currentActive > pagingItems.length) {
    currentActive = pagingItems.length;
  }
  update();
});

prevBtn.addEventListener("click", e => {
  currentActive--;
  if (currentActive < 0) {
    currentActive = 0;
  }
  update();
});

function btnControl() {
  if (currentActive == 0) {
    prevBtn.disabled = true;
  } else if (currentActive == pagingItems[pagingItems.length - 1]) {
    nextBtn.disabled = true;
  } else {
    nextBtn.disabled = false;
    prevBtn.disabled = false;
  }
}

function update() {
  const temp = Array.from(pageIndexEl.children);
  temp.forEach((item, idx) => {
    if (idx == currentActive) {
      removeActiveClass();
      item.classList.add("active");
    } else {
      item.classList.remove("active");
    };
  });
  btnControl();

  let currentlyShowing = temp.filter(item => !item.classList.contains("hide"));

  if (currentActive == currentlyShowing[currentlyShowing.length - 1].dataset.id) {
    const arr = temp.slice(currentActive, navPerPage + currentActive);
    updatePaging(arr);
  }

  if (currentActive == currentlyShowing[0].dataset.id - 2) {
    const arr = temp.slice(Math.floor(currentActive - (navPerPage - 1)), (2 + currentActive) - 1);
    updatePaging(arr);
  }
  showPaginatedItems(currentActive + 1);
}

function updatePaging(arr) {
  const allItem = Array.from(pageIndexEl.children);

  allItem.forEach(item => {
    if (!item.classList.contains("hide")) {
      item.classList.add("hide");
    }
  });

  allItem.filter(item => arr.includes(item))
    .forEach(item => item.classList.remove("hide"));
}

function removeActiveClass() {
  const actives = document.querySelectorAll(".active");
  actives.forEach(item => item.classList.remove("active"));
}

function displayPagingItems() {
  pageIndexEl.innerHTML = "";
  pagingItems.forEach((item, idx) => {
    pageIndexEl.innerHTML += `<li
      ${idx == 0 ? "class='active'" : ""}
      ${idx >= navPerPage ? "class='hide'" : ""}
      data-id="${idx + 1}" 
      >${item}</li>
    `;
  });
}

function pagingItemsArr() {
  const totalPage = Math.ceil(items.length / itemPerPage);
  const arr = [];
  for (let i = 0; i < totalPage; i++) {
    arr.push(i);
  }
  return arr;
}

function showPaginatedItems(pageIndex = 1) {

  const itemsArr = items.slice((pageIndex - 1) * itemPerPage, pageIndex * itemPerPage);
  
  listEl.innerHTML = "";
  itemsArr.forEach(item => {
    listEl.innerHTML += `<li>${item.innerText}</li>`;
  });
}

function init() {
  displayPagingItems();
  btnControl();
  showPaginatedItems();
}
init();
.app-container {
  max-width: 500px;
  min-height: 100vh;
  margin: 0 auto;
}

.list {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  list-style-type: none;
}

.list li {
  background-color: #999;
  width: 100%;
  margin: 10px 0;
  text-align: center;
}

.btn-container {
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  margin: 20px 0;
}

.btn {
  padding: 5px 10px;
  cursor: pointer;
}

.btn:disabled {
  cursor: not-allowed;
}

.page-index li {
  background-color: #fff;
  border: 1px solid #999;
  border-radius: 2px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin: 0 5px;
  cursor: pointer;
  transition: 0.1s ease-in-out;
}

.page-index li.hide {
  display: none;
}

.page-index li.active {
  background-color: #ddd;
}

.page-index li:focus,
.page-index li:hover {
  background-color: #ddd;
}

.page-index li:focus,
.page-index li:active,
.page-index li.active:focus,
.page-index li.active:active {
  transform: scale(0.95);
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Pagination</title>
</head>

<body>

  <div class="app-container">
    <ul class="list">
      <li class="active">List1</li>
      <li>List2</li>
      <li>List3</li>
      <li>List4</li>
      <li>List5</li>
      <li>List6</li>
      <li>List7</li>
      <li>List8</li>
      <li>List9</li>
      <li>List10</li>
      <li>List11</li>
      <li>List12</li>
      <li>List13</li>
      <li>List14</li>
      <li>List15</li>
      <li>List16</li>
      <li>List17</li>
      <li>List18</li>
      <li>List19</li>
      <li>List20</li>
      <li>List21</li>
      <li>List22</li>
      <li>List23</li>
      <li>List24</li>
      <li>List25</li>
      <li>List26</li>
      <li>List2</li>
      <li>List28</li>
      <li>List29</li>
      <li>List30</li>
      <li>List31</li>
      <li>List32</li>
      <li>List33</li>
      <li>List34</li>
      <li>List35</li>
      <li>List36</li>
    </ul>
    <div class="btn-container">
      <button class="btn prev">&larr;</button>
      <ul class="page-index">
        <!-- <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li> -->
      </ul>
      <button class="btn next">&rarr;</button>
    </div>
  </div>
  <script src="./index.js"></script>
</body>

</html>

<!-- https://stackoverflow.com/questions/25434813/simple-pagination-in-javascript -->

Upvotes: 0

JASON SAMUEL T
JASON SAMUEL T

Reputation: 36

  • Use Javascript built-in array function slice()

  • For Ex:

    let fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango", "Banana", "Orange", "Lemon", "Apple", "Mango"];
    fruits.slice(0, 3);     // 0 => offset, 3 => limit
    
  • output

    output => [Banana,Orange,Lemon]
    

Upvotes: 1

IVO GELOV
IVO GELOV

Reputation: 14259

If you can not change the backend/API to use pagination - you can use the following function to split the Array with results from the API into smaller chunks:

function arrChunk(arr, size)
{
  return arr.reduce((acc, e, i) =>
  {
    if (i % size)
    {
      acc[acc.length - 1].push(e);
    }
    else
    {
      acc.push([e]);
    }
    return acc;
  }, []);
}

But the best option is to change the backend and avoid transferring excessive data over the network.

Upvotes: 0

Related Questions