Hi 🤓 Cảm ơn bạn đã ghé thăm blog này, nếu những bài viết trên blog giúp ích cho bạn. Bạn có thể giúp blog hiển thị quảng cáo bằng cách tạm ngừng ad blocker 😫 và để giúp blog duy trì hoạt động nếu bạn muốn.
Cảm ơn bạn!

Giới thiệu

Hello các bạn, dạo gần đây cảm thấy lười viết blog quá, nhưng hôm nay mình mới tìm ra một chủ đề mới cho blog của mình 😄, đành phải bớt lười lại và viết bài viết này hi vọng nó giúp ích cho các bạn nhé 🔥.

Như chúng ta đã biết, một trang web được thành lập và hoạt động trong suốt khoảng thời gian đó thì việc những content mới sẽ được tạo ra và như thế thì trang web sẽ có rất nhiều content, để tăng tốc độ trang web cũng như trải nghiệm người dùng tốt hơn (UX) thì chúng ta sử dụng Pagination - phân trang.

Hôm nay mình sẽ hướng dẫn cho các bạn một cách phân trang áp dụng cho hầu hết các trường hợp 😄.

Dynamic Pagination

Đầu tiên chúng ta sẽ xem nó hoạt động như thế nào nhé:

Ở video trên mình đã giới hạn số trang là 10, các bạn có thể test nhiều hơn 😄. Mình đã Truncate đi một vài liên kết trang do đó chúng ta nhìn sẽ thấy gọn hơn rất nhiều.

Chúng ta sẽ cùng nhau tìm hiểu về cách làm này nhé:

ở phần html, để đơn giản mình sẽ chỉ tạo một phần tử để triển khai code:

<div id="pagination"></div>

Đây là div mà từ Javascript sau khi có được số trang mình sẽ render toàn bộ liên kết trang vào đây.

Phần Javascript:

Chúng ta sẽ lấy phần tử đã tạo ở trên, mình đặt phần tử này là pg:

const pg = document.getElementById('pagination');

Tiếp theo, khởi tạo một object chứa những thông tin cần thiết cho pagination:

const valuePage = {
    truncate: true, // Rút gọn liên kết trang hoặc không (true <=> rút gọn)
    curPage: 1, // Khởi tạo page hiện tại là 1
    numLinksTwoSide: 1, // Số lượng liên kết muốn hiển thị ở hai bên trang hiện tại.
    totalPages: 10, // Tổng số trang
};

Mình sẽ giải thích giá trị numLinksTwoSide: 1, như mình đã giải thích ở trên thì đây là số liên kết hiển thị ở hai bên trang hiện tại. Giả sử mình đang ở trang thứ 5. và số lượng liên kết 2 bên ở trang thứ 5 mình muốn hiện thị là 1.

Ta sẽ được như hình sau đây:

ex1pg

Khởi tạo xong chúng ta cùng thực hiện tạo hàm pagination() để xử lí phân trang nhé 😄.

Trong hàm này đầu tiên ta sẽ lấy các giá trị cần thiết từ object valuePage để sử dụng. Mình dùng cú pháp destructuring assignment lấy các giá trị bên trong object.

function pagination() {
    const { totalPages, curPage, truncate, numLinksTwoSide: delta } =         valuePage;
}

Tiếp theo ta sẽ tạo một biến để kiểm tra giá trị deltatotalPages nếu phù hợp với điều kiện sẽ truncate. Mình gọi ràng buộc này là range:

const range = delta + 4; // use for handle visible number of links left side

Biến này giúp ta kiểm soát được số liên kết bên trái xuất hiện và điều kiện truncate phụ thuộc một phần vào range.

Như ở video trên, số liên kết bên trái = delta + 4 = 1 + 4 = 5

ex2pg

Nãy giờ có thể các bạn thắc mắc số 4 này ở đâu ra vậy ta??🤨. Mình sẽ giải thích qua các hình ảnh để các bạn hiểu:

Giả sử mình muốn khi click vào số cuối của Left Side theo ví dụ là số 5, thì ta sẽ thực hiện hành động thu gọn những liên kết trước lại. Để thu gọn thì mình sẽ giới hạn tối thiểu số trang bị rút gọn đi là 2 trang. Như hình trên mình đã thu gọn thành 1 ... 4 5 (bỏ trang 2,3).

Điều kiện để thu gọn là lấy số trang hiện tại - số liên kết muốn hiển thị phải lớn hơn 3.

Mình sẽ thu gọn bên trái trước, bên phải sẽ tương tự. Mình tạo một biến và làm như trên:

// use for truncate two side
const numberTruncateLeft = curPage - delta;
const numberTruncateRight = curPage + delta;

Ví dụ: delta (số liên kết muốn hiển thị ở một bên trang hiện tại) = 1, trang hiện tại là: 5.

Ta tính được numberTruncateLeft = 5 - 1 = 4 > 3 (Thu gọn được). Tại sao lại lớn hơn 3? Vì sau khi chạy vòng for ta sẽ cho hiện những trang có index > 3 và chỉ số 4 sẽ được in ra ở bên trái trang hiện tại (vì mình muốn số lượng trang hiển thị ở mỗi bên trang hiện tại là 1 => Từ trang hiện tại là 5 đi lùi về một số là 4 bên phải sẽ tăng lên 1 số là số 6).

Ví dụ: delta = 1, trang hiện tại là: 4. numberTruncateLeft = 4 - 1 = 3 (Không thu gọn). Nếu bạn muốn thu gọn nó sẽ ra như này: 1 ... 3 4

