Giới thiệu với bạn tiện ích mới nhất: "Bài Viết Liên Quan".
Hãy cắt dán vào một HTML widget trong trang thiết kế để coi nó có chạy hay không.
Tôi có làm thử một cái, dùng chính đoạn mã mà tôi viết dưới đây, vào trang Anh ngữ của tôi, thì nó chạy ngay http://hoctro.blogspot.com/2010/08/testing-of-new-related-articles-widget.html . Trang Hoctro Việt cũng có tiện ích này.
Nếu bạn đã lắp các "Bài Liên Quan" widget trước của tôi, bạn cứ tự nhiên lắp tiếp cái này, chứ đừng bỏ cái kia vội. Cái mới này chỉ cần một bước cài đặt thôi, mà sau khi cài đặt xong ta có thể chuyển nó sang chỗ mới dễ dàng, không như cái cũ.
Như bạn thấy, cái tiện ích này tạo ra một dòng mã gồm những từ khóa/nhãn của trang đang xem, sau đó nó gửi lên Google, và được kết quả trả về gần sát nhất những gì bạn muốn tìm. Các kết quả này do google tìm kiếm trong trang của bạn mà thôi. Ta thấy cách này hệt như khi ta muốn tìm một bài viết nào đó trên Google http://www.google.com/ .
Theo thiển ý, đây là widget sát ý nhất với ý nghĩa "bài liên quan", vì nó đăng lên những bài giống với những từ khóa mà ta tìm nhất. tiện ích trước tôi viết không làm được điều này, vì nó chỉ trả về những kết quả mới nhất của từng nhãn trong trang mà thôi, những bài hay nhất mà xưa thì sẽ không được hiện ra. Ta đã dùng giải thuật "page ranking" của Google lần này để tìm các bài liên quan nhất.
Tôi sẽ đăng tiếp một bài viết tiếp theo về những động lực để làm tiện ích, giải thích cấu trúc mã, và những chỉnh sửa khác. Có nhiều cách như dùng CSS để chỉnh màu sắc, font chữ, hiện thêm dòng ngắn giới thiệu (snippet) hay không, v.v.
Chúc bạn thành công,
Thân mến,
Hoctro
8/16/2010
***
7/29/2026 Update; Tôi nhờ Claude Sonnet 5 sửa code để tiện ích này chạy tiếp. Nó làm xong trong vòng 10 phút. Thật kinh ngạc!!!! Code dưới đây là của nó, viết lại từ code cũ của tôi.
<!-- ===================================================================
Bài Viết Liên Quan — bản nâng cấp 2026
Thay Google AJAX Search API (đã ngừng hoạt động 11/2014) bằng
feed JSON theo nhãn của chính Blogger. Không cần API key.
Dựa trên ý tưởng gốc: Hoctro, "Related Articles - Take 5", 8/2010
https://hoctroviet.blogspot.com/2010/08/tien-ich-moi-viet-lien-quan-related.html
=================================================================== -->
<div id="hoctro-related-wrap">
<div id="hoctro-related-results">Đang tải bài viết liên quan…</div>
</div>
<style>
#hoctro-related-wrap { margin: 24px 0; }
#hoctro-related-heading { font-size: 1.05em; font-weight: bold; margin-bottom: 8px; }
#hoctro-related-results { font-size: 0.95em; }
#hoctro-related-results ul { list-style: none; margin: 0; padding: 0; }
#hoctro-related-results li { padding: 4px 0; border-bottom: 1px dotted #ccc; }
#hoctro-related-results li:last-child { border-bottom: none; }
#hoctro-related-results a { text-decoration: none; }
#hoctro-related-results a:hover { text-decoration: underline; }
#hoctro-related-results .hr-thumb { display:inline-block; width:16px; height:16px; vertical-align:middle; margin-right:6px; border-radius:2px; }
</style>
<script>
(function () {
'use strict';
var MAX_LABELS_TO_QUERY = 4; // số nhãn tối đa sẽ dò feed (giới hạn số request)
var MAX_RESULTS_SHOWN = 8; // số bài liên quan tối đa hiển thị
var callbackCounter = 0;
var pendingRequests = 0;
var collected = []; // {title, href, thumb}
var seenHrefs = {};
var currentHref = location.href.split('#')[0].split('?')[0];
function baseUrl() {
return location.protocol + '//' + location.host;
}
function escapeHtml(s) {
return String(s)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
// Bài đang xem chỉ có "liên quan" trên trang từng bài (item page).
// Blogger đặt class "item-page" hoặc <body> có data khác nhau tùy theme,
// nên cách chắc ăn nhất là: chỉ chạy nếu tìm thấy đúng 1 khối bài viết
// và có ít nhất 1 link nhãn trên trang.
function getCurrentLabels() {
var labels = [];
var seen = {};
// Các theme Blogger khác nhau đặt link nhãn ở những nơi/class khác nhau;
// dò theo thứ tự, dùng bộ chọn đầu tiên khớp được.
var selectors = [
'.post-labels a',
'.post-footer-line .labels a',
'a[rel="tag"]',
'a[href*="/search/label/"]'
];
for (var s = 0; s < selectors.length; s++) {
var links = document.querySelectorAll(selectors[s]);
if (links.length) {
for (var i = 0; i < links.length; i++) {
var href = links[i].getAttribute('href') || '';
var m = href.match(/\/search\/label\/([^?&#\/]+)/);
if (m) {
var label = decodeURIComponent(m[1].replace(/\+/g, ' '));
if (!seen[label]) { seen[label] = true; labels.push(label); }
} else if (!href) {
var text = (links[i].textContent || '').trim();
if (text && !seen[text]) { seen[text] = true; labels.push(text); }
}
}
break; // bộ chọn đầu tiên có kết quả là đủ, không dò tiếp
}
}
return labels;
}
function renderResults() {
var box = document.getElementById('hoctro-related-results');
var heading = document.getElementById('hoctro-related-heading');
if (!collected.length) {
box.innerHTML = '<em>Không tìm thấy bài viết liên quan.</em>';
return;
}
var html = '<ul>';
for (var i = 0; i < Math.min(collected.length, MAX_RESULTS_SHOWN); i++) {
var item = collected[i];
var thumb = item.thumb
? '<img class="hr-thumb" src="' + escapeHtml(item.thumb) + '" alt="">'
: '';
html += '<li>' + thumb + '<a href="' + escapeHtml(item.href) + '">' + escapeHtml(item.title) + '</a></li>';
}
html += '</ul>';
box.innerHTML = html;
heading.style.display = '';
}
function handleFeed(feed) {
pendingRequests--;
var entries = (feed && feed.feed && feed.feed.entry) ? feed.feed.entry : [];
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
var href = '';
for (var l = 0; l < entry.link.length; l++) {
if (entry.link[l].rel === 'alternate') { href = entry.link[l].href; break; }
}
if (!href || href === currentHref || seenHrefs[href]) continue;
seenHrefs[href] = true;
collected.push({
title: entry.title && entry.title.$t ? entry.title.$t : '(không có tiêu đề)',
href: href,
thumb: entry.media$thumbnail ? entry.media$thumbnail.url : ''
});
}
if (pendingRequests === 0) renderResults();
}
function queryLabel(label) {
pendingRequests++;
var cbName = 'hoctroRelatedCB_' + (callbackCounter++);
window[cbName] = function (feed) {
handleFeed(feed);
delete window[cbName];
};
var src = baseUrl() + '/feeds/posts/default/-/' + encodeURIComponent(label) +
'?alt=json-in-script&max-results=6&callback=' + cbName;
var script = document.createElement('script');
script.src = src;
script.async = true;
document.body.appendChild(script);
}
function init() {
var labels = getCurrentLabels();
if (!labels.length) {
document.getElementById('hoctro-related-results').innerHTML =
'<em>Bài này chưa gắn nhãn nên không thể tìm bài liên quan.</em>';
return;
}
labels = labels.slice(0, MAX_LABELS_TO_QUERY);
for (var i = 0; i < labels.length; i++) queryLabel(labels[i]);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>

