Cara Membuat Lonceng Notifikasi Ringan & Responsif di Blogger
Warkasa1919
Konten Premium Warkasa1919
Akses Lifetime • Script Eksklusif • Tutorial Langka • Fiksi
Premium Lifetime Access dengan fitur rutin update.
Cara Pembayaran
π Paket Premium — Rp125.000 (Akses Semua Artikel)
π Per Artikel — Rp25.000 (Satu artikel)
π Per Artikel — Rp25.000 (Satu artikel)
Cara Membuat Lonceng Notifikasi Ringan & Responsif di Blogger
Warkasa1919 - Dunia blogging kini bergerak cepat. Untuk mempertahankan perhatian pembaca, blog modern wajib memiliki fitur notifikasi langsung seperti platform besar.
Di tutorial premium ini, Anda akan mempelajari cara membuat lonceng notifikasi elegan yang mampu menampilkan:
- Artikel terbaru
- Konten premium berbayar
- Promo & info jasa digital
- Edu & pengumuman komunitas
Keunggulan Fitur Ini
✅ Super ringan, tanpa library besar
✅ UI modern seperti aplikasi profesional
✅ Responsif di semua perangkat
✅ Support konten premium & promo
✅ Bisa disimpan di
✅ Sistem badge + notifikasi sekali tampil
✅ UI modern seperti aplikasi profesional
✅ Responsif di semua perangkat
✅ Support konten premium & promo
✅ Bisa disimpan di
<head> template Blogger✅ Sistem badge + notifikasi sekali tampil
Kode gratis Lonceng Notifikasi Blogger
<!-- π Smart Notification Bell for Blogger -->
<style>
#notifBell{position:fixed;bottom:20px;right:20px;background:#111;color:#fff;width:55px;height:55px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;cursor:pointer;z-index:9999;transition:.3s;box-shadow:0 4px 12px rgba(0,0,0,.3)}
#notifBell:hover{transform:scale(1.08)}
#notifPanel{position:fixed;bottom:85px;right:20px;background:#fff;width:300px;border-radius:14px;box-shadow:0 8px 24px rgba(0,0,0,.2);padding:15px;display:none;font-family:sans-serif;z-index:9999}
#notifPanel h3{margin:0 0 10px 0;font-size:16px;font-weight:700}
#notifPanel .item{padding:10px;border-bottom:1px solid #eee;font-size:14px}
#notifBadge{background:red;color:#fff;font-size:12px;padding:2px 6px;border-radius:10px;position:absolute;top:-6px;right:-6px}
</style>
<div id="notifBell">π<span id="notifBadge" style="display:none">1</span></div>
<div id="notifPanel">
<h3>π Notifikasi Premium</h3>
<div class="item"><strong>Konten Premium Baru!</strong><br>Cara membuat landing page profesional di Blogger π―</div>
<div class="item">Jasa pembuatan website — Diskon 25%!</div>
<div class="item"><a href="/search/label/Premium" target="\_blank">Lihat semua konten premium →</a></div>
</div>
<script>
const bell=document.querySelector("#notifBell");
const panel=document.querySelector("#notifPanel");
const badge=document.querySelector("#notifBadge");
let seen=localStorage.getItem("premiumSeen");
if(!seen)badge.style.display="block";
bell.onclick=()=>{panel.style.display=panel.style.display==="block"?"none":"block";badge.style.display="none";localStorage.setItem("premiumSeen","yes")};
</script>
Selengkapnya : cara memasang Lonceng Notifikasi — ringan, responsif, dan siap-pasang untuk blog
Bagian premium terkunci π
Masukkan password untuk melanjutkan.
Masukkan password untuk melanjutkan.
✅ Premium unlocked!
…
Salin seluruh blok berikut dan tempel di <head> (CSS) + di area header/body (HTML) + sebelum </body> (JS) — atau gabungkan sesuai struktur template Anda.
<!-- ============================================
LONCENG NOTIFIKASI RINGAN UNTUK warkasa1919.com
- Copy CSS ke <head>
- Paste HTML di header/nav tempat Anda mau
- Paste JS sebelum </body>
- Mode "remote": ganti NOTIF_ENDPOINT ke url JSON Anda
============================================ -->
<!-- ====== CSS (paste ke <head>) ====== -->
<style>
/* Reset kecil untuk komponen */
.wk-notif { font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; position: relative; display:inline-block; z-index:9999; }
.wk-bell-btn {
background: transparent;
border: none;
cursor: pointer;
position: relative;
padding: 6px;
display: inline-flex;
align-items:center;
justify-content:center;
border-radius:999px;
transition: transform .12s ease, box-shadow .12s ease;
}
.wk-bell-btn:active{ transform: scale(.98); }
.wk-bell-btn:focus{ outline: 3px solid rgba(81, 203, 238, 0.4); outline-offset: 2px; }
/* Bell SVG size */
.wk-bell-svg { width:28px; height:28px; display:block; }
/* Badge */
.wk-badge {
position: absolute;
top: 2px;
right: 2px;
min-width:18px;
height:18px;
padding:0 5px;
font-size:12px;
line-height:18px;
border-radius:999px;
background:#e23b3b;
color:white;
display:inline-flex;
align-items:center;
justify-content:center;
box-shadow: 0 1px 3px rgba(0,0,0,.2);
}
/* Dropdown panel */
.wk-panel {
position:absolute;
right:0;
top:40px;
width:320px;
max-height:420px;
background: #fff;
border-radius:10px;
box-shadow: 0 10px 30px rgba(0,0,0,.12);
overflow:hidden;
display:flex;
flex-direction:column;
transform-origin: top right;
transition: opacity .16s ease, transform .16s ease;
opacity:0;
pointer-events:none;
transform: translateY(-6px) scale(.98);
border: 1px solid rgba(0,0,0,.04);
}
/* visible */
.wk-panel.open { opacity:1; pointer-events:auto; transform: translateY(0) scale(1); }
/* Header inside panel */
.wk-panel .hdr {
display:flex;
align-items:center;
justify-content:space-between;
padding:10px 12px;
border-bottom:1px solid rgba(0,0,0,.04);
gap:8px;
}
.wk-controls { display:flex; gap:8px; align-items:center; }
.wk-controls button { background:transparent; border:none; cursor:pointer; font-size:13px; color:#333; padding:6px; border-radius:6px; }
.wk-controls button:hover{ background:rgba(0,0,0,.04); }
/* List */
.wk-list { overflow:auto; padding:8px; display:flex; flex-direction:column; gap:8px; }
.wk-item {
display:flex; gap:10px; align-items:flex-start; padding:8px; border-radius:8px; transition: background .12s;
}
.wk-item.unread { background: linear-gradient(90deg, rgba(226,59,59,0.04), transparent); }
.wk-item:hover { background: rgba(0,0,0,.02); }
.wk-item .meta { font-size:12px; color:#666; }
.wk-item h4 { margin:0; font-size:14px; line-height:1.2; color:#111; }
.wk-item p { margin:4px 0 0 0; font-size:13px; color:#444; }
/* Tag chips */
.wk-tag { font-size:11px; padding:3px 6px; border-radius:6px; background: rgba(0,0,0,.06); color:#222; }
/* Empty state */
.wk-empty { padding:28px; text-align:center; color:#666; font-size:14px; }
/* Footer */
.wk-footer { padding:8px; border-top:1px solid rgba(0,0,0,.04); display:flex; justify-content:space-between; gap:8px; align-items:center; }
/* Responsive: full width on small screens */
@media (max-width:480px){
.wk-panel { left:8px; right:8px; width: auto; top:44px; border-radius:12px; }
.wk-bell-svg { width:26px; height:26px; }
}
</style>
<!-- ====== HTML (tempatkan di header/nav) ====== -->
<div class="wk-notif" id="wk-notif" aria-live="polite">
<button class="wk-bell-btn" id="wk-bell-btn" aria-haspopup="true" aria-expanded="false" aria-label="Notifikasi">
<!-- simple bell SVG -->
<svg class="wk-bell-svg" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M15 17H9c0 1.66 1.34 3 3 3s3-1.34 3-3z" fill="currentColor" opacity=".9"/><path d="M18 16v-5c0-3.07-1.63-5.64-4.5-6.32V4a1.5 1.5 0 10-3 0v.68C7.63 5.36 6 7.92 6 11v5l-1.99 2H20l-2-2z" fill="currentColor"/></svg>
<span class="wk-badge" id="wk-badge" style="display:none">0</span>
</button>
<div class="wk-panel" id="wk-panel" role="dialog" aria-label="Daftar notifikasi" aria-hidden="true">
<div class="hdr">
<strong>Notifikasi</strong>
<div class="wk-controls">
<button id="wk-filter-btn" aria-haspopup="true" aria-expanded="false" title="Filter">Filter</button>
<button id="wk-mark-all" title="Tandai semua sudah dibaca">Tandai semua</button>
</div>
</div>
<div class="wk-list" id="wk-list">
<!-- items di-render lewat JS -->
<div class="wk-empty" id="wk-empty">Belum ada notifikasi.</div>
</div>
<div class="wk-footer">
<small id="wk-foot-info">Menampilkan 0 notifikasi</small>
<div>
<button id="wk-open-all" title="Buka semua di halaman notifikasi">Lihat semua</button>
</div>
</div>
</div>
</div>
<!-- ====== OPTIONAL: Toast area (untuk "ada konten baru") ====== -->
<div id="wk-toast-root" style="position:fixed; bottom:18px; right:18px; z-index:99999; pointer-events:none;"></div>
<!-- ====== JS (paste sebelum </body>) ====== -->
<script>
(function(){
/* =========== Konfigurasi =========== */
const MODE = 'remote'; // 'static' atau 'remote'
const NOTIF_POLL_INTERVAL = 45 * 1000; // polling tiap 45 detik (hanya untuk contoh)
const NOTIF_ENDPOINT = '/api/notifications.json'; // ganti dengan endpoint Anda (jika MODE==='remote')
const STORAGE_KEY = 'warkasa1919_notif_read_v1';
/* Contoh data statis (digunakan jika MODE === 'static') */
const STATIC_DATA = [
{ id: 'p1', type:'premium', title:'Artikel Premium: "Rahasia SEO 2025"', body:'Khusus untuk pelanggan — 3 tips cepat.', url:'/premium/seo-2025', ts: Date.now()-3600e3 },
{ id: 'b1', type:'paid', title:'Promo Paket Penulisan', body:'Diskon 25% untuk 5 artikel.', url:'/jasa/penulisan', ts: Date.now()-7200e3 },
{ id: 's1', type:'service', title:'Jasa Pembuatan Website', body:'Portfolio dan harga tersedia.', url:'/jasa/web', ts: Date.now()-86400e3 }
];
/* =========== Utility =========== */
function el(id){ return document.getElementById(id); }
function timeAgo(ts){
const s = Math.floor((Date.now()-ts)/1000);
if (s<60) return s + 's';
if (s<3600) return Math.floor(s/60)+'m';
if (s<86400) return Math.floor(s/3600)+'h';
return Math.floor(s/86400)+'d';
}
/* =========== State =========== */
let items = [];
let readMap = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
/* =========== DOM refs =========== */
const bellBtn = el('wk-bell-btn');
const panel = el('wk-panel');
const listEl = el('wk-list');
const badge = el('wk-badge');
const empty = el('wk-empty');
const markAllBtn = el('wk-mark-all');
const openAllBtn = el('wk-open-all');
const footInfo = el('wk-foot-info');
const toastRoot = el('wk-toast-root');
/* =========== Render =========== */
function renderList(filter='all'){
listEl.innerHTML = '';
const filtered = items.filter(it => filter==='all' ? true : it.type===filter);
if (filtered.length===0){
empty.style.display='block';
} else {
empty.style.display='none';
}
filtered.slice(0,50).forEach(it => {
const unread = !readMap[it.id];
const item = document.createElement('div');
item.className = 'wk-item' + (unread ? ' unread' : '');
item.tabIndex = 0;
item.setAttribute('role','button');
item.innerHTML = `
<div style="flex:1;">
<div style="display:flex;align-items:center;gap:8px;justify-content:space-between">
<h4>${escapeHtml(it.title)}</h4>
<span class="wk-tag">${escapeHtml(it.type)}</span>
</div>
<p>${escapeHtml(it.body || '')}</p>
<div class="meta">${timeAgo(it.ts)} • ${it.source || 'Warkasa'}</div>
</div>
`;
item.addEventListener('click', function(e){
markRead(it.id);
if (it.url) window.location.href = it.url;
});
listEl.appendChild(item);
});
footInfo.textContent = `Menampilkan ${filtered.length} notifikasi`;
updateBadge();
}
function updateBadge(){
const unread = items.filter(it => !readMap[it.id]).length;
if (unread>0){
badge.style.display='inline-flex';
badge.textContent = unread>99 ? '99+' : unread;
} else {
badge.style.display='none';
}
}
/* =========== Mark read ========= */
function markRead(id){
readMap[id] = Date.now();
localStorage.setItem(STORAGE_KEY, JSON.stringify(readMap));
renderList(currentFilter);
}
function markAllRead(){
items.forEach(i => readMap[i.id] = Date.now());
localStorage.setItem(STORAGE_KEY, JSON.stringify(readMap));
renderList(currentFilter);
}
/* =========== Fetch / Load ========= */
async function loadRemote(){
try{
const res = await fetch(NOTIF_ENDPOINT, {cache:'no-store'});
if (!res.ok) throw new Error('Fetch error');
const json = await res.json();
// json expected as array of {id,type,title,body,url,ts,source}
if (!Array.isArray(json)) throw new Error('Invalid JSON');
handleNewItems(json);
}catch(err){
console.warn('wk-notif: remote load failed', err);
// fallback: jika belum ada item, gunakan static
if (items.length===0) handleNewItems(STATIC_DATA);
}
}
function loadStatic(){
handleNewItems(STATIC_DATA);
}
/* =========== New item handling / toast =========== */
function handleNewItems(arr){
// merge by id, newest first
const map = {};
arr.forEach(a => map[a.id] = a);
items.forEach(i => map[i.id] = i);
items = Object.values(map).sort((a,b) => (b.ts||0) - (a.ts||0));
// show toast for items not seen before
arr.forEach(a => {
if (!readMap[a.id] && !seenIdsBefore.has(a.id)){
showToast(`${a.title}`, a.type, a.url);
}
seenIdsBefore.add(a.id);
});
renderList(currentFilter);
}
/* escaped HTML helper */
function escapeHtml(s){
if (!s) return '';
return String(s).replace(/[&<>"']/g, function(m){ return ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[m]; });
}
/* =========== Toast =========== */
function showToast(title, type, url){
const node = document.createElement('div');
node.style.pointerEvents='auto';
node.style.marginTop='8px';
node.style.padding='12px 14px';
node.style.borderRadius='10px';
node.style.boxShadow='0 8px 20px rgba(0,0,0,.12)';
node.style.background='#fff';
node.style.minWidth='220px';
node.style.cursor='pointer';
node.innerHTML = `<strong style="display:block;margin-bottom:4px">${escapeHtml(title)}</strong><small style="color:#666">${escapeHtml(type)}</small>`;
node.addEventListener('click', function(){
if (url) window.location.href = url;
});
toastRoot.appendChild(node);
setTimeout(()=> node.style.transform='translateX(0)', 10);
setTimeout(()=> {
node.style.transition='opacity .4s, transform .4s';
node.style.opacity='0';
node.style.transform='translateY(8px)';
setTimeout(()=> node.remove(), 450);
}, 6000);
}
/* =========== small helpers & state =========== */
let pollTimer = null;
let seenIdsBefore = new Set();
let currentFilter = 'all';
/* =========== Events =========== */
bellBtn.addEventListener('click', function(){
const expanded = panel.classList.toggle('open');
bellBtn.setAttribute('aria-expanded', expanded ? 'true' : 'false');
panel.setAttribute('aria-hidden', expanded ? 'false' : 'true');
if (expanded) {
// when open, mark visible ones read after a short delay? (optional)
}
});
// click outside to close
document.addEventListener('click', function(e){
if (!document.getElementById('wk-notif').contains(e.target)){
panel.classList.remove('open');
bellBtn.setAttribute('aria-expanded','false');
panel.setAttribute('aria-hidden','true');
}
});
// keyboard close
document.addEventListener('keydown', function(e){
if (e.key==='Escape') {
panel.classList.remove('open');
bellBtn.setAttribute('aria-expanded','false');
panel.setAttribute('aria-hidden','true');
}
});
markAllBtn.addEventListener('click', function(){
markAllRead();
});
openAllBtn.addEventListener('click', function(){
// as contoh, redirect ke halaman notifikasi/premium
window.location.href = '/notifikasi';
});
// simple filter dropdown prompt (ke-sederhanaan supaya tidak perlu UI tambahan)
document.getElementById('wk-filter-btn').addEventListener('click', function(){
const choice = prompt('Filter notifikasi: ketik "all", "premium", "paid", atau "service"', currentFilter);
if (!choice) return;
const v = choice.trim().toLowerCase();
currentFilter = (['all','premium','paid','service'].includes(v)) ? v : 'all';
renderList(currentFilter);
});
/* =========== Init load/poll ========= */
if (MODE==='static'){
loadStatic();
} else {
loadRemote();
pollTimer = setInterval(loadRemote, NOTIF_POLL_INTERVAL);
}
// initial seen ids
items.forEach(i => seenIdsBefore.add(i.id));
// expose small API (opsional)
window.WarkasaNotif = {
push(item){
// item: {id,type,title,body,url,ts}
item.ts = item.ts || Date.now();
handleNewItems([item]);
},
markRead,
markAllRead,
getAll: ()=> items.slice()
};
// initial render after small delay
setTimeout(()=> renderList(currentFilter), 200);
})();
</script>
Penjelasan singkat & langkah integrasi
-
Mode penggunaan
-
MODE = 'static'→ cocok kalau Anda ingin menaruh notifikasi manual (cepat). -
MODE = 'remote'→ cocok kalau Anda punya endpoint JSON (contoh:/api/notifications.json) yang mengembalikan array notifikasi{ id, type, title, body, url, ts, source }.
-
-
Format JSON yang diharapkan (contoh)
Anda bisa sediakan endpoint (serverless, PHP, atau file statis) yang mengembalikan:
[
{ "id":"p1", "type":"premium", "title":"Judul Premium", "body":"Ringkasan", "url":"/premium/1", "ts":1698710400000, "source":"Warkasa" }
]
ts adalah timestamp milidetik.
-
Integrasi ke Blogger
-
Paste CSS ke
<head>template. -
Paste HTML di bagian header (mis. sebelum
</header>). -
Paste JS tepat sebelum
</body>. -
Jika Anda pakai Blogger yang membatasi
fetchke domain, letakkan JSON pada hosting Anda atau sebagaiData Feedvia Google Script/endpoint yang mengizinkan CORS.
-
-
Menambahkan push asli (opsional)
-
Jika mau push browser (FCM Web Push / Firebase Cloud Messaging), modulnya harus ditambahkan terpisah (service worker + server key). Saya bisa bantu siapkan snippet FCM/Service Worker bila Anda mau (jelaskan: punya Firebase project?).
-
-
Kustomisasi cepat
-
Ubah warna badge, ukuran, dan label chip lewat CSS.
-
Tambah ikon per jenis (premium = crown, paid = wallet, service = wrench) dengan menambahkan elemen sebelum judul di
wk-item.
-
Di bawah ini kode yang telah upgrade versi sebelumnya menjadi:
✅ Langsung siap-pasang ke Blogger (Editor HTML / Tema → Edit HTML)
✅ Tanpa prompt — sudah diganti menjadi Dropdown Filter UI
✅ Sangat ringan (tanpa library eksternal)
✅ Responsif, modern, SEO-friendly
✅ Mendukung Premium / Berbayar / Jasa
✅ Bisa fetch data dari JSON (opsional–nanti saya bantu buat feed-nya)
Cukup copy-paste semua kode di bawah.
✅ CODE LONCENG NOTIFIKASI BLOGGER
Tempel CSS di
<head>
<!-- π️ Notifikasi Warkasa1919 - Blogger Ready -->
<style>
.wk-notif{font-family:system-ui;position:relative;display:inline-block;z-index:9999}
.wk-bell-btn{background:transparent;border:none;cursor:pointer;padding:6px;display:flex;align-items:center;border-radius:50%}
.wk-bell-svg{width:26px;height:26px}
.wk-badge{position:absolute;top:0;right:0;min-width:18px;height:18px;padding:0 5px;font-size:12px;line-height:18px;border-radius:50%;background:#e23b3b;color:#fff;display:none;align-items:center;justify-content:center}
.wk-panel{position:absolute;right:0;top:40px;width:320px;max-height:420px;background:#fff;border-radius:12px;box-shadow:0 8px 28px rgba(0,0,0,.15);overflow:hidden;display:none;flex-direction:column;border:1px solid rgba(0,0,0,.06)}
.wk-panel.open{display:flex}
.wk-header{padding:10px 12px;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}
.wk-filter{font-size:13px;padding:6px;border:1px solid #ddd;border-radius:6px;background:#fff;cursor:pointer}
.wk-list{padding:8px;overflow:auto}
.wk-item{padding:8px;border-radius:8px;margin-bottom:6px;cursor:pointer;background:#fafafa}
.wk-item.unread{background:#fff2f2}
.wk-tag{font-size:10px;padding:2px 6px;border-radius:6px;background:#eee;margin-left:6px}
.wk-footer{padding:8px;border-top:1px solid #eee;text-align:right}
@media(max-width:480px){.wk-panel{right:8px;left:8px;width:auto}}
</style>
Tempel HTML pada navbar Blogger (di atas atau sebelum menu)
<div class="wk-notif" id="wkNotif">
<button class="wk-bell-btn" id="wkBell">
<svg class="wk-bell-svg" viewBox="0 0 24 24"><path d="M15 17H9a3 3 0 006 0z"/><path d="M18 16v-5a6 6 0 00-12 0v5l-2 2h16l-2-2z"/></svg>
<span class="wk-badge" id="wkBadge">0</span>
</button>
<div class="wk-panel" id="wkPanel">
<div class="wk-header">
<strong>Notifikasi</strong>
<select id="wkFilter" class="wk-filter">
<option value="all">Semua</option>
<option value="premium">Premium</option>
<option value="paid">Berbayar</option>
<option value="service">Jasa</option>
</select>
</div>
<div class="wk-list" id="wkList">Loading...</div>
<div class="wk-footer">
<button id="wkMarkAll">Tandai Semua Dibaca</button>
</div>
</div>
</div>
Tempel JS sebelum
</body>
<script>
(function(){
const mode="static";
const endpoint="/api/notifications.json";
const badge=document.getElementById("wkBadge");
const bell=document.getElementById("wkBell");
const panel=document.getElementById("wkPanel");
const list=document.getElementById("wkList");
const filter=document.getElementById("wkFilter");
const markAll=document.getElementById("wkMarkAll");
let items=[],read=JSON.parse(localStorage.getItem("notif_read")||"{}");
const staticData=[
{id:"1",type:"premium",title:"Artikel Premium Baru",body:"Cara SEO Bisnis 2025",url:"#",ts:Date.now()},
{id:"2",type:"paid",title:"Diskon Jasa Penulisan",body:"Diskon 30% bulan ini",url:"#",ts:Date.now()},
{id:"3",type:"service",title:"Jasa Pembuatan Website",body:"Paket lengkap mulai 1jt",url:"#",ts:Date.now()}
];
function timeAgo(t){return Math.floor((Date.now()-t)/1000/60)+"m";}
function render(){
let f=filter.value;
let d=items.filter(x=>f==="all"||x.type===f);
list.innerHTML=d.map(x=>`
<div class="wk-item ${!read[x.id]?'unread':''}" onclick="location.href='${x.url}'">
<b>${x.title}</b><span class="wk-tag">${x.type}</span><br>
<small>${x.body}</small><br>
<small>${timeAgo(x.ts)} lalu</small>
</div>`).join("")||"<div style='padding:15px'>Tidak ada notifikasi</div>";
let u=items.filter(x=>!read[x.id]).length;
badge.style.display=u? "flex":"none";
badge.textContent=u;
}
function markAllRead(){
items.forEach(i=>read[i.id]=true);
localStorage.setItem("notif_read",JSON.stringify(read));
render();
}
bell.onclick=()=>panel.classList.toggle("open");
document.addEventListener("click",e=>{
if(!document.getElementById("wkNotif").contains(e.target))panel.classList.remove("open");
});
filter.onchange=render;
markAll.onclick=markAllRead;
function load(){
items=mode==="remote"?[]:staticData;
render();
}
load();
})();
</script>
Cara memasang di Blogger
-
Tema → Edit HTML
-
Tempel CSS di atas
</head> -
Tempel HTML di
<header>atau navbar -
Tempel Script sebelum
</body> -
Save
Hasil fitur
| Fitur | Status |
|---|---|
| Responsive | ✅ |
| Dropdown filter | ✅ |
| Static + siap remote | ✅ |
| Lightweight | ✅ |
| Tekan lonceng → panel keluar | ✅ |
| Mark all read | ✅ |
Berikutnya adalah versi khusus 1-file (CSS + JS) yang langsung dipasang di <head> Blogger dan otomatis muncul di pojok kanan blog, jadi Anda tidak perlu menambah HTML di body.
π Fitur versi ini
✅ Langsung tempel di <head>
✅ UI lonceng fixed floating kanan atas
✅ Dropdown filter Premium / Berbayar / Jasa
✅ Ringan — tanpa library
✅ Mobile responsive
✅ Auto-inject tombol & panel
✅ Kode Tempel di <head> Blogger
Tema → Edit HTML → sebelum
</head>paste ini
<!-- π️ Warkasa1919 Notification Bell (Floating) -->
<style>
#wkBellWrap{position:fixed;top:20px;right:20px;z-index:9999}
.wk-notif{font-family:system-ui;position:relative;display:inline-block}
.wk-bell-btn{background:#fff;border:1px solid #ddd;cursor:pointer;padding:7px;border-radius:50%;box-shadow:0 4px 12px rgba(0,0,0,.15)}
.wk-bell-svg{width:26px;height:26px}
.wk-badge{position:absolute;top:-5px;right:-5px;min-width:18px;height:18px;padding:0 5px;font-size:12px;line-height:18px;border-radius:50%;background:#e23b3b;color:#fff;display:none;align-items:center;justify-content:center}
.wk-panel{position:fixed;top:70px;right:20px;width:320px;max-height:420px;background:#fff;border-radius:12px;box-shadow:0 8px 28px rgba(0,0,0,.18);overflow:hidden;display:none;flex-direction:column;border:1px solid rgba(0,0,0,.06);z-index:9999}
.wk-panel.open{display:flex}
.wk-header{padding:10px 12px;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}
.wk-filter{font-size:13px;padding:6px;border:1px solid #ddd;border-radius:6px;background:#fff;cursor:pointer}
.wk-list{padding:8px;overflow:auto}
.wk-item{padding:8px;border-radius:8px;margin-bottom:6px;cursor:pointer;background:#fafafa}
.wk-item.unread{background:#fff2f2}
.wk-tag{font-size:10px;padding:2px 6px;border-radius:6px;background:#eee;margin-left:6px}
.wk-footer{padding:8px;border-top:1px solid #eee;text-align:right}
@media(max-width:480px){
#wkBellWrap{top:10px;right:10px}
.wk-panel{right:10px;left:10px;width:auto}
}
</style>
<script>
document.addEventListener("DOMContentLoaded",function(){
document.body.insertAdjacentHTML("beforeend",`
<div id="wkBellWrap">
<div class="wk-notif" id="wkNotif">
<button class="wk-bell-btn" id="wkBell">
<svg class="wk-bell-svg" viewBox="0 0 24 24"><path d="M15 17H9a3 3 0 006 0z"/><path d="M18 16v-5a6 6 0 00-12 0v5l-2 2h16l-2-2z"/></svg>
<span class="wk-badge" id="wkBadge">0</span>
</button>
</div>
</div>
<div class="wk-panel" id="wkPanel">
<div class="wk-header">
<strong>Notifikasi</strong>
<select id="wkFilter" class="wk-filter">
<option value="all">Semua</option>
<option value="premium">Premium</option>
<option value="paid">Berbayar</option>
<option value="service">Jasa</option>
</select>
</div>
<div class="wk-list" id="wkList">Loading...</div>
<div class="wk-footer"><button id="wkMarkAll">Tandai Dibaca</button></div>
</div>
`);
const mode="static"; // ganti "remote" jika sudah punya API
const staticData=[
{id:"1",type:"premium",title:"Artikel Premium Baru",body:"Strategi SEO Bisnis 2025",url:"#",ts:Date.now()},
{id:"2",type:"paid",title:"Diskon Jasa Konten",body:"Diskon 30% bulan ini",url:"#",ts:Date.now()},
{id:"3",type:"service",title:"Jasa Website Profesional",body:"Mulai 1jt full setup",url:"#",ts:Date.now()}
];
const badge=document.getElementById("wkBadge");
const bell=document.getElementById("wkBell");
const panel=document.getElementById("wkPanel");
const list=document.getElementById("wkList");
const filter=document.getElementById("wkFilter");
const markAll=document.getElementById("wkMarkAll");
let items=staticData;
let read=JSON.parse(localStorage.getItem("notif_read")||"{}");
function timeAgo(t){return Math.floor((Date.now()-t)/60000)+"m";}
function render(){
let f=filter.value;
let data=items.filter(x=>f==="all"||x.type===f);
list.innerHTML=data.map(x=>`
<div class="wk-item ${!read[x.id]?'unread':''}" onclick="location.href='${x.url}'">
<b>${x.title}</b><span class="wk-tag">${x.type}</span><br>
<small>${x.body}</small><br>
<small>${timeAgo(x.ts)} lalu</small>
</div>`).join("") || "<div style='padding:18px'>Tidak ada notifikasi</div>";
let unread=items.filter(x=>!read[x.id]).length;
badge.style.display=unread?"flex":"none";
badge.textContent=unread;
}
bell.onclick=()=>panel.classList.toggle("open");
document.addEventListener("click",e=>{if(!panel.contains(e.target)&&!bell.contains(e.target))panel.classList.remove("open")});
filter.onchange=render;
markAll.onclick=()=>{items.forEach(i=>read[i.id]=true);localStorage.setItem("notif_read",JSON.stringify(read));render()};
render();
});
</script>
✅ Hasil setelah dipasang
-
Lonceng muncul otomatis pojok kanan atas
-
Klik → panel notifikasi buka
-
Dropdown filter
-
“Tandai Dibaca” berfungsi
Berikutnya adalah versi siap-pasang yang otomatis mengambil posting dari feed Blogger (tanpa Anda edit data manual).
Fitur utama yang saya tambahkan:
-
Ambil feed Blogger via JSONP (bypass CORS, bekerja untuk domain Blogger & custom domain seperti
warkasa1919.com) -
Otomatis memetakan label/post tag ke jenis notifikasi:
premium,paid(berbayar),service(jasa) -
Menampilkan hanya posting yang berlabel sesuai (atau semua jika Anda mau)
-
Dedup, toast untuk item baru, persistensi read/unread di
localStorage -
Polling terjadwal (opsional) — interval default 2 menit (bisa diubah)
-
Siap-paste hanya di
<head>Blogger (tidak perlu edit body)
Langkah pemasangan singkat:
Tema → Edit HTML → sebelum
</head>paste seluruh kode di bawah.Ganti
FEED_URLhanya jika Anda ingin feed khusus; default sudah diarahkan kehttps://warkasa1919.com/feeds/posts/default?alt=json-in-script.Simpan. Lonceng akan otomatis mengambil posting bertag
Premium,Berbayar, danJasadan menampilkannya.
Kode — paste seluruh blok ini ke dalam <head> tema Blogger Anda
<!-- π️ Warkasa1919 - Notifikasi otomatis dari Feed Blogger (JSONP) -->
<style>
/* minimal styling (floating bell + panel) */
#wkBellWrap{position:fixed;top:20px;right:20px;z-index:99999}
.wk-bell-btn{background:#fff;border:1px solid #ddd;cursor:pointer;padding:7px;border-radius:50%;box-shadow:0 6px 18px rgba(0,0,0,.12)}
.wk-bell-svg{width:26px;height:26px}
.wk-badge{position:absolute;top:-5px;right:-5px;min-width:18px;height:18px;padding:0 5px;font-size:12px;line-height:18px;border-radius:50%;background:#e23b3b;color:#fff;display:none;align-items:center;justify-content:center}
.wk-panel{position:fixed;top:70px;right:20px;width:360px;max-height:60vh;background:#fff;border-radius:12px;box-shadow:0 10px 30px rgba(0,0,0,.18);overflow:hidden;display:none;flex-direction:column;border:1px solid rgba(0,0,0,.06);z-index:99999}
.wk-panel.open{display:flex}
.wk-header{padding:10px 12px;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center}
.wk-filter{font-size:13px;padding:6px;border:1px solid #ddd;border-radius:6px;background:#fff;cursor:pointer}
.wk-list{padding:8px;overflow:auto}
.wk-item{padding:10px;border-radius:8px;margin-bottom:8px;cursor:pointer;background:#fafafa}
.wk-item.unread{background:#fff6f6}
.wk-item h4{margin:0;font-size:14px}
.wk-item p{margin:6px 0 0 0;font-size:13px;color:#444}
.wk-tag{font-size:10px;padding:3px 6px;border-radius:6px;background:#eee;margin-left:6px;vertical-align:middle}
.wk-footer{padding:8px;border-top:1px solid #eee;text-align:right}
@media(max-width:480px){
#wkBellWrap{top:10px;right:10px}
.wk-panel{right:10px;left:10px;width:auto}
}
</style>
<script>
/*
Warkasa1919 - Blogger Feed -> Notification (JSONP)
Paste this into <head>. Does JSONP requests to Blogger feed and maps labels:
- 'premium' => premium
- 'berbayar' or 'paid' => paid
- 'jasa' or 'service' => service
If a post has none of those labels, it's ignored (no notification).
Config below: FEED_URL, MAX_RESULTS, POLL_MS
*/
(function(){
/* ================== CONFIG ================== */
const FEED_URL = 'https://warkasa1919.com/feeds/posts/default?alt=json-in-script'; // default - ganti hanya jika perlu
const MAX_RESULTS = 20; // berapa post diambil tiap polling
const POLL_MS = 2 * 60 * 1000; // polling interval (ms) — default 2 menit
const AUTO_SHOW_TYPES = ['premium','paid','service']; // jenis yang ditampilkan
/* ============================================ */
/* inject base DOM (no need edit body) */
document.addEventListener('DOMContentLoaded', function(){
document.body.insertAdjacentHTML('beforeend', `
<div id="wkBellWrap">
<div class="wk-notif" id="wkNotif">
<button class="wk-bell-btn" id="wkBell" aria-label="Notifikasi">
<svg class="wk-bell-svg" viewBox="0 0 24 24"><path d="M15 17H9a3 3 0 006 0z"/><path d="M18 16v-5a6 6 0 00-12 0v5l-2 2h16l-2-2z"/></svg>
<span class="wk-badge" id="wkBadge">0</span>
</button>
</div>
</div>
<div class="wk-panel" id="wkPanel" role="dialog" aria-label="Daftar notifikasi">
<div class="wk-header">
<strong>Notifikasi</strong>
<select id="wkFilter" class="wk-filter" aria-label="Filter notifikasi">
<option value="all">Semua</option>
<option value="premium">Premium</option>
<option value="paid">Berbayar</option>
<option value="service">Jasa</option>
</select>
</div>
<div class="wk-list" id="wkList"><div style="padding:12px">Memuat...</div></div>
<div class="wk-footer"><button id="wkMarkAll">Tandai Semua Dibaca</button></div>
</div>
<div id="wkToastRoot" style="position:fixed;bottom:18px;right:18px;z-index:999999;pointer-events:none"></div>
`);
// DOM refs
const bell = document.getElementById('wkBell');
const panel = document.getElementById('wkPanel');
const list = document.getElementById('wkList');
const badge = document.getElementById('wkBadge');
const filter = document.getElementById('wkFilter');
const markAll = document.getElementById('wkMarkAll');
const toastRoot = document.getElementById('wkToastRoot');
// state
let items = []; // { id, type, title, body, url, ts, source }
let read = JSON.parse(localStorage.getItem('warkasa_notif_read_v1') || '{}');
let seenIds = new Set(JSON.parse(localStorage.getItem('warkasa_notif_seen_v1') || '[]'));
let pollingTimer = null;
let currentFilter = 'all';
/* helper: time ago */
function timeAgo(ts){
const s = Math.floor((Date.now()-ts)/1000);
if (s<60) return s + 's';
if (s<3600) return Math.floor(s/60)+'m';
if (s<86400) return Math.floor(s/3600)+'h';
return Math.floor(s/86400)+'d';
}
/* render list according to filter */
function render(){
const f = filter.value || currentFilter;
currentFilter = f;
const arr = items.filter(it => f==='all' ? true : it.type===f);
if (arr.length===0){
list.innerHTML = '<div style="padding:14px">Tidak ada notifikasi.</div>';
} else {
list.innerHTML = arr.map(it => {
const unread = !read[it.id];
return `<div class="wk-item ${unread?'unread':''}" data-id="${it.id}" role="button" tabindex="0">
<h4>${escapeHtml(it.title)} <span class="wk-tag">${escapeHtml(it.type)}</span></h4>
<p>${escapeHtml(it.body || '')}</p>
<small style="color:#666">${timeAgo(it.ts)} • ${escapeHtml(it.source||'Warkasa')}</small>
</div>`;
}).join('');
// attach click handlers
Array.from(list.querySelectorAll('.wk-item')).forEach(node => {
node.addEventListener('click', function(){
const id = this.getAttribute('data-id');
const it = items.find(x=>x.id===id);
if (!it) return;
markRead(id);
if (it.url) window.open(it.url, '_self');
});
});
}
updateBadge();
}
function updateBadge(){
const unread = items.filter(i=>!read[i.id]).length;
badge.style.display = unread ? 'flex' : 'none';
badge.textContent = unread>99 ? '99+' : unread;
}
function markRead(id){
read[id] = Date.now();
localStorage.setItem('warkasa_notif_read_v1', JSON.stringify(read));
render();
}
function markAllRead(){
items.forEach(i => read[i.id] = Date.now());
localStorage.setItem('warkasa_notif_read_v1', JSON.stringify(read));
render();
}
/* small HTML escape */
function escapeHtml(s){
if (!s) return '';
return String(s).replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));
}
/* toast for new item */
function showToast(title, type, url){
const node = document.createElement('div');
node.style.pointerEvents = 'auto';
node.style.marginTop = '8px';
node.style.padding = '12px 14px';
node.style.borderRadius = '10px';
node.style.boxShadow = '0 8px 20px rgba(0,0,0,.12)';
node.style.background = '#fff';
node.style.minWidth = '220px';
node.style.cursor = 'pointer';
node.innerHTML = `<strong style="display:block">${escapeHtml(title)}</strong><small style="color:#666">${escapeHtml(type)}</small>`;
node.addEventListener('click', ()=> { if (url) window.open(url,'_self'); });
toastRoot.appendChild(node);
setTimeout(()=> {
node.style.transition='opacity .5s, transform .5s';
node.style.opacity='0';
node.style.transform='translateY(8px)';
setTimeout(()=> node.remove(), 550);
}, 5500);
}
/* ================= JSONP fetch & parser for Blogger feed ================= */
// callback name must be global
window.WarkasaFeedCallback = function(json){
try {
const feed = json.feed || {};
const entries = feed.entry || [];
const parsed = entries.map(e => {
// id: prefer post id (g:id in feed) or link href
const id = e.gd$comments ? (e.id && e.id.$t) : (e.id && e.id.$t) || (e.link && (e.link.find(l=>l.rel==='alternate')||{}).href) || (e.title && e.title.$t);
// title
const title = e.title && e.title.$t || 'Tanpa Judul';
// content
const body = (e.summary && e.summary.$t) || (e.content && e.content.$t) || '';
// link
const alt = (e.link && e.link.find(l=>l.rel==='alternate')) || {};
const url = alt.href || '#';
// labels/categories
const cats = (e.category || []).map(c => (c.term || '').toLowerCase());
// detect type by labels
let type = null;
if (cats.some(t => t.includes('premium'))) type = 'premium';
else if (cats.some(t => t.includes('berbayar') || t.includes('paid'))) type = 'paid';
else if (cats.some(t => t.includes('jasa') || t.includes('service'))) type = 'service';
else type = 'other';
// published timestamp
const ts = e.published ? Date.parse(e.published.$t) : Date.now();
return { id: id+'', title, body: stripHtml(body).slice(0,200), url, type, ts, source: (feed.title && feed.title.$t) || 'Warkasa' };
});
// keep only desired types (configurable)
const filtered = parsed.filter(p => AUTO_SHOW_TYPES.includes(p.type));
// merge with existing items (avoid dup)
const map = {};
items.concat(filtered).forEach(it => { map[it.id] = it; });
items = Object.values(map).sort((a,b) => (b.ts||0)-(a.ts||0));
// toast for brand-new unseen items
filtered.forEach(it => {
if (!seenIds.has(it.id) && !read[it.id]) {
showToast(it.title, it.type, it.url);
}
seenIds.add(it.id);
});
// persist seen ids
localStorage.setItem('warkasa_notif_seen_v1', JSON.stringify(Array.from(seenIds)));
// initial render
render();
} catch (err) {
console.warn('WarkasaFeedCallback error', err);
}
};
// helper: strip html tags
function stripHtml(s){ return s ? s.replace(/(<([^>]+)>)/gi, '') : ''; }
// insert JSONP <script> to fetch feed
function fetchFeed(){
// build url: FEED_URL + params
const url = FEED_URL + (FEED_URL.indexOf('?')===-1 ? '?' : '&') + 'max-results=' + MAX_RESULTS + '&callback=WarkasaFeedCallback';
const s = document.createElement('script');
s.src = url + '&_=' + Date.now(); // cache-bust
s.async = true;
s.onerror = function(){ console.warn('Warkasa: feed load error'); s.remove(); };
s.onload = function(){ setTimeout(()=> s.remove(), 5000); };
document.body.appendChild(s);
}
// first load + polling
fetchFeed();
pollingTimer = setInterval(fetchFeed, POLL_MS);
/* ============== UI events ============== */
bell.addEventListener('click', ()=> panel.classList.toggle('open'));
document.addEventListener('click', function(e){
if (!document.getElementById('wkBellWrap').contains(e.target) && !panel.contains(e.target)) {
panel.classList.remove('open');
}
});
filter.addEventListener('change', render);
markAll.addEventListener('click', function(){ items.forEach(i => read[i.id] = Date.now()); localStorage.setItem('warkasa_notif_read_v1', JSON.stringify(read)); render(); });
// keyboard accessibility: close with Esc
document.addEventListener('keydown', function(e){ if (e.key === 'Escape') panel.classList.remove('open'); });
// initial empty state
render();
}); // DOMContentLoaded end
})();
</script>
Penjelasan teknis singkat (penting)
-
Bagaimana pemetaan berkerja: skrip membaca
category/label posting. Jika Anda menandai posting dengan labelPremium,Berbayar(atauPaid), atauJasa(atauService), posting tadi akan masuk ke notifikasi dengan tipe sesuai. Label diperiksa case-insensitive. Jika Anda pakai label lain, sesuaikanAUTO_SHOW_TYPESatau atur label yang sesuai di dashboard Blogger. -
JSONP: saya menggunakan
alt=json-in-script(JSONP) +callback=WarkasaFeedCallback. Blogger menyediakan format ini, jadi tidak perlu backend tambahan dan tidak terblokir CORS. -
Jika feed custom / subfolder: ganti
FEED_URLdi konfigurasi menjadi URL feed Anda (tetap tambahkan?alt=json-in-scriptjika belum ada). -
Interval polling: default 2 menit (POLL_MS). Kurangi/increase sesuai kebutuhan, tapi jangan terlalu sering agar tidak membebani server.
-
Penyimpanan:
localStoragemenyimpan read/seen agar pengguna tidak melihat toast berulang.
Itulah beberapa kode Lonceng Notifikasi Ringan & Responsif di Blogger yang bisa Anda pakai untuk mempercantik tampilan blog. Selamat mencoba!
Diposting oleh:
Warkasa1919
Untuk segala sesuatu ada Masanya, untuk apapun di bawah Langit ada Waktunya.
Anda mungkin menyukai postingan ini
Tunjukkan semua


Rp.25,000,00
Berlangganan Konten Premium Rp.25.000,00 sekali baca atau Rp.120.000,00 per tahun



Lihat Peta
atrbpn

SIGAP Kementerian LHK
OpenStreetMap
Pusat Database BMKG
Google
.jpg)