Như vậy chỉ có một trang bị thu gọn sẽ không hợp lí. Và đây chính là lí do mình giới hạn tối thiểu là 2 trang bị thu gọn. Do đó điều kiện ta rút ra được là: numberTruncate = số trang hiện tại - số liên kết muốn hiển thị một bên > 3

Một ví dụ nữa để các bạn hiểu hơn:

delta = 2 Trang hiện tại là 6

numberTruncateLeft = 6 - 2 = 4 (Thu gọn).

Kết quả ta được:

ex3pg

Tới đây chắc các bạn đã hiểu ra ý tưởng của cách phân trang này 😄. Nếu chưa hiểu thì tiếp tục nhé, mình sẽ giải thích tiếp:

Ở trên mình đã triển khai một biến range = delta + 4 biến này khá quan trọng 👏. Vì mình muốn nếu thu gọn thì phải ít nhất 2 phần tử bị thu gọn không tính số 1 nên range = delta + 4.

Ví dụ: ta muốn số liên kết muốn hiển thị một bên là delta = 2, range là số liên kết bên trái hiển thị là 2 + 4 = 6

Như vậy khi bạn click vào số 6 dùng for ta lùi 2 số ta sẽ được: 1 ... 4 5 6.

Giải thích dài dòng nhưng mình muốn các bạn hiểu rõ hơn ^^.

Sau đây ta triển khai vòng lặp để hiển thị các liên kết trang và xử lí truncate trong vòng for này nhé:

    let active = '';
    for (let pos = 1; pos <= totalPages; pos++) {
        active = pos == curPage ? 'active' : '';
        if (totalPages >= 2 * range - 1 && truncate) {
            ...
        } else {
            // not truncate
            render += renderPage(pos, active);
        }
    }

Trong vòng for mình đặt class "active" vào trang hiện tại. Điều kiện thỏa mãn totalPages >= 2 * range - 1 && truncate sẽ thực hiện thu gọn liên kết trang nếu không thỏa sẽ hiển thị trang như bình thường. Mình sẽ giải thích điều kiền này sau.

Nếu thỏa ta sẽ chia ra ba trường hợp như video dưới:

3 Trường hợp đó là:

  • Thu gọn bên trái và cả bên phải
  • Thu gọn bên trái không thu gọn bên phải
  • Thu gọn bên phải không thu gọn bên trái

Mình sẽ giải thích khi code 3 trường hợp trên:

Trường hợp: Thu gọn bên trái và cả bên phải

Điều kiện: numberTruncateLeft > 3 && numberTruncateRight < totalPages - 3 + 1 const numberTruncateLeft = curPage - delta;

Như mình đã giải thích ở trên để xảy ra thì số lượng liên kết muốn thu gọn một bên ít nhất là 2 do đó numberTruncateLeft = curPage - delta > 3. Bên phải ta làm tương tự.

Ta sẽ lấy vị trí của các vị trí trang thỏa điều kiện trên để hiển thị.

let renderTwoSide = '';
for (let pos = 1; pos <= totalPages; pos++) {
  active = pos == curPage ? 'active' : '';

  // truncate
  if (totalPages >= 2 * range - 1 && truncate) {
    if (
      numberTruncateLeft > 3 &&
      numberTruncateRight < totalPages - 3 + 1
    ) {
      // truncate 2 side
      if (pos >= numberTruncateLeft && pos <= numberTruncateRight) {
        renderTwoSide += renderPage(pos, active);
      }
    } 
  } else {
    // not truncate
    render += renderPage(pos, active);
  }
}

Hình ảnh minh họa:

ex4pg

Nếu trường hợp trên không xảy ra ta sẽ thực hiện code 2 trường hợp còn lại: Điều kiện xảy ra 2 trường hợp trên:

curPage < range && pos <= range) ||
curPage > totalPages - range && pos >= totalPages - range + 1)

Mình sẽ từ vị trí trang hiện tại tìm phần tử có vị trí thuộc vùng range nếu trang hiện tại ở trong vùng này. Trường hợp trang hiện tại nằm bên phải ta chỉ việc làm ngược lại trường hợp trên.

Trường hợp đặc biệt là trang hiện tại nằm trong cả vùng range và vùng bên phải ví dụ trang hiện tại là 4 và tổng số trang là 7. Giả sử số liên kết hiển thị 1 bên là delta = 1 => range = 4 + 1 = 5 như vậy trang hiện tại là 4 nằm trong vùng range và trang hiện tại cũng thuộc vùng bên phải. Điều này gây cho ta việc thu gọn trang ngoài ý muốn

Để tránh điều này mình đã thêm điều kiện ở trên totalPages >= 2 * range - 1

Hình minh họa:

ex5pg

Để các bạn có cái hình tổng quát hơn thì chúng ta cùng xem toàn bộ phần code nhé:

Kết luận

Như vầy là chúng ta đã thực hiện xử lí xong phần Dynamic Pagination rồi. Nếu có thắc mắc gì về bài viết các bạn có thể liên hệ mình nhé.

Chúc các bạn học tốt 😃.

Có thể bạn thích ⚡
homiedev
About Me

Hi, I'm @devnav. Một người thích chia sẻ kiến thức, đặc biệt là về Frontend 🚀. Trang web này được tạo ra nhằm giúp các bạn học Frontend hiệu quả hơn 🎉😄.

Chúc các bạn tìm được kiến thức hữu ích trong blog này 😁😁.