Recent và Random Posts là 2 tiện ích được các blogger khá ưa chuộng, về mặt dữ liệu nó đều thuộc phương pháp tải feed post get data, xong việc về xử lý thì recent post đơn giản hơn, và trong bài viết này tôi sẽ cùng các bạn tìm hiểu về chúng.
Xem demo
Cách thức thực hiện tương tự như bài feed post label đó là dùng 1 thẻ trắng span để lấy dữ liệu đầu vào
<span data-type="recent" data-no="8"></span> hoặc <span data-type="random" data-no="8"></span>
1. Recent post
Dịch ra nó là bài đăng gần đây, thiết nghĩ tạo ra nó hơi thừa thãi vì khi vào trang chủ thì các bài viết được sắp xếp theo thời gian gần đây rồi. Vậy tại sao tạo ra nó ? vì có nhiều blog không hiển thị bài đăng theo thời gian mà trang index họ hiển thị theo label, hoặc đơn giản hơn là họ muốn kéo nó vào sidebar tại trang bài viết
Việc tải bài viết gần đây rất đơn giản, chỉ cần gửi request /feeds/posts/default?alt=json và gán 1 giá trị max-results là xong
2. Random post
Bài viết ngẫu nhiên được sử dụng nhiều hơn vì bài viết hiển thị đa dạng hơn, và đương nhiên xử lý nó cũng phức tạp hơn
Giá trị quan trọng nhất với bài đăng ngẫu nhiên đó là start-index, tức ta sẽ lấy index ngẫu nhiên trong list từ 1 tới tổng số bài viết có trong blog của bạn kèm theo đó là giá trị max-results luôn bằng 1 để tránh tải thêm các entry.
Như vậy mỗi lần tải 1 bài ngẫu nhiên bạn sẽ gửi 1 request dạng /feeds/posts/default?alt=json&start-index=random&max-results=1. Công việc của ta sẽ phải tạo ra giá trị random đó
Tôi sẽ trình bày sơ qua ý tưởng trong link demo như sau
- Lấy số bài viết ngẫu nhiên từ thuộc tính data-no
- Sử dụng ajax call feed post với max-results=0 để lấy tổng số bài đăng
- Tạo 1 mảng gồm n giá trị với n chạy từ 1 tới tổng số bài đăng
- Mỗi lần gửi request tải feed ta sẽ dùng hàm kiểm tra giá trị start-index để tránh trùng lặp bài đăng (vì random tỉ lệ trùng rất cao)
- Mỗi lần tải xong feed sẽ append dữ liệu thu được để build giao diện
Triển khai cho blog
Tạo các section để hiển thị tiện ích
- Recent Post
<div class='featured-box-wrapper row'>
<b:section class='featured-box' id='featured-box' showaddelement='yes'>
<b:widget id='HTML777' locked='true' title='Recent Post' type='HTML' version='1'>
<b:widget-settings>
<b:widget-setting name='content'>
<![CDATA[<span data-type="recent" data-no="4"></span>]]></b:widget-setting>
</b:widget-settings>
<b:includable id='main'>
<b:if cond='data:title != ""'>
<h2 class='title'><data:title/></h2>
</b:if>
<div class='widget-content'>
<data:content/>
</div>
</b:includable>
</b:widget>
</b:section>
</div>
- Random post
<div class='featured-box-wrapper row'>
<b:section class='featured-box' id='featured-box2' showaddelement='yes'>
<b:widget id='HTML778' locked='true' title='Random Post' type='HTML' version='1'>
<b:widget-settings>
<b:widget-setting name='content'>
<![CDATA[<span data-type="random" data-no="8"></span>]]></b:widget-setting>
</b:widget-settings>
<b:includable id='main'>
<b:if cond='data:title != ""'>
<h2 class='title'><data:title/></h2>
</b:if>
<div class='widget-content'>
<data:content/>
</div>
</b:includable>
</b:widget>
</b:section>
</div>
Sơ sơ 1 ít css
.featured-box-wrapper,.featured-inner:nth-of-type(4n+5){clear:both}
.featured-inner{float:left;margin:0 15px 0 0;width:calc(95% / 4)}
.featured-thumb{width:100%;height:200px;overflow:hidden}
.featured-img{width:100%;height:100%;display:block}
.featured-title{margin:15px 0;height:44px;overflow:hidden}
.featured-title a{color:#000;cursor:pointer;font-weight:bold;text-decoration:none;font-size:17px}
.featured-thumb a:hover{-webkit-transform:scale(1.1) rotate(-1.5deg);-moz-transform:scale(1.1) rotate(-1.5deg);-ms-transform:scale(1.1) rotate(-1.5deg);-o-transform:scale(1.1) rotate(-1.5deg);transform:scale(1.1) rotate(-1.5deg);-webkit-transition:all .25s ease;-moz-transition:all .25s ease;-ms-transition:all .25s ease;-o-transition:all .25s ease;transition:all .25s ease}
.featured-meta{margin:0 0 10px;font-size:14px}
.featured-summary{margin-bottom:20px}
.featured-auth::before{font-family:fontawesome;content:'\f2c0';margin:0 5px 0 0}
.featured-time::before{font-family:fontawesome;content:'\f017';margin:0 5px 0 3px}
.featured-cmt a{color:#000;text-decoration:none}
.featured-cmt::before{font-family:fontawesome;content:'\f0e6';margin:0 5px 0 3px}
Script
<!-- Sử dụng ajax yêu cầu có thư viện jQuery-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">//<![CDATA[
var month_format = [, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"];
var text_cmt = "Bình luận";
$(".featured-box .HTML .widget-content").each(function() { // lặp qua từng widget
var a1 = $(this).find("span").attr("data-type"), // lấy kiểu hiển thị
a2 = $(this).find("span").attr("data-no"), // lấy số bài cần hiển thị để gán cho max-results
a3 = $(this).parent().attr("id"), // lấy id của widget mục đích để code có thể chạy khi tạo nhiều widget
totalPost; // khai báo biến để lấy tổng số bài đăng của blog phục vụ random post
// xử lý cho recent post
if (a1 == "recent") {
$.ajax({
url: "/feeds/posts/default",
type: "get",
data: {
alt: 'json',
'max-results': a2
},
dataType: "jsonp",
success: function(e) {
getData(e)
}
})
};
// xử lý cho random post
if (a1 == "random") {
// call ajax lấy tổng số bài đăng
$.ajax({
url: "/feeds/posts/default",
type: "get",
data: {
alt: 'json',
'max-results': 0 // tổng số bài đăng của blog không nằm trong node entry nên không cần tải thêm entry feed
},
dataType: "jsonp",
success: function(e) {
totalPost = e.feed.openSearch$totalResults.$t; // lấy tổng số bài đăng của blog
// đoạn này sẽ tạo 1 mảng gồm các giá trị từ 1 -> totalPost hay chính là các entry
// sau đó sử dụng hàm random để lấy 1 giá trị bất kì trong mảng cho mỗi lần random sao cho các giá trị này không được trùng nhau
var nums = [],
gen_nums = [];
for (var x = 0; x < totalPost; x++) {
nums.push(x + 1)
};
function in_array(array, el) {
for (var i = 0; i < array.length; i++)
if (array[i] == el) return true;
return false;
}
function get_rand(array) {
var rand = array[Math.floor(Math.random() * array.length)];
if (!in_array(gen_nums, rand)) {
gen_nums.push(rand);
return rand;
}
return get_rand(array);
}
// end
for (var v = 0; v < a2; v++) { // lặp từ 0 tới giá trị data-no
for (var w = 1; w <= 1; w++) { // lấy 1 giá trị trong list từ 1 -> totalPost đế set cho start-index
// console.log(get_rand(nums)) // đọc log kiểm tra xem start index có bị trùng không
$.ajax({
url: "/feeds/posts/default",
type: "get",
data: {
'start-index': get_rand(nums),
alt: 'json',
'max-results': 1
},
dataType: "jsonp",
success: function(e1) {
getData(e1)
}
})
}
}
}
})
}
// viết chung 1 function để xử lí dữ liệu cho cả recent post và random post khi get ajax thành công
function getData(e) {
var r = "";
for (var n = 0; n < e.feed.entry.length; n++) { // lặp qua từng entry để lấy dữ liệu
// lấy link bài viết
for (var s = 0; s < e.feed.entry[n].link.length; s++) {
if (e.feed.entry[n].link[s].rel === "alternate") {
t = e.feed.entry[n].link[s].href;
break
}
}
// lấy số nhận xét và link nhận xét
for (var d = 0; d < e.feed.entry[n].link.length; d++) {
if ("replies" === e.feed.entry[n].link[d].rel && "text/html" === e.feed.entry[n].link[d].type) {
i = e.feed.entry[n].link[d].title;
q = e.feed.entry[n].link[d].href;
break
}
}
i = parseInt(i, 10) // chỉ lấy số, bỏ text
// tạo rẽ nhánh để lấy đoạn trích ngắn áp dụng cho cả /default và /summary
if ("content" in e.feed.entry[n]) l = e.feed.entry[n].content.$t;
else if ("summary" in e.feed.entry[n]) l = e.feed.entry[n].summary.$t;
else l = "";
// loại bỏ kí tự đặc biệt và cắt chuỗi
if (l.length != 0) {
l = l.replace(/<\S[^>]*>/g, "");
if (l.length > 120) {
l2 = l.substring(0, 100);
l3 = l2.lastIndexOf(" ");
l = l2.substring(0, l3) + ' ...';
}
}
var f = e.feed.entry[n].title.$t, // lấy tiêu đề bài đăng
c = e.feed.entry[n].author[0].name.$t, // lấy tên tác giả
h = e.feed.entry[n].published.$t, // lấy thời gian đăng bài
o = h.substring(0, 4), // cắt chuỗi lấy năm
p = h.substring(5, 7), // cắt chuỗi lấy tháng
u = h.substring(8, 10), // cắt chuỗi lấy ngày
m = month_format[parseInt(p, 10)] + " " + u + ", " + o; // định dạng lại thời gian
// xử lý thumbnail và resize
if ("media$thumbnail" in e.feed.entry[n]) { // nếu bài viết có thumbnail
d = e.feed.entry[n].media$thumbnail.url.replace("s72-c", "s1600");
} else {
// nếu không có thì dùng ảnh thay thế
d = "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXvoeWlnXfuDDxD0LIWf1tODuI3Wti5c3N2v73T19xj9fNAM45q8OWvY4cL-rRcVe0NisWEk9lZ0v4uuDUX4b54sx0uFOgNBRC6Gfoj7nUJaCh-JsZLVa-G-Nt0j5OrQ5vOfcCLsG-HqI/s1600-r/nth.png"
}
// tạo div và truyền dữ liệu
r += '<div class="featured-inner"><div class="featured-thumb"><a class="featured-img" href="' + t + '" style="background:url(' + d + ') no-repeat center center;background-size: cover"></a></div><div class="featured-header"><h3 class="featured-title"><a href="' + t + '">' + f + '</a></h3><div class="featured-meta"><span class="featured-auth">' + c + '</span> <span class="featured-time">' + m + '</span> <span class="featured-cmt"><a href="' + q + '">' + i + " " + text_cmt + '</span></a></div><div class="featured-summary">' + l + '</div></div></div>'
}
$(".featured-box .HTML .widget-content").each(function() {
if ($(this).parent().attr("id") == a3) {
$(this).append(r)
} // kiểm tra nếu đúng id thì append mã html vào
})
}
});
//]]></script>
Bạn đọc kĩ phần comment để hiểu code chạy như nào nhé.
Nói chung tôi rất ngại tải feed, tuy nhiên lượng dữ liệu nó mang lại và các tiện ích có thể xây dựng từ nó là điều không thể phủ nhận. Hi vọng qua bài viết này bạn sẽ hiểu thêm được việc build các tiện ích từ blogger feed api
Good luck !
1. Không vi phạm luật pháp nước CHXHCN Việt Nam
2. Không vi phạm thuần phong mỹ tục Việt Nam
3. Không bàn luận vấn đề liên quan đến tôn giáo, chính trị
4. Không đả kích, chửi bới hay đưa ra những lời nói không phù hợp với mục tiêu của website
5. Không bình luận với mục đích quảng cáo, trao đổi, mua bán
6. Khuyến khích sử dụng Tiếng Việt có dấu, hạn chế sử dụng tiếng lóng, viết tắt
7. Khi cần sự trợ giúp, vui lòng miêu tả chi tiết lỗi và để lại link đính kèm, tránh nói chung chung gây mất thời gian cho đôi bên
Bủi tối vv nhê a