Code tạo phân trang blogger bằng Ajax Get URL số thứ tự

Phân trang bằng Ajax Get URL là phương pháp tạo thanh số trang (1, 2, 3, …) cho Blogger mà không cần dùng nguồn cấp dữ liệu feeds. Cực kỳ hữu ích với blog đã chặn feeds, đặt blog riêng tư, hoặc cần tốc độ tải nhanh hơn so với phương pháp đọc JSON feeds.

Bài viết này hướng dẫn từ A đến Z: từ nguyên lý, ưu nhược điểm, đến full code Blogger dán được ngay và cách cấu hình số bài mỗi trang, số trang hiển thị.

1. Phân trang Ajax Get URL là gì?

Thay vì đọc dữ liệu từ /feeds/posts/default, script sẽ dùng jQuery $.get() tải HTML của trang tìm kiếm Blogger tại URL:

/search?max-results=200

Sau đó script phân tích HTML trả về, đếm số post bằng class .post, đọc ngày đăng qua class .post_date (định dạng ISO8601), rồi sinh ra các liên kết phân trang có dạng:

/search?updated-max=YYYY-MM-DDTHH:MM:SS%2B07:00&start=N&max-results=10&page=P

2. Ưu điểm và nhược điểm

Ưu điểm

  • ✅ Không cần feeds — chạy được cả khi Blogger đã chặn feeds hoặc blog riêng tư.
  • ✅ Tốc độ nhanh — $.get URL nhanh hơn đọc JSON feeds.
  • ✅ Hiển thị số trang trực quan — không chỉ hiện "Trang trước / Trang sau".
  • ✅ Hoạt động với label search và query search — tự phát hiện view hiện tại.

Nhược điểm

  • ⚠️ Phụ thuộc vào cấu trúc HTML của theme: cần class .post và class ngày .post_date có data-date ISO8601.
  • ⚠️ Bắt buộc bài viết có thẻ <!--more--> để đếm tổng số post chính xác.
  • ⚠️ Nếu theme đổi cấu trúc (class cha khác .post), script cần cập nhật selector.

3. Điều kiện bắt buộc trước khi cài

  1. Theme có jQuery (nếu chưa có, thêm trước </body>).
  2. Theme có element .post bao mỗi bài đăng.
  3. Theme có element .post_date với thuộc tính data-date (ISO8601).
  4. Mỗi bài đăng đều có thẻ <!--more-->.

3.1. Thêm thẻ <!--more--> cho bài cũ

Vào trình soạn thảo HTML của bài viết, chèn sau vài dòng mở đầu:

<!--more-->

Nếu blog có nhiều bài, dùng chức năng Sửa bài viết hàng loạt hoặc chèn thủ công từng bài.

4. Cài đặt phân trang Ajax Get URL cho Blogger

Bước 1 — Mở tiện ích Blog1

Vào Blogger → Chủ đề → Chỉnh sửa HTML → Ctrl+F tìm <b:widget id='Blog1'.

Bước 2 — Chèn thẻ <nav class='page-numbers'>

Tìm thẻ <b:includable id='main'> (hoặc <b:includable id='main' var='top'>), rồi chèn ngay trước </b:includable>:

<b:if cond='data:view.isMultipleItems and !data:view.isArchive'>
<nav class='page-numbers nav-pagination' id='blog-pager'/>
</b:if>

Bước 3 — Thêm data-date cho ngày đăng

Tìm element hiển thị ngày đăng của mỗi bài, thường có dạng:

<span class='post_date'><data:post.date/></span>

Thêm thuộc tính expr:data-date='data:post.date.iso8601':

<span class='post_date' expr:data-date='data:post.date.iso8601'><data:post.date/></span>

Nếu theme không có element ngày đăng, thêm dòng ẩn dưới đây:

<span class='post_date' expr:data-date='data:post.date.iso8601' style='display:none'>
<data:post.date/>
</span>

