Selasa, 11 Juni 2013
Performa Widget Random Post
Saya harus mengatakan bahwa widget random post itu sangat buruk dalam hal performa. Kerja mereka sangat lambat dan membuang-buang tenaga, karena mereka bekerja dengan cara memanggil feed posting berukuran sangat besar! Kemudian mereka hanya akan menampilkannya sebagian saja secara acak:
<script src='/feeds/posts/default?alt=json-in-script&max-results=99999&callback=randomPosts'></script>Bagi Anda para pemakai mungkin tidak pernah tahu mengenai ini, tetapi sebenarnya setiap pemakai widget random post —termasuk juga widget artikel terkait— tanpa sadar telah menerima beban begitu besar. Ketika Anda membuka halaman tunggal dimana terdapat widget random post di dalamnya, pada saat yang bersamaan Anda juga sebenarnya sedang membuka semua posting yang telah Anda terbitkan dalam satu waktu. Mengapa? Karena parameter max-results pada feed menunjukkan angka 99999 yang artinya bahwa semua feed posting akan “diusahakan” untuk dipanggil (diusahakan, karena jumlah posting yang kita terbitkan tidak mungkin mencapai angka sebesar itu).
Mengacak Indeks Permulaan
Saya menyadari terdapat sebuah peluang untuk menciptakan widget random post yang lebih ringan dan cepat dimuat. Dibandingkan memuat semua posting dan memilah beberapa posting secara acak untuk ditampilkan, akan lebih baik jika kita mengacak nilai start-index pada feed sehingga posting yang dimuat bisa dimulai dari urutan sembarang:
function randomPosts(json) {
// Ubah JSON menjadi HTML...
}
var startIndex = Math.round(Math.random() * 9999); // Membuat angka acak
document.write('<scr' + 'ipt src="/feeds/posts/summary?alt=json-in-script&start-index=' + startIndex + '&max-results=7&callback=randomPosts"></scr' + 'ipt>');Ada satu masalah kecil yang mungkin akan mengganggu. Jika angka acak yang tercipta nilainya lebih besar dari total posting blog Anda saat ini, maka widget random post ini tidak akan bekerja. Oleh karena itu kita harus membatasi angka acak yang tercipta, yaitu tidak boleh kurang dari 1 dan tidak boleh lebih dari “total posting dikurangi jumlah posting yang ingin ditampilkan”.
Untuk mendapatkan jumlah posting secara keseluruhan kita bisa mengambilnya melalui objek json.feed.openSearch$totalResults.$t:
// Konfigurasi
var homePage = 'http://nama_blog.blogspot.com',
maxResults = 7;
// Fungsi untuk menggenerasikan angka acak dengan batasan minimal dan maksimal
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Mendapatkan indeks pemulai yang aman untuk memanggil feed utama
function createRandomPostsStartIndex(json) {
// Buat angka acak dengan nilai tidak boleh kurang dari `1` dan tidak boleh lebih dari `total posting - posting yang ingin ditampilkan`
var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));
// Tampilkan pesan log
console.log('Get the post feed start from ' + startIndex + ' until ' + (startIndex + maxResults));
}
document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&max-results=0&callback=createRandomPostsStartIndex"></scr' + 'ipt>');Muat ulang widget Anda berkali-kali. Seharusnya Anda akan melihat pesan log indeks pemanggilan posting yang berubah-ubah seperti ini:

Kita akan menggunakan angka acak itu sebagai angka pemulai pada parameter start-index seperti ini:
function createRandomPostsStartIndex(json) {
var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));
document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&orderby=updated&start-index=' + startIndex + '&max-results=' + maxResults + '&callback=randomPosts"></scr' + 'ipt>');
}Pada bagian akhir URL feed terdapat parameter callback=randomPosts. randomPosts di sini merupakan fungsi utama yang akan kita buat untuk mengubah JSON Blogger menjadi widget:
function randomPosts(json) {
var link, ct = document.getElementById('random-post-container'),
entry = json.feed.entry,
skeleton = "<ul>";
for (var i = 0, len = entry.length; i < len; i++) {
for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
link = (entry[i].link[j].rel == "alternate") ? entry[i].link[j].href : '#';
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
ct.innerHTML = skeleton + '</ul>';
}Sentuhan akhir, acak urutan posting yang ditampilkan menggunakan fungsi ini sehingga daftar posting yang ditampilkan nantinya akan semakin acak urutannya:
// Fungsi untuk mengacak array
function shuffleArray(arr) {
var i = arr.length, j, temp;
if (i === 0) return false;
while (--i) {
j = Math.floor(Math.random() * (i + 1));
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}
// Widget
function randomPosts(json) {
var link, ct = document.getElementById('random-post-container'),
entry = shuffleArray(json.feed.entry),
skeleton = "<ul>";
for (var i = 0, len = entry.length; i < len; i++) {
for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
link = (entry[i].link[j].rel == "alternate") ? entry[i].link[j].href : '#';
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
ct.innerHTML = skeleton + '</ul>';
}Hasil Akhir
<div id='random-post-container'>Memuat...</div>
<script>
//<![CDATA[
// Feed configuration
var homePage = 'http://nama_blog.blogspot.com',
maxResults = 7,
containerId = 'random-post-container';
// Function to generate random number limited from `min` to `max`
// Used to create a valid and safe random feed `start-index`
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Function to shuffle arrays
// Used to randomize order of the generated JSON feed
function shuffleArray(arr) {
var i = arr.length, j, temp;
if (i === 0) return false;
while (--i) {
j = Math.floor(Math.random() * (i + 1));
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}
// Get a random start index
function createRandomPostsStartIndex(json) {
var startIndex = getRandomInt(1, (json.feed.openSearch$totalResults.$t - maxResults));
// console.log('Get the post feed start from ' + startIndex + ' until ' + (startIndex + maxResults));
document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&orderby=updated&start-index=' + startIndex + '&max-results=' + maxResults + '&callback=randomPosts"></scr' + 'ipt>');
}
// Widget's main function
function randomPosts(json) {
var link, ct = document.getElementById(containerId),
entry = shuffleArray(json.feed.entry),
skeleton = "<ul>";
for (var i = 0, len = entry.length; i < len; i++) {
for (var j = 0, jen = entry[i].link.length; j < jen; j++) {
link = (entry[i].link[j].rel == "alternate") ? entry[i].link[j].href : '#';
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
ct.innerHTML = skeleton + '</ul>';
}
document.write('<scr' + 'ipt src="' + homePage + '/feeds/posts/summary?alt=json-in-script&max-results=0&callback=createRandomPostsStartIndex"></scr' + 'ipt>');
//]]>
</script>Ketika Posting Acak Dikirim Secara Langsung oleh Server
Dalam sebuah forum Saya pernah menemukan seseorang yang mencoba menyarankan Blogger untuk menyertakakan parameter orderby baru berupa random dan popular. Saya pikir ini adalah ide yang bagus. Karena berdasarkan dokumentasi, kalau tidak salah hanya ada dua macam nilai untuk parameter orderby, yaitu published dan updated:
http://nama_blog.blogspot.com/feeds/posts/summary?orderby=published
http://nama_blog.blogspot.com/feeds/posts/summary?orderby=updated
Jika suatu saat Blogger merealisasikan masukan dari pengguna ini, maka kita tidak perlu lagi menerapkan hack semacam ini. Cukup dengan membuat widget recent post biasa, kita bisa mengubah parameter URL feed dari orderby=published menjadi orderby=random untuk menciptakan widget random post. Tapi kasus ini masih dalam tahap “seandainya”.
Label: JavaScript, Pemecahan Masalah, Posting, Tingkat Lanjut, Widget
Selasa, 02 April 2013
Widget Blogger untuk Menampilkan Daftar Posting Terbaru dalam Label-Label yang Spesifik
Judulnya sangat panjang karena Saya sendiri tidak tahu harus memberi nama apa kepada widget ini. Pada intinya widget ini adalah widget recent post biasa, namun memiliki kemampuan untuk menggandakan diri agar bisa menampilkan daftar posting terbaru lebih dari satu buah berdasarkan label yang spesifik:
Pada awalnya Saya membuat widget ini agar bisa menampilkan posting-posting terbaru dari blog tunggal saja, namun mengingat ada juga beberapa blog yang suka menampilkan daftar posting dari blog lain, jadi sekarang widget ini tidak hanya berfungsi untuk menampilkan daftar posting dari satu feed blog, melainkan juga bisa menampilkan daftar posting dari feed blog Blogger yang lain.
Pemasangan Widget
Masuk ke halaman Tata Letak kemudian tambahkan sebuah widget HTML/JavaScript. Salin kode ini dan letakkan di dalam formulirnya:
<link rel="stylesheet" type="text/css" scoped="scoped" href="http://blogger-json-experiment.googlecode.com/svn/resources/multi-feed/multi-feed.min.css"/>
<style type="text/css" scoped="scoped">
/* Custom CSS */
.list-entries {
margin:5px;
width:270px; /* Lebar widget */
float:left;
font-size:11px;
}
</style>
<div id="feed-list-container"></div>
<div style="clear:both;"></div>
<script type="text/javascript">
var multiFeed = {
feedsUri: [
{
name: "Judul Widget 1",
url: "http://nama_blog-1.blogspot.com",
tag: "JQuery"
},
{
name: "Judul Widget 2",
url: "http://nama_blog-2.blogspot.com",
tag: "CSS"
},
{
name: "Judul Widget 1",
url: "http://nama_blog-3.blogspot.com",
tag: "Widget"
}
],
numPost: 4,
showThumbnail: true,
showSummary: true,
summaryLength: 80,
titleLength: "auto",
thumbSize: 72,
containerId: "feed-list-container",
readMore: {
text: "Selengkapnya",
endParam: "?max-results=20"
}
};
</script>
<script type="text/javascript" src="http://blogger-json-experiment.googlecode.com/svn/resources/multi-feed/multi-feed.min.js"></script>Konfigurasi
| Opsi | Keterangan |
|---|---|
| feedsUri | Berupa array objek dimana di dalamnya terdapat beberapa data berupa name, url dan tag: name digunakan untuk menentukan judul widget, url digunakan untuk menentukan URL feed, tag digunakan untuk menentukan tag/label posting yang ingin ditampilkan. |
| numPost | Digunakan untuk menentukan jumlah posting yang ingin ditampilkan |
| showThumbnail | Ganti nilainya menjadi false untuk menyembunyikan gambar posting |
| showSummary | Ganti nilainya menjadi false untuk menyembunyikan ringkasan posting |
| summaryLength | Digunakan untuk menentukan jumlah karakter ringkasan posting |
| titleLength | Digunakan untuk memotong karakter judul posting yang terlalu panjang. Ganti nilainya menjadi angka untuk memotong karakter sebanyak angka tersebut, atau set sebagai "auto" untuk membiarkan judul posting sebagai apa adanya |
| thumbSize | Digunakan untuk menentukan ukuran thumbnail posting |
| containerId | Digunakan untuk menentukan ID kontainer yang akan digunakan untuk memuat widget |
| readMore | Terdiri dari text dan endParam. text digunakan untuk menentukan label tautan “Baca Selengkapnya” yang terletak di bawah masing-masing widget dan endParam digunakan untuk menentukan parameter akhir tautan tersebut (abaikan jika tidak perlu). |
Opsi feedsUri merupakan array objek dimana setiap objek akan mewakili masing-masing widget:
// Tiga objek = tiga widget
feedsUri: [{}, {}, {}]Setiap objek berisi beberapa data yang digunakan untuk menentukan judul widget, sumber URL dan tag/kategori posting spesifik:
feedsUri: [
{
name: "Judul Widget 1",
url: "http://nama_blog-1.blogspot.com",
tag: "Blogger"
},
{
name: "Judul Widget 2",
url: "http://nama_blog-2.blogspot.com",
tag: "WordPress"
},
{
name: "Judul Widget 3",
url: "http://nama_blog-3.blogspot.com",
tag: "SEO"
}
]Setelah semua konfigurasi sudah diatur dengan benar, klik Simpan Widget. Modifikasi tampilan bisa dilakukan di dalam tag <style> yang Saya tambahkan pada widget di atas.
Kode Sumber
Label: JavaScript, JSON Blogger, Posting, Sidebar, Widget
Senin, 18 Maret 2013
Plugin Feed Rotator untuk Blogger
Plugin ini pada dasarnya merupakan perpaduan antara Basic JQuery Image Rotator dengan JSON Blogger yang kemudian Saya jadikan sebagai semacam slideshow dengan konten yang akan diperbaharui secara otomatis berdasarkan posting yang sudah terbit. Saya memutuskan untuk membuat ini karena ada beberapa orang yang menanyakan melalui email mengenai bagaimana cara memadukan plugin Image Rotator dengan JSON Blogger.
Plugin JQuery Image Rotator ini juga sudah dimodifikasi hampir secara keseluruhan, khusus untuk diaplikasikan pada widget ini. Beberapa hal yang Saya tambahkan diantaranya adalah navigasi, efek animasi kontainer dan dukungan fungsi callback yang dapat dijalankan pada sesi-sesi tertentu.
Implementasi Dasar
Berikut ini adalah contoh implementasi paling mendasar yang bisa Anda lakukan untuk mengaktifkan plugin:
<link rel="stylesheet" type="text/css" href="http://blogger-json-experiment.googlecode.com/svn/resources/blogger-feed-rotator-plugin/default-style.min.css"/>
<div id="slider-rotator" class="slider-rotator loading"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://blogger-json-experiment.googlecode.com/svn/resources/blogger-feed-rotator-plugin/blogger-feed-rotator.min.js"></script>
<script type="text/javascript">
makeSlider({
url: "http://nama_blog.blogspot.com" // URL blog Anda
});
</script>Susun file CSS dan plugin Blogger Feed Rotator sesuai dengan urutan yang Saya perlihatkan di atas. Tambahkan juga elemen HTML yang berfungsi untuk menampilkan slideshow sebelum plugin. Setelah itu aktifkan fungsi makeSlider() dengan parameter berupa objek tunggal url yang diisi dengan URL blog Anda. Salin dan tempelkan di dalam formulir elemen halaman HTML/JavaScript. Dengan ini maka Anda sudah bisa menampilkan sebuah slideshow sederhana seperti yang terlihat pada halaman demo di atas.
Daftar Opsi Konfigurasi Dasar
Konfigurasi dasar Saya batasi pada opsi-opsi yang berhubungan dengan pengaturan posting:
{
url: "http://www.dte.web.id", // Your blog URL
numPost: 5, // Number of posts to display
newTabLink: false, // `true` to automatically open link in new window tab
labelName: null, // Show posts in specific label. Specify name of the post label, or `null` to show all posts
showDetail: true, // `false` to hide the blog post title & description
summaryLength: 60, // Length of post summary
titleLength: "auto", // Length of the post title. "auto" by default, or specify number to crop the post title characters and truncate it with `...`
showThumb: true, // `false` to hide the post thumbnails
thumbWidth: 250, // Width of post thumbnail in pixels
squareThumb: true, // Thumbnail mode: Square mode or use the scaled thumbnail with original width and height ratio
noThumb: "images/grey.png", // Fallback thumbnail for no picture posts
showNav: true, // `true` to show both next/prev navigation and navigation number, "next/prev" to show next/prev navigation only, "number" to show navigation number only, `false` to hide the navigation
navText: {
prev: "<", // Label of the previous navigation
next: ">" // Label of the next navigation
},
containerId: "slider-rotator" // HTML element ID that is used to display the slideshow/rotator
}| Opsi | Keterangan |
|---|---|
| url | Isi dengan URL halaman muka blog Anda |
| numPost | Digunakan untuk menentukan jumlah posting yang akan ditampilkan |
| newTabLink | Ubah nilainya menjadi true untuk membuat tautan-tautan di dalam widget ini terbuka di jendela/tab baru saat diklik |
| labelName | Digunakan untuk menampilkan daftar posting dengan label/kategori tertentu. Nilai null berarti menampilkan semua posting terbaru. Ganti nilainya menjadi nama label untuk menampilkan posting dengan label tertentu. Misalnya labelName:"Foto" untuk menampilkan semua posting yang memiliki label Foto - Contoh |
| showDetail | Ubah nilainya menjadi false untuk menyembunyikan judul dan ringkasan posting. Berguna untuk membuat slideshow foto/gambar tanpa tulisan - Contoh |
| summaryLength | Digunakan untuk membatasi panjang ringkasan posting. Akhir pemotongan ringkasan posting akan digantikan dengan simbol “…” |
| titleLength | Digunakan untuk membatasi panjang judul posting. Secara normal bernilai "auto". Ubah nilainya dengan angka tertentu untuk memotong panjang judul jika Anda merasa judul posting yang tampil terlalu panjang. Akhir pemotongan judul posting akan digantikan dengan simbol “…” |
| showThumb | Ubah nilainya menjadi false untuk menyembunyikan gambar posting. Berguna untuk membuat slideshow teks - Contoh |
| thumbWidth | Digunakan untuk menentukan lebar thumbnail posting dan juga sekaligus menentukan lebar slideshow |
| squareThumb | Opsi untuk memilih mode thumbnail. true untuk menampilkan gambar dengan resolusi /sN-c, false untuk menampilkan gambar dengan resolusi /sN |
| noThumb | Isi dengan URL gambar. Gambar ini akan digunakan sebagai cadangan jika posting yang tampil tidak memiliki gambar |
| showNav | Opsi untuk menampilkan atau menyembunyikan navigasi slideshow. Ubah nilainya menjadi false untuk menyembunyikan navigasi. "number" untuk menampilkan navigasi angka saja. "next/prev" untuk menampilkan navigasi next/prev saja. true untuk menampilkan keduanya |
| navText | Opsi untuk menentukan label navigasi Next dan Previous |
| containerId | Digunakan untuk menentukan di mana slideshow akan ditampilkan. Isi dengan ID elemen HTML. Dan jangan lupa untuk menyertakan elemen HTML tersebut sebelum eksekusi plugin. |
Pengaturan Animasi
Pengaturan animasi Saya batasi pada opsi-opsi yang berhubungan dengan animasi plugin:
{
interval: 6000, // Slideshow interval
speed: 1000, // Slideshow animation speed
hoverPause: true, // `false` to make the slideshow/rotator keep running on mouse-over
crossFade: false, // `true` if you want to make the slide change effect occurs simultaneously between disappearance and appearance
autoHeight: false, // Animate the rotator to adjust the height of the displayed item
autoSlide: true // `false` to run the slideshow manually (with navigation)
}| Opsi | Keterangan |
|---|---|
| interval | Digunakan untuk menentukan rentang waktu perpindahan setiap item slide dalam satuan milidetik |
| speed | Digunakan untuk menentukan kecepatan animasi dalam satuan milidetik |
| hoverPause | Jika bernilai true maka slideshow akan berhenti bergerak saat pointer mouse berada di atasnya |
| crossFade | Digunakan untuk menentukan apakah efek penghilangan dan pemunculan slide dilakukan secara bergantian atau bersamaan dalam waktu yang sama tanpa ada jeda |
| autoHeight | Jika bernilai true, maka tinggi slideshow akan menyesuaikan diri berdasarkan tinggi item slide yang tampil dengan efek animasi - Contoh |
| autoSlide | Ubah nilainya menjadi false untuk membuat slideshow berjalan secara manual melalui aksi klik navigasi |
Konfigurasi Lanjutan
Bagian ini Saya khususkan untuk menjelaskan fungsi-fungsi callback dan implementasi lanjutan, khususnya dalam hal modifikasi:
{
onInit: function() {}, // Callback function that will be executed when slideshow is starting
onHide: function(index) {}, // Callback function that will be executed when slideshow is start hiding the slide item
onShow: function(index) {} // Callback function that will be executed after the slideshow showing the slide item
}onInit
Fungsi ini akan dijalankan pada saat pertama kali slideshow terbentuk. Saat slideshow tampil, maka fungsi ini akan langsung dieksekusi. Contoh penerapan:
makeSlider({
url: "http://nama_blog.blogspot.com",
onInit: function() {
alert('Slideshow ready!');
}
});Kode di atas akan menampilkan pesan “Slideshow ready!” segera setelah slideshow tampil.
onHide
Fungsi ini akan dijalankan setiap kali item slide mulai menghilang. Saat item slide menghilang, maka fungsi ini akan tereksekusi. Contoh penerapan:
makeSlider({
url: "http://nama_blog.blogspot.com",
onHide: function() {
alert('Mengganti slide...');
}
});Kode di atas akan menampilkan pesan “Mengganti slide...” setiap kali slide mulai berganti.
onShow
Fungsi ini akan dijalankan setiap kali item slide tampil. Saat item slide tampil, maka fungsi ini akan tereksekusi. Contoh penerapan:
makeSlider({
url: "http://nama_blog.blogspot.com",
onShow: function(index) {
alert('Slide ke ' + index + ' berhasil tampil.');
}
});Kode di atas akan menampilkan pesan “Slide ke {nomor urut slide yang tampil} berhasil tampil.” setiap kali slide ditampilkan.
Parameter `index`
Parameter index hanya berlaku untuk onHide dan onShow. Parameter ini akan menampilkan indeks slide yang aktif:
makeSlider({
url: "http://nama_blog.blogspot.com",
onInit: function() {
console.log('Slideshow dimulai...');
},
onHide: function(index) {
console.log('Menyembunyikan slide ke ' + index);
},
onShow: function(index) {
console.log('Menampilkan slide ke ' + index);
}
});Beberapa Contoh Modifikasi Dasar
Mengubah Tampilan
Dengan mengubah ukuran lebar pada gambar dan slideshow, ukuran tinggi pada slideshow dan mengubah tampilan deskripsi/detail posting sebagai caption, maka Anda bisa semakin memperkuat status plugin ini sebagai “slideshow”:
CSS
<link rel="stylesheet" type="text/css" href="http://blogger-json-experiment.googlecode.com/svn/resources/blogger-feed-rotator-plugin/default-style.min.css"/>
<style type="text/css">
/* Custom CSS... */
.slider-rotator {
width:600px;
height:240px;
padding:0 0;
background-color:white;
font:italic normal 12px/1.4 Georgia,Serif;
color:white;
border:1px solid black;
}
.slider-rotator .slider-item {
background-color:white;
height:240px; /* Same with `.slider-rotator` height */
padding:0 0;
}
.slider-rotator h4 {margin-top:0}
.slider-rotator p {margin:2px 0 0}
/* Slider details as caption */
.slider-rotator .detail-wrapper {
position:absolute;
top:auto;
right:0;
bottom:0;
left:0;
background-color:black;
background-color:rgba(0,0,0,.9);
padding:.5em 1em;
z-index:4;
}
</style>JavaScript
makeSlider({
url: "http://nama_blog.blogspot.com", // Your blog URL
thumbWidth: 600 // Thumbnail width in pixels (same with slideshow width)
});Memanfaatkan Fungsi Callback untuk Menganimasikan Caption
Dengan memanfaatkan parameter index pada onHide dan onShow, Anda bisa memberikan efek animasi pada caption slide yang aktif setiap kali slide berganti:
makeSlider({
url: "http://nama_blog.blogspot.com", // Your blog URL
thumbWidth: 600, // Thumbnail width in pixels (same with slideshow width)
containerId: "slider-rotator",
// Hide all captions on initiation...
onInit: function() {
$('#' + this.containerId).find('.detail-wrapper').hide();
},
// Hide the caption with `.slideUp()` effect when the slide item hides
onHide: function() {
$('#' + this.containerId).find('.detail-wrapper').slideUp();
},
// Show the caption with `.slideDown()` effect when the slide item appears
onShow: function(index) {
$('#' + this.containerId).children().eq(index).find('.detail-wrapper').slideDown();
}
});Menampilkan Beberapa Slideshow Sekaligus
Karena eksekusi plugin ini dilakukan di luar, maka ini memungkinkan Anda untuk menampilkan beberapa slideshow sekaligus dalam satu halaman. Pertama-tama buatlah beberapa elemen HTML yang dibutuhkan untuk memuat slideshow:
<div id="container-1" class="slider-rotator loading"></div>
<div id="container-2" class="slider-rotator loading"></div>
<div id="container-3" class="slider-rotator loading"></div>Kemudian jalankan fungsi makeSlider() sebanyak jumlah kontainer yang dibuat. Tentukan juga konfigurasi containerId yang berbeda-beda berdasarkan ID kontainer:
makeSlider({
url: "http://nama_blog-1.blogspot.com",
containerId: "container-1"
});
makeSlider({
url: "http://nama_blog-2.blogspot.com",
containerId: "container-2"
});
makeSlider({
url: "http://nama_blog-3.blogspot.com",
containerId: "container-3"
});Mengatur Plugin dan Melihat Demo Secara Langsung
Saya sudah menyertakan halaman konfigurasi plugin yang bisa Anda gunakan untuk melakukan pengaturan dan melihat hasilnya secara langsung di sini:
Daftar Contoh
- Demo 1: Basic
- Demo 2: Show Posts under Category “Widget”
- Demo 3: About Thumbnails
- Demo 4: Thumbnail Only
- Demo 5: Text Only
- Demo 6: Multiple Feed Loading
- Demo 7: Casual Slideshow
- Demo 8: Example Callback Function to Animate the Slideshow Caption (1)
- Demo 9: Example Callback Function to Animate the Slideshow Caption (2)
Label: Gambar, JSON Blogger, Slideshow, Tingkat Lanjut, Widget
Selasa, 12 Februari 2013
Modifikasi Widget Posting Populer Menjadi Berwarna-Warni
Pada konfigurasi widget Posting Populer, pastikan bahwa Anda memilih model widget berupa thumbnail dan judul. Setelah itu masuk ke editor HTML template. Salin kode CSS ini dan letakkan di atas kode ]]></b:skin> atau </style>:
/* Custom CSS for Blogger Popular Post Widget */
.PopularPosts ul,
.PopularPosts li,
.PopularPosts li img,
.PopularPosts li a,
.PopularPosts li a img {
margin:0 0;
padding:0 0;
list-style:none;
border:none;
background:none;
outline:none;
}
.PopularPosts ul {
margin:.5em 0;
list-style:none;
font:normal normal 13px/1.4 "Arial Narrow",Arial,Sans-Serif;
color:black;
counter-reset:num;
}
.PopularPosts ul li img {
display:block;
margin:0 .5em 0 0;
width:50px;
height:50px;
float:left;
}
.PopularPosts ul li {
background-color:#eee;
margin:0 10% .4em 0;
padding:.5em 1.5em .5em .5em;
counter-increment:num;
position:relative;
}
.PopularPosts ul li:before,
.PopularPosts ul li .item-title a {
font-weight:bold;
font-size:120%;
color:inherit;
text-decoration:none;
}
.PopularPosts ul li:before {
content:counter(num);
display:block;
position:absolute;
background-color:black;
color:white;
width:30px;
height:30px;
line-height:30px;
text-align:center;
top:50%;
right:-10px;
margin-top:-15px;
-webkit-border-radius:30px;
-moz-border-radius:30px;
border-radius:30px;
}
/* Set color & level */
.PopularPosts ul li:nth-child(1) {background-color:#E11E28;margin-right:1%}
.PopularPosts ul li:nth-child(2) {background-color:#FD3C03;margin-right:2%}
.PopularPosts ul li:nth-child(3) {background-color:#FECB09;margin-right:3%}
.PopularPosts ul li:nth-child(4) {background-color:#6EBE27;margin-right:4%}
.PopularPosts ul li:nth-child(5) {background-color:#149A48;margin-right:5%}
.PopularPosts ul li:nth-child(6) {background-color:#5BBFF1;margin-right:6%}
.PopularPosts ul li:nth-child(7) {background-color:#61469C;margin-right:7%}
.PopularPosts ul li:nth-child(8) {background-color:#863E86;margin-right:8%}
.PopularPosts ul li:nth-child(9) {background-color:#863E62;margin-right:9%}
.PopularPosts ul li:nth-child(10) {background-color:#815540;margin-right:10%}Klik Simpan Template. Begitu saja.
Label: CSS, Dasar, Desain Tampilan, Widget
Jumat, 16 November 2012
Kotak Penelusuran Blogger dengan Ajax JQuery

Beberapa waktu yang lalu Saya pernah menuliskan cara menampilkan hasil penelusuran dengan JSON Blogger tanpa harus berpindah dari halaman awal menuju halaman hasil penelusuran dengan JavaScript (Anda bisa membacanya di sini).
Melalui JQuery $.ajax() kita bisa meniadakan penyisipan script callback dan langsung memanggil JSON dengan cara seperti ini:
$('#search-form').on("submit", function() {
$.ajax({
url: '../feeds/posts/summary?alt=json-in-script&q=KATA_KUNCI',
type: 'get',
dataType: 'jsonp',
success: function(json) {
...
}
});
});Sehingga jika dijabarkan akan menjadi seperti ini:
HTML Formulir
<form action="/search" id="ajax-search-form">
<input type="text" name="q">
<input type="submit" value="Search">
</form>JQuery
(function($) {
var $form = $('#ajax-search-form'),
$input = $form.find(':text');
$form.append('<div id="search-result"></div>');
var $result_container = $('#search-result');
$form.on("submit", function() {
var keyword = $input.val();
$result_container.show().html('Loading...');
$.ajax({
url: 'http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&q=' + keyword + '&max-results=9999',
type: 'get',
dataType: 'jsonp',
success: function(json) {
var entry = json.feed.entry,
link, skeleton = "";
if (entry !== undefined) {
skeleton = '<h4>Search results for keyword "' + keyword + '"</h4>';
skeleton += '<a class="close" href="/">×</a><ol>';
for (var i = 0; i < entry.length; i++) {
for (var j = 0; j < entry[i].link.length; j++) {
if (entry[i].link[j].rel == "alternate") {
link = entry[i].link[j].href;
}
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
skeleton += '</ol>';
$result_container.html(skeleton);
} else {
$result_container.html('<a class="close" href="/">×</a><strong>No result!</strong>');
}
},
error: function() {
$result_container.html('<a class="close" href="/">×</a><strong>Error loading feed.</strong>');
}
});
return false;
});
$form.on("click", ".close", function() {
$result_container.fadeOut();
return false;
});
})(jQuery);Cara Kerja
Pertama-tama kita tangkap beberapa elemen penting yaitu formulir pencarian dan elemen input kata kunci pencarian:
var $form = $('#ajax-search-form'), // Mendapatkan elemen formulir
$input = $form.find(':text'); // Mendapatkan elemen input bertipe teks (penampung kata kunci pencarian)Sisipkan sebuah elemen HTML secara tidak langsung sebagai kontainer hasil pencaran:
$form.append('<div id="search-result"></div>');
var $result_container = $('#search-result');Setelah itu kita berlakukan event .submit() atau .on("submit") pada formulir untuk kemudian kita bisa langsung memproses data JSON yang akan ditransfer pada saat yang bersamaan ketika kita menekan tombol Enter pada papan ketik atau mengeklik tombol penelusuran di dalam formulir:
$form.on("submit", function() {
$.ajax(url, type, dataType, success, error); // Dapatkan data dan proses data di sini...
return false; // <= Ini digunakan untuk mencegah formulir membawa kita menuju halaman hasil penelusuran saat kita men-submit kata kunci pencarian
});Pengambilan data JSON dilakukan oleh JQuery $.ajax(), sehingga kita tidak perlu menyisipkan script callback ke dalam area <head> seperti dalam metode JavaScript mentah pada umumnya:
$.ajax({
url: 'http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&q=' + keyword + '&max-results=9999',
type: 'get',
dataType: 'jsonp',
success: function(json) {
var entry = json.feed.entry,
link, skeleton = "";
if (entry !== undefined) {
skeleton = '<h4>Search results for keyword "' + keyword + '"</h4>';
skeleton += '<a class="close" href="/">×</a><ol>';
for (var i = 0; i < entry.length; i++) {
for (var j = 0; j < entry[i].link.length; j++) {
if (entry[i].link[j].rel == "alternate") {
link = entry[i].link[j].href;
}
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
skeleton += '</ol>';
$result_container.html(skeleton);
} else {
$result_container.html('<a class="close" href="/">×</a><strong>No result!</strong>');
}
},
error: function() {
$result_container.html('<a class="close" href="/">×</a><strong>Error loading feed.</strong>');
}
});keyword adalah variabel. Nilainya diambil dari elemen input teks:
var keyword = $input.val();
// `../feeds/posts/summary?alt=json-in-script&q=keyword&max-results=9999`Integrasi Widget ke Blogger
Widget ini hanya akan bekerja jika blog Anda sudah dilengkapi dengan JQuery.
Pertama-tama masuk ke halaman Tata Letak, kemudian tambahkan sebuah elemen halaman HTML/JavaScript. Salin kode ini dan letakkan di dalam formulirnya:
<style type="text/css" scoped="scoped">
#ajax-search-form {
position:relative;
font:normal normal 13px/normal Arial,Sans-Serif;
}
#ajax-search-form a {
color:#741F27;
text-decoration:none;
}
#ajax-search-form input {
border:1px solid #ccc;
border-top-color:#999;
background-color:white;
font:inherit;
color:black;
margin:0 0;
padding:5px 5px;
width:180px;
}
#ajax-search-form input::-moz-focus-inner {
margin:0;
padding:0;
border:none;
outline:none;
}
#ajax-search-form input[type="submit"] {
width:auto;
background-color:#084670;
border-color:transparent;
color:#B4D8F0;
font-weight:bold;
cursor:pointer;
padding-left:7px;
padding-right:7px;
}
#ajax-search-form input[type="submit"]:hover,
#ajax-search-form input[type="submit"]:focus {background-color:#083E5F}
#search-result {
border:1px solid #bbb;
background-color:white;
padding:10px 15px;
margin:2px 0;
width:auto;
height:auto;
position:absolute;
top:100%;
left:0;
z-index:99;
-webkit-box-shadow:0 1px 3px rgba(0,0,0,.4);
-moz-box-shadow:0 1px 3px rgba(0,0,0,.4);
box-shadow:0 1px 3px rgba(0,0,0,.4);
display:none;
}
#search-result ol,
#search-result li,
#search-result h4 {
margin:0;
padding:0;
}
#search-result h4,
#search-result strong {
display:block;
margin:0 30px 10px 0;
}
#search-result ol {margin:0 0 10px 28px}
#search-result ol a:hover {text-decoration:underline}
#search-result .close {
display:block;
position:absolute;
top:6px;
right:10px;
line-height:normal;
color:#17950F;
}
#search-result strong {color:#B75252}
</style>
<form action="/search" id="ajax-search-form">
<input type="text" name="q" />
<input type="submit" value="Search" />
</form>
<script type="text/javascript">
(function($) {
var $form = $('#ajax-search-form'),
$input = $form.find(':text');
// Append a search result container to the search form
$form.append('<div id="search-result"></div>');
var $result_container = $('#search-result');
// When the keyword is submitted...
$form.on("submit", function() {
// Get the input value
var keyword = $input.val();
// Show the search result container and insert a `Loading...` text
$result_container.show().html('Loading...');
// Get the blog JSON via $.ajax() to show the search result
// The URL format: http://blog_name.blogspot.com/feeds/posts/summary?alt=json-in-script&q={THE_KEYWORD}&max-results=9999
$.ajax({
url: 'http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&q=' + keyword + '&max-results=9999',
type: 'get',
dataType: 'jsonp',
// If success, grab the search result list...
success: function(json) {
var entry = json.feed.entry,
link, skeleton = "";
if (entry !== undefined) {
skeleton = '<h4>Search results for keyword "' + keyword + '"</h4>';
skeleton += '<a class="close" href="/">×</a><ol>';
for (var i = 0; i < entry.length; i++) {
for (var j = 0; j < entry[i].link.length; j++) {
if (entry[i].link[j].rel == "alternate") {
link = entry[i].link[j].href;
}
}
skeleton += '<li><a href="' + link + '">' + entry[i].title.$t + '</a></li>';
}
skeleton += '</ol>';
$result_container.html(skeleton);
} else {
// If the JSON is empty ... (entry === undefined)
// Show the `not found` or `no result` message
$result_container.html('<a class="close" href="/">×</a><strong>No result!</strong>');
}
},
error: function() {
// If error, show an error message
$result_container.html('<a class="close" href="/">×</a><strong>Error loading feed.</strong>');
}
});
return false;
});
// Fade out the search result container if the close button is clicked
$form.on("click", ".close", function() {
$result_container.fadeOut();
return false;
});
})(jQuery);
</script>Ganti kode yang Saya beri tanda dengan URL blog Anda kemudian klik Simpan Widget.
Label: JQuery, JSON, JSON Blogger, Sidebar, Tingkat Lanjut, Widget
Senin, 12 November 2012
Memperbaharui Pemuatan JSON berdasarkan Event Scroll

Metode ini masih sama dengan metode-metode penundaan pemuatan JSON yang biasa Saya lakukan untuk widget-widget Saya yaitu dengan cara menyisipkan script callback secara tidak langsung ke dalam area <head> dengan ID tertentu. Kemudian, jika Saya ingin memperbaharui muatan JSON yang sudah ada, Saya tinggal menyingkirkan script callback yang lama kemudian menggantinya dengan duplikat baru dengan parameter yang sudah diperbaharui.
Bayangkan saja bahwa sudah terdapat sebuah script callback dengan ID foo di dalam area <head> seperti ini:
...
...
<script id="foo" src="../feeds/posts/summary?alt=json-in-script&callback=functionName" type="text/javascript"></script>
</head>Kemudian Saya akan menyingkirkannya dengan cara menangkap ID elemen tersebut sebagai awalan untuk menyeleksi node induknya:
var a = document.getElementById('foo');
var parent = a.parentNode; // Mendapatkan elemen induk dari `foo`Kemudian, dari induk tersebut Saya akan menyingkirkan elemen itu sendiri:
var a = document.getElementById('foo');
var parent = a.parentNode; // Mendapatkan elemen induk dari `foo`
parent.removeChild(a); // Singkirkan `foo` dari `parent`Di sini, kita akan melakukan pekerjaan di atas berdasarkan event onscroll dengan batasan akhir berupa jarak gulungan maksimal kontainer. Kode di bawah ini akan menjalankan fungsi bernama myFunction() berdasarkan event onscroll dengan syarat jarak gulungan telah mencapai titk maksimal:
elem.onscroll = function() {
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {
myFunction();
}
};Dimana elem adalah objek berupa kontainer.
Memulai Pekerjaan
Markup HTML
Kita mulai dengan pembuatan markup HTML sederhana seperti ini:
<div id="result-container" style="width:400px;height:400px;overflow:auto;">
<ol></ol>
<span class="loading">Memuat...</span>
</div>Elemen #result-container digunakan sebagai area scroll, elemen ol digunakan sebagai kontainer data yang nantinya akan dihasilkan dari JSON, dan elemen span.loading digunakan sebagai indikator sedang memuat.
Membangun JavaScript
Pertama-tama kita buat script untuk memparse data JSON menjadi elemen HTML. Sederhana, seperti script recent post biasa yang Saya beri nama grabList(), dan hanya akan menghasilkan elemen <li> dengan tautan dan judul posting di dalamnya:
var elem = document.getElementById('result-container'), // Mendapatkan elemen `#result-container`
inner = elem.getElementsByTagName('ol')[0], // Mendapatkan elemen `ol` pertama
loading = elem.getElementsByTagName('span')[0]; // Mendapatkan elemen `span` pertama (dalam hal ini adalah elemen indikator sedang memuat)
// Bangun sebuah script untuk menampilkan daftar posting
function grabList(json) {
var list = json.feed.entry, link, skeleton = "";
// Jalankan loop hanya jika data JSON masih ada/dapat didefinisikan
if (list !== undefined) {
for (var i = 0; i < list.length; i++) {
for (var j = 0; j < list[i].link.length; j++) {
if (list[i].link[j].rel == "alternate") {
link = list[i].link[j].href; // Mendapatkan URL posting
}
}
// Bangun beberapa elemen `<li>` yang berisi tautan dan judul posting...
skeleton += '<li><a href="' + link + '">' + list[i].title.$t + '</a></li>';
}
// ... kemudian sisipkan elemen tersebut ke dalam elemen `<ol>`
inner.innerHTML += skeleton;
// dan sembunyikan indikator sedang memuat.
loading.style.display = "none";
} else {
// Jika data JSON sudah tidak ada (list == undefined), tambahkan kelas baru kepada elemen indikator sedang memuat dengan nilai `the-end`
loading.className += ' the-end';
// kemudian ganti teks indikator sedang memuat dengan kata `Habis`
loading.textContent = 'Habis';
}
}Setelah itu buat sebuah fungsi untuk memuat script callback secara tidak langsung. Tambahkan dua buah parameter untuk menangani start-index dan max-results:
function updateScript(i, max) {
var head = document.getElementsByTagName('head')[0],
script = document.createElement('script');
script.type = 'text/javascript';
script.id = 'load-on-scroll-end';
script.src = 'http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&start-index=' + i + '&max-results=' + max + '&callback=grabList';
head.appendChild(script);
}
// Jalankan fungsi!
updateScript(1, 25);Fungsi di atas akan menyisipkan sebuah script callback JSON Blogger dengan nilai parameter start-index berupa 1 dan max-results berupa 25, sehingga sebuah elemen <script> dengan parameter yang sudah diatur akan disipkan ke dalam area <head> secara tidak langsung seperti ini:
...
...
...
<script id="load-on-scroll-end" src="http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&start-index=1&max-results=25&callback=grabList" type="text/javascript"></script>
</head>Memperbaharui JSON Berdasarkan Jarak Maksimal Gulungan Area
Setelah itu kita tambahkan sebuah kondisional untuk menyingkirkan script callback lama jika script tersebut ada. Tidak perlu membuat fungsi baru, cukup gunakan fungsi tadi agar kita bisa menggunakannya untuk dua hal sekaligus, yaitu menyisipkan script baru dan/atau menyingkirkan script lama (jika ada):
function updateScript(i, max) {
var head = document.getElementsByTagName('head')[0],
script = document.createElement('script');
script.type = 'text/javascript';
script.id = 'load-on-scroll-end';
script.src = 'http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&start-index=' + i + '&max-results=' + max + '&callback=grabList';
if (document.getElementById('load-on-scroll-end')) {
var oldScript = document.getElementById('load-on-scroll-end');
oldScript.parentNode.removeScript(oldScript);
}
head.appendChild(script);
}
updateScript(1, 25); // Jalankan fungsi!Karena kita juga akan menjalankan fungsi updateScript() berdasarkan event onscroll, maka kita juga harus memasukkan updateScript() ke dalam event yang Saya tuliskan pertama kali:
elem.onscroll = function() {
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {
// Muat ulang JSON Blogger dengan `start-index` yang baru melalui parameter `i`
updateScript(i, 25);
// Kemudian tampilkan indikator sedang memuat
loading.style.display = "block";
}
};Parameter i haru dinamis, dan harus bisa bertambah setiap kali gulungan kontainer berakhir. Untuk membuatnya menjadi dinamis, kita akan menggunakan variabel awalan dengan nilai 0, kemudian kita tingkatkan nilainya di dalam event onscroll setiap kali jarak gulungan telah mencapai titik maksimal:
var start = 0;
elem.onscroll = function() {
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {
// Tingkatkan nilai `start` dengan 1 (dari `start = 0` menjadi `start = 1`, `start = 2`, dst...)
start++;
// Muat ulang JSON Blogger dengan `start-index` yang telah diperbaharui...
// ... melalui `start` yang nilainya dikalikan dengan 25
updateScript(start*25, 25); // => dari `(1, 25)` menjadi `(25, 25)`, `(50, 25)`, `(75, 25)`, dst...
// Kemudian tampilkan indikator sedang memuat
loading.style.display = "block";
}
};Produk Final
HTML
<div id="result-container">
<ol></ol>
<span class="loading">Memuat...</span>
</div>CSS
#result-container {
height:400px;
width:400px;
overflow:auto;
margin:50px auto;
font:normal normal 12px 'Trebuchet MS',Trebuchet,Geneva,Arial,Sans-Serif;
}
#result-container ol {
margin:0 0;
padding:0 0;
background-color:#B5D68C;
}
#result-container li {
margin:0 0;
padding:0 0;
list-style:none;
}
#result-container li:nth-child(even) {background-color:#A2C179}
#result-container li a {
display:block;
padding:5px 10px;
font-weight:bold;
color:#396B18;
text-decoration:none;
}
#result-container li a:hover {
background-color:#396B18;
color:white;
text-decoration:none;
}
#result-container .loading {
display:block;
height:26px;
font:normal bold 11px/26px Arial,Sans-Serif;
color:white;
text-align:center;
background-color:#B75A6F;
border-top:2px solid #222;
}
#result-container .loading.the-end {background-color:#666}JavaScript
var widget_config = {
home_page: 'http://nama_blog.blogspot.com', // Your blog homepage
container_id: 'result-container', // ID of the result container
script_id: 'load-on-scroll-end-script', // ID of the asynchronous script
max_result: 25, // Max result post at once script loading
end_text: 'Habis' // End text if all posts has been loaded
};
var elem = document.getElementById(widget_config.container_id),
inner = elem.getElementsByTagName('ol')[0],
loading = elem.getElementsByTagName('span')[0],
start = 0, // Dynamic start-index
max = widget_config.max_result;
function grabList(json) {
var list = json.feed.entry, link, skeleton = "";
if (list !== undefined) {
for (var i = 0; i < list.length; i++) {
for (var j = 0; j < list[i].link.length; j++) {
if (list[i].link[j].rel == "alternate") {
link = list[i].link[j].href;
}
}
skeleton += '<li><a href="' + link + '">' + list[i].title.$t + '</a></li>';
}
inner.innerHTML += skeleton; // Insert the list to the container
loading.style.display = "none"; // Hide the loading indicator
} else {
// If the JSON is empty (list == undefined),
// add a new class to the loading indicator called `the-end`
loading.className += ' the-end';
// Replace the loading indicator text into `fully loaded!` for the example
loading.textContent = widget_config.end_text;
}
}
// Make an indirect script loader with two parameters: start-index and max-result post
function updateScript(a, b) {
var head = document.getElementsByTagName('head')[0],
script = document.createElement('script');
script.type = 'text/javascript';
script.id = widget_config.script_id;
script.src = widget_config.home_page + '/feeds/posts/summary?alt=json-in-script&start-index=' + a + '&max-results=' + b + '&callback=grabList';
// If there is an old script in the document...
if (document.getElementById(widget_config.script_id)) {
var oldScript = document.getElementById(widget_config.script_id);
// Remove the old script, and replace with the new one that has an updated start-index value
oldScript.parentNode.removeChild(oldScript);
}
head.appendChild(script);
}
// Start loading the callback script with start-index of 1
updateScript(1, max);
// When the container is being scrolled...
elem.onscroll = function() {
// ... check the scroll distance
if ((this.scrollTop + this.offsetHeight) == inner.offsetHeight) {
// If the distance equal to the height of the inner container...
start++; // Increase the start value by one
// then load the new script with an updated start-index
updateScript(start*max, max);
// and show the loading indicator
loading.style.display = "block";
}
};Label: JavaScript, JSON, JSON Blogger, Layar, Posting, Tingkat Lanjut, Widget
Minggu, 11 November 2012
Widget Recent Comment dengan Sistem Notifikasi
Pembaharuan 22 April 2013: Mengintegrasikan widget dengan cookies, menambahkan opsi summary dan menyingkirkan opsi tt_id.
Widget ini bukan widget recent comment biasa yang biasanya Anda temukan di blog-blog lain, karena widget ini memiliki kemampuan untuk memberitahu kepada administrator bahwa terdapat komentar baru yang telah masuk.
Demonya bisa Anda lihat di sini, namun efeknya hanya akan terlihat apabila terdapat (paling tidak) satu komentar baru yang masuk di blog ini:
Untuk memasang widget ini, pertama-tama tambahkan sebuah elemen halaman HTML/JavaScript. Salin kode ini kemudian letakkan di dalam formulirnya:
<style type="text/css" scoped="scoped">
.cm-outer {
margin:0 auto;
padding:0 0;
font:normal normal 11px Arial,Sans-Serif;
border:1px solid;
border-top:none;
}
.cm-outer li {
margin:0 0;
padding:7px 10px 12px;
list-style:none;
clear:both;
border-top:1px solid;
}
.cm-outer .cm-header {margin:0 0 5px}
.cm-outer .cm-content {overflow:hidden}
.cm-outer img {
display:block;
float:left;
margin:2px 10px 2px 0;
border:4px solid black;
background:#8fa2cb url('http://img1.blogblog.com/img/anon36.png') no-repeat 50% 50%;
overflow:hidden;
}
</style>
<div id="comments-container">Loading…</div>
<script type="text/javascript">
var cm_config = {
home_page: "http://nama_blog.blogspot.com",
max_result: 7,
t_w: 32,
t_h: 32,
summary: 9999,
new_tab_link: true,
ct_id: "comments-container",
new_cm: " Komentar Baru!",
interval: 30000,
alert: true
};
</script>
<script type="text/javascript" src="http://reader-download.googlecode.com/svn/trunk/recent-comments-script-with-notification.js"></script>Ganti kode yang Saya beri tanda dengan URL blog Anda kemudian klik Simpan. Hasil maksimal akan terlihat saat terdapat komentar baru yang masuk.
Konfigurasi Widget Lanjutan
Ada beberapa hal yang harus Anda ketahui mengenai konfigurasi lanjutan widget ini:
| Opsi | Keterangan |
|---|---|
| home_page | Ganti nilainya dengan URL halaman muka blog Anda |
| max_result | Digunakan untuk menentukan jumlah komentar yang akan ditampilkan pada widget ini |
| t_w | Digunakan untuk menentukan lebar avatar |
| t_h | Digunakan untuk menentukan tinggi avatar |
| summary | Digunakan untuk menentukan jumlah karakter komentar yang ditampilkan |
| new_tab_link | Jika bernilai true, seluruh tautan yang ada di dalam widget ini akan terbuka di tab/jendela baru saat diklik |
| ct_id | ID kontainer daftar komentar (abaikan jika tidak perlu) |
| new_cm | Label teks yang akan muncul setelah jumlah komentar (Misalnya: 2 Komentar Baru!) |
| interval | Lihat deskripsi di bawah |
| alert | Lihat deskripsi di bawah |
Opsi: interval
Digunakan untuk menentukan interval penyegaran feed komentar. Menggunakan satuan milidetik. Nilai 30000 artinya bahwa feed komentar akan disegarkan selama setengah menit sekali untuk memeriksa apakah terdapat komentar baru yang masuk atau tudak. Jika terdapat komentar baru yang masuk, maka widget ini akan menampilkan pesan bahwa komentar baru telah masuk.
Opsi: alert
Ini cuma opsi untuk menentukan gaya pemberitahuan pesan. Jika bernilai false, widget ini akan menampilkan pesan masuknya komentar baru pada title bar peramban seperti gambar 1. Sebaliknya, jika bernilai true, notifikasi akan muncul pada kotak peringatan seperti terlihat pada gambar 2:


Pembaharuan
Sekarang Anda bisa menampilkan teks notifikasi pada elemen HTML tertentu dengan cara mengubah nilai opsi alert menjadi sebuah fungsi seperti ini:
var cm_config = {
...
alert: function(total, label) {
// Lakukan sesuatu dengan `total` dan `label`
}
};total berfungsi untuk menampilkan total komentar baru, sedangkan label berfungsi untuk menampilkan nilai pada opsi new_cm. Sebagai contoh, buat sebuah elemen HTML seperti ini. Elemen ini akan digunakan sebagai penampil/kontainer teks total komentar:
<div id="show-total"></div>Setelah itu, sisipkan teks total komentar ke dalam elemen tersebut dengan JavaScript innerHTML melalui fungsi di atas:
var cm_config = {
...
alert: function(total, label) {
document.getElementById('show-total').innerHTML = '<strong>'+total+' '+label+'</strong>';
}
};Jika terdapat notifikasi baru, maka teks notifikasi tersebut akan muncul di dalam elemen #show-total dan akan menghasilkan markup HTML seperti ini:
<div id="show-total">
<strong>1 Komentar Baru!</strong>
</div>Sebaiknya tambahkan juga atribut title dengan pesan tertentu dan satu buah fungsi kecil pada elemen di atas seperti ini:
<div id="show-total" title="Abaikan!" onclick="this.innerHTML='';"></div>Fungsinya untuk menghilangkan teks total komentar apabila pengguna mengeklik elemen tersebut.
Label: JavaScript, Komentar, Widget
Rabu, 07 November 2012
XHTML Blogger, Item Kontrol
Tombol Edit Cepat Posting
Biasanya berbentuk gambar/ikon pensil dan terletak di dalam footer posting:
<b:includable id='postQuickEdit' var='post'>
<b:if cond='data:post.editUrl'>
<span expr:class='"item-control " + data:post.adminClass'>
<a expr:href='data:post.editUrl' expr:title='data:top.editPostMsg'>
<img alt='' class='icon-action' height='18' src='http://img2.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/>
</a>
</span>
</b:if>
</b:includable>| Data | Keterangan | Tampilan/Contoh Tampilan |
|---|---|---|
| data:post.editUrl | Elemen ini akan menghasilkan URL pengeditan posting | http://www.blogger.com/post-edit.g?blogID=489094982896596XXXX&postID=622513084927827XXXX&from=pencil |
| data:top.editPostMsg | Elemen ini akan menghasilkan deskripsi/pesan tautan pengeditan posting | Edit Entri |
| data:post.adminClass | Elemen ini akan menghasilkan kelas khusus administrator (???) | blog-admin pid-251761511 |
Tombol Edit Cepat Widget
Berbentuk gambar/ikon kunci inggris dan terletak pada bagian bawah widget:
<b:include name='quickedit'/>Tombol Penghapusan Komentar
Label: Kamus Blogspot, Komentar, Posting, Tingkat Lanjut, Widget
Selasa, 30 Oktober 2012
Widget Daftar Isi Tabulasi untuk Blogger
Pembaharuan 29 Maret 2013: Menambahkan opsi thumbSize, noThumb, sortAlphabetically, showNew dan newText
Berikut ini adalah sebuah widget daftar isi blog berbentuk tabulasi. Terdiri dari dua sisi berupa daftar kategori dan daftar posting. Setiap nama kategori akan menjadi tab, sedangkan daftar posting akan menjadi konten tab yang akan muncul setiap kali tab diklik:
Integrasi Widget ke Blogger
Pertama-tama klik menu Laman pada sidebar. Kemudian, pada menu Laman Baru pilihlah Laman Kosong:

Setelah itu Anda akan melihat formulir halaman statis seperti ini. Klik mode HTML:

Salin kode di bawah ini kemudian letakkan di dalam formulirnya:
<link rel="stylesheet" href="http://reader-download.googlecode.com/svn/trunk/tabbed-toc-skin.css" type="text/css" media="screen" />
<div id="tabbed-toc"><span class="loading">Memuat...</span></div>
<a style="display:block;text-align:right;font:normal bold 8px Arial,Sans-Serif;text-decoration:none;margin:10px;" href="http://hompimpaalaihumgambreng.blogspot.com/2012/10/widget-daftar-isi-tabulasi-untuk-bogger.html" title="Tabbed TOC">DTE :]</a>
<script type="text/javascript">
var tabbedTOC = {
blogUrl: "http://nama_blog.blogspot.com", // Blog URL
containerId: "tabbed-toc", // Container ID
activeTab: 1, // The default active tab index (default: the first tab)
showDates: false, // `true` to show the post date
showSummaries: false, // `true` to show the posts summaries
numChars: 200, // Number of summary chars
showThumbnails: false, // `true` to show the posts thumbnails (Not recommended)
thumbSize: 40, // Thumbnail size
noThumb: "http://3.bp.blogspot.com/-vpCFysMEZys/UOEhSGjkfnI/AAAAAAAAFwY/h1wuA5kfEhg/s72-c/grey.png", // A "no thumbnail" URL
monthNames: [ // Array of month names
"Januari",
"Februari",
"Maret",
"April",
"Mei",
"Juni",
"Juli",
"Agustus",
"September",
"Oktober",
"November",
"Desember"
],
newTabLink: true, // Open link in new window?
maxResults: 99999, // Maximum post results
preload: 0, // Load the feed after 0 seconds (option => time in milliseconds || "onload")
sortAlphabetically: true, // `false` to sort posts by published date
showNew: 7, // `false` to hide the "New!" mark in most recent posts, or define how many recent posts are to be marked
newText: " - <em style='color:red;'>New!</em>" // HTML for the "New!" text
};
</script>
<script type="text/javascript" src="http://reader-download.googlecode.com/svn/trunk/tabbed-toc.js"></script>Ganti URL yang Saya beri tanda dengan alamat blog Anda kemudian klik Publikasikan.
Pengaturan Lanjutan
| Opsi | Keterangan |
|---|---|
| blogUrl | Ganti nilainya dengan alamat blog Anda. |
| containerId | ID elemen yang akan digunakan sebagai penampung JSON yang sudah diproses (abaikan jika tidak perlu). |
| activeTab | Digunakan untuk menentukan nomor urut tab yang aktif (nilai 1 akan membuat tab di urutan pertama menjadi aktif, begitu pula konten tab pertama juga akan terbuka saat pertama kali halaman diakses). |
| showDates | Ganti nilainya menjadi true untuk menampilkan waktu terbit posting. |
| showSummaries | Ganti nilainya menjadi true untuk menampilkan ringkasan posting. |
| showThumbnails | Ganti nilainya menjadi true untuk menampilkan thumbnail posting (tidak direkomendasikan karena akan membuat halaman lama termuat). |
| thumbSize | Digunakan untuk menentukan ukuran thumbnail. |
| noThumb | URL gambar cadangan untuk posting yang tidak memiliki gambar di dalamnya. |
| monthNames | Digunakan untuk menentukan nama-nama bulan sesuai dengan sistem penanggalan di negara tempat Anda tinggal. |
| newTabLink | Jika bernilai true, semua tautan akan membuka di tab/jendela baru saat diklik. |
| maxResults | Jumlah maksimal posting yang akan ditampilkan. |
| preload | Digunakan untuk menentukan waktu penundaan pemuatan JSON. Gunakan satuan milidetik atau cukup tuliskan "onload" agar widget ini memuat setelah keseluruhan halaman telah selesai termuat. |
| sortAlphabetically | false untuk menyortir posting secara normal berdasarkan bulan terbit, true untuk menyortir posting berdasarkan alfabet. |
| showNew | false untuk menyembunyikan tanda New!. Ganti dengan angka untuk menentukan berapa banyak posting terbaru yang ingin ditandai dengan label New!. |
| newText | Markup HTML bebas untuk membuat label New! pada posting-posting terbaru. |
Selasa, 18 September 2012
Membangun Aplikasi Quick Search dengan JSON Blogger

Selain melalui halaman pencarian biasa, Blogger juga mengizinkan kita untuk mengakses posting-posting acak di dalam feed yang dapat disortir berdasarkan kata kunci. Ini bisa dilakukan melalui parameter q.
Format dasar URL feed untuk memanggil data posting dengan sortir berdasarkan kata kunci adalah seperti ini:
http://nama_blog.blogspot.com/feeds/posts/summary?q=Kata KunciYang jika diakses akan menghasilkan halaman berisi daftar posting tersortir berdasarkan kata kunci dari parameter q seperti ini ⇒ Daftar posting dengan kata kunci 'september'
Dengan memanfaatkan kerangka dasar widget Blogger, event onsubmit pada formulir dan juga penargetan elemen kontainer yang tepat, maka parameter ini memungkinkan kita untuk menciptakan aplikasi quick search. Yaitu sebuah aplikasi yang bisa digunakan untuk mencari artikel/posting dalam sebuah blog tanpa harus berpindah dari halaman asal. Seluruh hasil penelusuran akan tampil pada tempat yang sama!
Pada prinsipnya, hal pertama yang Saya buat adalah sebuah fungsi yang akan menyisipkan elemen <script> secara tidak langsung ke dalam area <head> saat fungsi tersebut dieksekusi. JavaScript yang Saya bangun adalah callback widget Blogger biasa dengan pola sumber yang umum. Saya hanya menambahkan parameter kata kunci pencarian sebagai penyortir posting yang akan dikirim nantinya:
http://nama_blog.blogspot.com/feeds/posts/summary?alt=json-in-script&q=Kata Kunci&callback=functionNameNilai parameter sumber diambil dari sebuah elemen <input> yang Saya letakkan di dalam formulir. Logikanya seperti ini:

function insertScript() {
// Membangun elemen <script> untuk disisipkan ke dalam area <head> ...
// ... dengan parameter kueri yang ditentukan oleh nilai elemen 'feed-q-input'
var query = document.getElementById('feed-q-input').value,
s = document.createElement('script');
s.type = "text/javascript";
s.src = "http://nama_blog.blogspot.com/feeds/posts/summary?q="+query+"&alt=json-in-script&callback=functionName";
document.getElementsByTagName('head')[0].appendChild(s);
}<form onsubmit="insertScript();return false;"> ... </form>Kode aslinya lebih panjang, tapi pada intinya inilah dasar yang Saya gunakan untuk membangun aplikasi pencarian cepat. Anda bisa mengunduh JavaScript dan melihat demonya melalui tombol-tombol ini:
Integrasi Widget ke Blogger
Pertama-tama masuk ke halaman editor HTML template Anda, kemudian salin kode CSS ini dan letakkan di atas kode ]]></b:skin> atau </style>:
#search-form-feed {
width:200px; /* lebar kotak penelusuran */
position:relative;
margin:0 0 10px;
padding:0 0;
font:normal normal 11px Arial,Sans-Serif;
color:#333;
}
#feed-q-input {
display:block;
width:100%;
border:2px solid #bbb;
background-color:white;
padding:5px 5px;
font:normal bold 13px Tahoma,Arial,Sans-Serif;
color:#ccc;
margin:0 0;
-webkit-border-radius:4px;
-moz-border-radius:4px;
border-radius:4px;
-webkit-box-shadow:inset 0 1px 5px rgba(0,0,0,.2);
-moz-box-shadow:inset 0 1px 5px rgba(0,0,0,.2);
box-shadow:inset 0 1px 5px rgba(0,0,0,.2);
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
#feed-q-input:focus {
border-color:#0D6786;
color:#333;
outline:none;
-webkit-box-shadow:0 0 5px #153E95,0 0 7px #153E95;
-moz-box-shadow:0 0 5px #153E95,0 0 7px #153E95;
box-shadow:0 0 5px #153E95,0 0 7px #153E95;
}
#search-result-container {
width:400px;
height:300px;
overflow:auto;
position:absolute;
top:100%;
right:0;
z-index:999;
background-color:#E5EDF7;
border:2px solid white;
padding:10px 10px 0;
margin:10px 0 0;
-webkit-box-shadow:0 1px 2px rgba(0,0,0,.4),0 7px 7px -4px rgba(0,0,0,.4);
-moz-box-shadow:0 1px 2px rgba(0,0,0,.4),0 7px 7px -4px rgba(0,0,0,.4);
box-shadow:0 1px 2px rgba(0,0,0,.4),0 7px 7px -4px rgba(0,0,0,.4);
-webkit-border-radius:5px;
-moz-border-radius:5px;
border-radius:5px;
display:none;
}
#search-result-container mark {
background-color:yellow;
color:black;
}
#search-result-container a {
text-decoration:none;
color:#0D3E71;
font-weight:bold;
font-size:12px;
display:block;
}
#search-result-container a:hover {
color:#052748;
}
#search-result-container h4 {
margin:0 0 10px;
font:normal bold 12px 'Trebuchet MS',Arial,Sans-Serif;
color:#B50001;
}
#search-result-container ol {
background:transparent;
border:none;
margin:0 0 10px;
padding:0 0;
}
#search-result-container li {
margin:0 0 1px;
padding:7px 8px;
list-style:none;
border:1px solid #B7C1CE;
background-color:white;
overflow:hidden;
word-wrap:break-word;
}
#search-result-container li img {
display:block;
float:left;
margin:0 10px 4px 0;
border:1px solid #B7C1CE;
background-color:#F5F5F5;
padding:2px 2px;
}
#search-result-loader {
position:absolute;
top:100%;
left:5px;
z-index:999;
background-color:#0D6786;
color:white;
padding:3px 5px;
margin:-2px 0 0;
font:normal bold 10px Arial,Sans-Serif;
-webkit-border-radius:0 0 3px 3px;
-moz-border-radius:0 0 3px 3px;
border-radius:0 0 3px 3px;
display:none;
}Setelah itu masuk ke halaman Tata Letak, kemudian tambahkan sebuah elemen halaman HTML/JavaScript. Salin kode ini dan letakkan di dalam formulirnya:
<div id="search-form-feed">
<form action="/search" onsubmit="return updateScript();">
<input name="q" type="text" value="Telusuri..." id="feed-q-input" onkeyup="resetField();" onfocus="this.value='';"/>
</form>
<div id="search-result-container"></div>
<div id="search-result-loader">Loading...</div>
</div>
<script type="text/javascript">
//<![CDATA[
var searchFormConfig = {
url: "http://hompimpaalaihumgambreng.blogspot.com", // URL Blog
numPost: 9999, // Jumlah maksimal temuan
summaryPost: true, // 'true' jika ingin menampilkan deskripsi posting
summaryLength: 400, // Jumlah karakter ringkasan posting
resultTitle: "Hasil penelusuran untuk kata kunci", // Judul hasil pencarian
noResult: "No result", // Deskripsi 'tak ditemukan'
resultThumbnail: true, // 'true' untuk menampilkan thumbnail posting
thumbSize: 40, // Ukuran & resolusi thumbnail
fallbackThumb: "http://reader-download.googlecode.com/svn/trunk/images/no-image-72x72.png" // Fallback thumbnail untuk posting tak bergambar
};
//]]>
</script>
<script type="text/javascript" src="http://reader-download.googlecode.com/svn/trunk/blogger-quick-search.js"></script>Klik Simpan Widget.
Konfigurasi Widget
Semua opsi sangat umum. Saya yakin Anda bisa dengan cepat mempelajarinya:
| Opsi | Keterangan |
|---|---|
| url | URL blog Anda |
| numPost | Jumlah posting yang akan ditampilkan dalam hasil pencarian. Nilai 9999 maksudnya adalah Anda mencoba untuk menampilkan semua posting yang ditemukan |
| summaryPost | Pilihan untuk menampilkan atau menyembunyikan ringkasan posting dalam hasil pencarian |
| summaryLength | Digunakan untuk menentukan jumlah karakter ringkasan hasil pencarian |
| resultTitle | Judul hasil pencarian |
| noResult | Peringatan yang menunjukkan bahwa posting tidak ditemukan |
| resultThumbnail | Pilihan untuk menampilkan atau menyembunyikan thumbnail posting dalam hasil pencarian |
| thumbSize | Digunakan untuk menentukan ukuran dan resolusi thumbnail hasil pencarian |
| fallbackThumb | Gambar cadangan untuk posting tak bergambar. |
Label: JavaScript, JSON, JSON Blogger, Menengah, Posting, Widget
Senin, 03 September 2012
Widget Translator Halaman - Versi 2
Widget ini dibuat sebagai pengganti aplikasi Google Translate yang berat. Murni, dibuat dari kode HTML dan JavaScript, sehingga Anda bisa terbebas dari permintaan HTTP yang biasanya datang dari JavaScript bawaan widget Google Translate. Di sini JavaScript berfungsi untuk melakukan pemformatan URL translasi berdasarkan pola query string dari Google Translate yang berbentuk seperti ini agar kita bisa menuju ke halaman Google Translate tanpa harus terlibat dengannya pada saat awal kunjungan:
http://translate.google.com/translate?u=http://www.nama-situs.com&langpair=from|to&hl=en- from ⇒ Bahasa Asal
- to ⇒ Bahasa Tujuan
Kode Widget
<style type="text/css">
#translator-wrapper {
display:block;
width:200px;
border:1px solid #0A340A;
background-color:white;
overflow:hidden;
position:relative;
}
#translator-wrapper select {
border:none;
background:transparent;
font:normal normal 11px Tahoma,Verdana,Arial,Sans-Serif;
width:100%;
color:black;
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
-webkit-appearance:none;
cursor:text;
padding:5px 10px;
}
#translator-wrapper a,
#translator-wrapper a:hover {
display:block;
background-color:#0A340A;
border:none;
color:white;
margin:0 0;
text-decoration:none;
position:absolute;
top:0;
right:0;
bottom:0;
cursor:pointer;
width:30px;
}
#translator-wrapper a:before {
content:"";
display:block;
width:0;
height:0;
border:5px solid transparent;
border-left-color:white;
position:absolute;
top:50%;
left:13px;
margin-top:-5px;
}
#translator-wrapper a:hover {background-color:#0A2C0A}
#translator-wrapper a:active {background-color:#082408}
#translator-wrapper select:focus,
#translator-wrapper a:focus,
#translator-wrapper select:active,
#translator-wrapper a:active {
border:none;
outline:none;
cursor:pointer;
}
</style>
<h3>Translate this page to:</h3>
<div id="translator-wrapper">
<select id="translate-language">
<option value="en" selected="selected">English</option>
<option value="id">Indonesian</option>
<option value="af">Afrikaans</option>
<option value="sq">Albanian</option>
<option value="ar">Arabic</option>
<option value="hy">Armenian</option>
<option value="az">Azerbaijani</option>
<option value="eu">Basque</option>
<option value="be">Belarusian</option>
<option value="bn">Bengali</option>
<option value="bg">Bulgarian</option>
<option value="ca">Catalan</option>
<option value="zh-CN">Chinese</option>
<option value="hr">Croatian</option>
<option value="cs">Czech</option>
<option value="da">Danish</option>
<option value="nl">Dutch</option>
<option value="en">English</option>
<option value="eo">Esperanto</option>
<option value="et">Estonian</option>
<option value="tl">Filipino</option>
<option value="fi">Finnish</option>
<option value="fr">French</option>
<option value="gl">Galician</option>
<option value="ka">Georgian</option>
<option value="de">German</option>
<option value="el">Greek</option>
<option value="gu">Gujarati</option>
<option value="ht">Haitian Creole</option>
<option value="iw">Hebrew</option>
<option value="hi">Hindi</option>
<option value="hu">Hungarian</option>
<option value="is">Icelandic</option>
<option value="id">Indonesian</option>
<option value="ga">Irish</option>
<option value="it">Italian</option>
<option value="ja">Japanese</option>
<option value="kn">Kannada</option>
<option value="ko">Korean</option>
<option value="la">Latin</option>
<option value="lv">Latvian</option>
<option value="lt">Lithuanian</option>
<option value="mk">Macedonian</option>
<option value="ms">Malay</option>
<option value="mt">Maltese</option>
<option value="no">Norwegian</option>
<option value="fa">Persian</option>
<option value="pl">Polish</option>
<option value="pt">Portuguese</option>
<option value="ro">Romanian</option>
<option value="ru">Russian</option>
<option value="sr">Serbian</option>
<option value="sk">Slovak</option>
<option value="sl">Slovenian</option>
<option value="es">Spanish</option>
<option value="sw">Swahili</option>
<option value="sv">Swedish</option>
<option value="ta">Tamil</option>
<option value="te">Telugu</option>
<option value="th">Thai</option>
<option value="tr">Turkish</option>
<option value="uk">Ukrainian</option>
<option value="ur">Urdu</option>
<option value="vi">Vietnamese</option>
<option value="cy">Welsh</option>
<option value="yi">Yiddish</option>
</select><a id="translate-me" href="#" title="Translate!"></a>
</div>
<script type="text/javascript">
(function() {
var mylang = "id", // Your website language
anchor = document.getElementById('translate-me');
anchor.onclick = function() {
window.open('http://translate.google.com/translate?u=' + encodeURIComponent(location.href) + '&langpair=' + mylang + '%7C' + document.getElementById('translate-language').value + '&hl=en');
return false;
};
})();
</script>Salin saja kode di atas lalu letakkan di dalam sebuah widget HTML/JavaScript. Klik Simpan dan lihat hasilnya:
Label: JavaScript, Navigasi, Widget