Bước 4 — Dán CSS

<b:if cond='data:view.isMultipleItems and !data:view.isArchive'>
<b:tag name='style'>
/* ==================== VEOFOOD PAGINATION CSS ==================== */
.nav-pagination{ display:flex;flex-wrap:wrap;justify-content:center;align-items:center; gap:8px;margin:24px auto;padding:0;list-style:none; max-width:100%;font-family:'Segoe UI',Roboto,Arial,sans-serif; }
.nav-pagination .page-number{ display:inline-flex;align-items:center;justify-content:center; min-width:38px;height:38px;padding:0 12px; border-radius:10px; background:#fff;color:#5A0FC8;border:1px solid #e5d9ff; font-size:14px;font-weight:700;text-decoration:none; transition:all .2s cubic-bezier(.4,0,.2,1); cursor:pointer; }
.nav-pagination .page-number:hover{ background:#f3ecff;border-color:#c8b3ff;transform:translateY(-1px); box-shadow:0 4px 12px rgba(90,15,200,.15); }
.nav-pagination .page-number.current{ background:linear-gradient(135deg,#5A0FC8,#7c3aed); color:#fff;border-color:transparent; box-shadow:0 4px 14px rgba(90,15,200,.35); pointer-events:none; }
.nav-pagination .page-number[rel="prev"], .nav-pagination .page-number[rel="next"]{ padding:0;width:38px; }
.nav-pagination .page-number svg{display:block;fill:currentColor} @media (max-width:480px){ .nav-pagination{gap:6px} .nav-pagination .page-number{min-width:34px;height:34px;font-size:13px} }
</b:tag>
</b:if>

Bước 5 — Dán JS

Dán trước thẻ </body>:

<script>
// ============================================================
// VEOFOOD PAGINATION — AJAX GET URL
// ============================================================
var data = {
view: {
isHomepage: "<b:if cond='data:view.isHomepage'>true</b:if>",
isSearch: "<b:if cond='data:view.isSearch and !data:view.search.label and !data:view.search.query'>true</b:if>",
isLabelSearch: "<b:if cond='data:view.search.label'>true</b:if>",
isSearchQuery: "<b:if cond='data:view.search.query'>true</b:if>"
},
blog: {
pageName: "<b:if cond='data:blog.pageName'><data:blog.pageName/></b:if>",
homepageUrl: "<data:blog.homepageUrl/>",
searchqueryescaped: "<b:if cond='data:blog.searchQuery'><data:blog.searchQuery/></b:if>"
},
messages: {
home: "<data:messages.home/>"
}
};
//<![CDATA[
var pages = {
num: {
posts: 10, // ← Số bài mỗi trang (khớp Settings → Posts → Posts per page)
pages: 5, // ← Số trang hiển thị (số lẻ để căn giữa đẹp)
max: 500 // ← Số bài tối đa (lớn hơn tổng số bài blog)
},
element: {
id: '#blog-pager',
parent: '.Blog .post',
child: '.post_date'
}, messages: {
next: '<svg height="16px" viewBox="0 0 608 1280" width="16px" xmlns="http://www.w3.org/2000/svg"><path d="M595 288q0 13-10 23L192 704l393 393q10 10 10 23t-10 23l-50 50q-10 10-23 10t-23-10L23 727q-10-10-10-23t10-23l466-466q10-10 23-10t23 10l50 50q10 10 10 23z"/></svg>',
prev: '<svg height="16px" viewBox="0 0 608 1280" width="16px" xmlns="http://www.w3.org/2000/svg"><g transform="translate(608 0) scale(-1 1)"><path d="M595 288q0 13-10 23L192 704l393 393q10 10 10 23t-10 23l-50 50q-10 10-23 10t-23-10L23 727q-10-10-10-23t10-23l466-466q10-10 23-10t23 10l50 50q10 10 10 23z"/></g></svg>',
page: 'Trang',
nextpage: 'Trang kế',
prevpage: 'Trang trước'
}
};
//]]> </script>
<b:if cond='data:view.isMultipleItems and !data:view.isArchive'>
<script>
//<![CDATA[
(function(){
var serviceUrl = '';
if (data.view.isHomepage === 'true' || data.view.isSearch === 'true') { serviceUrl = '/search?max-results=' + pages.num.max; }
if (data.view.isLabelSearch === 'true') { serviceUrl = '/search/label/' + data.blog.pageName + '?max-results=' + pages.num.max; }
if (data.view.isSearchQuery === 'true') { serviceUrl = '/search?q=' + data.blog.searchqueryescaped + '&max-results=' + pages.num.max; }
$.get(serviceUrl, function(resp) { var posts = $(resp).find(pages.element.parent); if (!posts.length) return;
var url = window.location.href; var totalPages = Math.ceil(posts.length / pages.num.posts); var currentPage = 1; var html = '';
// Xác định trang hiện tại từ URL if (url.indexOf('&page=') !== -1 || url.indexOf('?page=') !== -1) { var m = url.match(/[?&]page=(\d+)/); if (m) currentPage = Number(m[1]) || 1; }
var half = Math.floor(pages.num.pages / 2); if (half === pages.num.pages - half) pages.num.pages = half * 2 + 1;
var startPage = currentPage - half; if (startPage < 1) startPage = 1; var endPage = startPage + pages.num.pages - 1; if (endPage > totalPages) endPage = totalPages;
function getDate(idx) { var el = posts.eq(idx).find(pages.element.child); var d = el.attr('data-date') || ''; return d.replace('+', '%2B'); }
function buildUrl(pageNum, rel) { var start = (pageNum - 1) * pages.num.posts; if (pageNum === 1) { if (data.view.isHomepage === 'true' || data.view.isSearch === 'true') return data.blog.homepageUrl; if (data.view.isLabelSearch === 'true') return '/search/label/' + data.blog.pageName + '?max-results=' + pages.num.posts; if (data.view.isSearchQuery === 'true') return '/search?q=' + data.blog.searchqueryescaped + '&max-results=' + pages.num.posts; }
var t = (pageNum - 1) * pages.num.posts - 1;
var d = getDate(t); if (data.view.isLabelSearch === 'true') { return '/search/label/' + data.blog.pageName + '?updated-max=' + d + '&max-results=' + pages.num.posts + '&start=' + start + '&page=' + pageNum; } if (data.view.isSearchQuery === 'true') { return '/search?q=' + data.blog.searchqueryescaped + '&updated-max=' + d + '&max-results=' + pages.num.posts + '&start=' + start + '&page=' + pageNum; } return '/search?updated-max=' + d + '&max-results=' + pages.num.posts + '&start=' + start + '&page=' + pageNum; }
// Nút Prev if (currentPage > 1) { html += '<a class="page-number" rel="prev" title="' + pages.messages.prevpage + '" href="' + buildUrl(currentPage - 1, 'prev') + '">' + pages.messages.prev + '</a>'; }
// Số trang for (var i = startPage; i <= endPage; i++) { var cls = (i === currentPage) ? 'page-number current' : 'page-number'; var title = pages.messages.page + ' ' + i; html += '<a class="' + cls + '" title="' + title + '" href="' + buildUrl(i) + '">' + i + '</a>'; }
// Nút Next if (currentPage < totalPages) { html += '<a class="page-number" rel="next" title="' + pages.messages.nextpage + '" href="' + buildUrl(currentPage + 1, 'next') + '">' + pages.messages.next + '</a>'; }
var pager = document.querySelector(pages.element.id); if (pager) pager.innerHTML = html; });
})();
//]]> </script> </b:if>

5. Cấu hình số bài và số trang hiển thị

Trong đoạn JS ở Bước 5, tìm object pages.num:

num: {
posts: 10, // ← Số bài mỗi trang
pages: 5, // ← Số trang hiển thị
max: 500 // ← Tổng số bài tối đa (phải lớn hơn tổng bài blog)
}
Tham số Ý nghĩa Khuyến nghị
postsSố bài hiển thị mỗi trang6 – 12 (mobile dùng 6, desktop 10)
pagesSố trang hiển thị quanh trang hiện tại5 hoặc 7 (số lẻ để căn giữa)
maxSố bài tối đa script sẽ tải về để đếm≥ tổng số bài blog (500 – 1000)

Quan trọng: Phải đồng bộ pages.num.posts với Blogger → Settings → Posts → Posts per page. Nếu Blogger đang set 7 bài/trang mà script để 10 → số trang sẽ sai.

6. Troubleshooting — Lỗi thường gặp

6.1. Phân trang không hiện

  • Kiểm tra URL hiện tại: script chỉ chạy khi view là multiple items và không phải archive.
  • Mở DevTools Console xem có lỗi $.get is not a function → thiếu jQuery.
  • Kiểm tra selector .Blog .post có khớp với theme không (F12 → Elements).

6.2. Số trang bị sai (nhiều/ít hơn thực tế)

  • Do bài viết thiếu thẻ <!--more-->.
  • Do pages.num.posts khác cấu hình Blogger.
  • Do pages.num.max nhỏ hơn tổng số bài blog.

6.3. Bấm số trang bị lỗi 404

  • Do class .post_date thiếu thuộc tính data-date → kiểm tra Bước 3.
  • Do định dạng ngày không đúng ISO8601 → phải là data:post.date.iso8601.

6.4. Phân trang hiện nhưng CSS không đẹp

  • Do theme có CSS khác ghi đè lên .nav-pagination. Thêm !important hoặc tăng specificity.
  • Do file CSS dán sai vị trí — phải nằm trong <b:if> của Bước 4.

7. Checklist triển khai

  • ☐ Đã backup theme trước khi sửa.
  • ☐ Theme có jQuery.
  • ☐ Mỗi bài viết có <!--more-->.
  • ☐ Đã chèn <nav id='blog-pager'> trong <b:includable id='main'>.
  • ☐ Đã thêm expr:data-date='data:post.date.iso8601' cho .post_date.
  • ☐ Đã dán CSS ở Bước 4.
  • ☐ Đã dán JS ở Bước 5.
  • ☐ pages.num.posts khớp với cấu hình Blogger.
  • ☐ Test chuyển trang 1 → 2 → 3 → quay lại.
  • ☐ Test trên mobile và desktop.
  • ☐ Test với label search (/search/label/xxx).
  • ☐ Test với query search (/search?q=xxx).

8. Kết luận

Phân trang Ajax Get URL là giải pháp mạnh, nhẹ, không cần feeds, phù hợp cho mọi blog Blogger — đặc biệt khi blog đã chặn feeds hoặc để riêng tư. Chỉ cần đảm bảo 3 điều kiện: có jQuery, có class .post, và mọi bài có thẻ <!--more--> là có thể triển khai ngay trong 10 phút.

Nếu bạn cần bản phân trang kết hợp Load More tự động (infinite scroll) hoặc sticky sidebar, đọc thêm các bài viết liên quan trên veospot.net.

Đã đọc đến đây? Cảm ơn bạn đã theo dõi bài viết "Code tạo phân trang blogger bằng Ajax Get URL số thứ tự". Hy vọng những thông tin trên sẽ hữu ích với bạn. Nếu có câu hỏi, góp ý hoặc muốn chia sẻ thêm kinh nghiệm, hãy để lại bình luận bên dưới. Mỗi ý kiến của bạn sẽ giúp VEOSpot ngày càng hoàn thiện và mang đến nhiều nội dung chất lượng hơn.

VeoSpot.net có sinh nhật vào 9 tháng 3

Đăng nhận xét