/** * 天涯神贴 · 静态网站交互逻辑 * 依赖:data.js 中提供的 window.SHEN_TIE */ (function () { 'use strict'; var DATA = window.SHEN_TIE || []; var STORAGE_KEY = 'tianya-favs'; /* ---------- 工具函数 ---------- */ function $(sel, ctx) { return (ctx || document).querySelector(sel); } function $all(sel, ctx) { return Array.prototype.slice.call((ctx || document).querySelectorAll(sel)); } function escapeHtml(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function loadFavs() { try { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || []; } catch (e) { return []; } } function saveFavs(list) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(list)); } catch (e) { /* 隐私模式忽略 */ } } /* ---------- 全局状态 ---------- */ var state = { section: '全部', keyword: '', sort: 'hot', // hot | year-asc | year-desc favs: loadFavs(), onlyFav: false, }; /* ---------- 派生数据 ---------- */ function sections() { var set = {}; DATA.forEach(function (t) { set[t.section] = true; }); return ['全部'].concat(Object.keys(set)); } function filtered() { var kw = state.keyword.trim().toLowerCase(); var list = DATA.filter(function (t) { var okSection = state.section === '全部' || t.section === state.section; var okFav = !state.onlyFav || state.favs.indexOf(t.id) > -1; var okKw = true; if (kw) { var hay = [t.title, t.author, t.authorAlias, t.summary, t.section] .concat(t.tags || []) .join(' ') .toLowerCase(); okKw = hay.indexOf(kw) > -1; } return okSection && okFav && okKw; }); if (state.sort === 'hot') { list.sort(function (a, b) { return b.hot - a.hot; }); } else if (state.sort === 'year-asc') { list.sort(function (a, b) { return a.year - b.year; }); } else if (state.sort === 'year-desc') { list.sort(function (a, b) { return b.year - a.year; }); } return list; } /* ---------- 渲染:分类标签 ---------- */ function renderSections() { var box = $('#sections'); box.innerHTML = sections() .map(function (s) { var active = s === state.section ? ' active' : ''; return ( '' ); }) .join(''); } /* ---------- 渲染:卡片列表 ---------- */ function renderCards() { var box = $('#cards'); var list = filtered(); if (!list.length) { box.innerHTML = '
没有找到相关神贴,换个关键词或分类试试。
'; $('#count').textContent = '0 篇'; return; } var html = list .map(function (t) { var isFav = state.favs.indexOf(t.id) > -1; var tagsHtml = (t.tags || []) .map(function (tag) { return '#' + escapeHtml(tag) + ''; }) .join(''); var verifyBadge = t.verified ? '已应验' : ''; return ( '
' + '
' + '' + escapeHtml(t.section) + '' + '' + escapeHtml(String(t.year)) + '' + '
' + '

' + escapeHtml(t.title) + '

' + '
' + '' + escapeHtml((t.authorAlias || t.author).slice(0, 1)) + '' + '' + escapeHtml(t.authorAlias || t.author) + '' + '@' + escapeHtml(t.author) + '' + verifyBadge + '
' + '

' + escapeHtml(t.summary) + '

' + '
' + tagsHtml + '
' + '
' + '🔥 ' + escapeHtml(String(t.hot)) + '' + '' + '展开阅读 →' + '
' + '
' ); }) .join(''); box.innerHTML = html; $('#count').textContent = list.length + ' 篇'; } /* ---------- 渲染:详情弹窗 ---------- */ function openDetail(id) { var t = DATA.filter(function (x) { return x.id === id; })[0]; if (!t) return; var paras = (t.content || []) .map(function (p) { return '

' + escapeHtml(p) + '

'; }) .join(''); var tagsHtml = (t.tags || []) .map(function (tag) { return '#' + escapeHtml(tag) + ''; }) .join(''); var isFav = state.favs.indexOf(t.id) > -1; var html = ''; var old = $('#modalMask'); if (old) old.remove(); document.body.insertAdjacentHTML('beforeend', html); document.body.style.overflow = 'hidden'; } function closeDetail() { var mask = $('#modalMask'); if (mask) mask.remove(); document.body.style.overflow = ''; } /* ---------- 收藏切换 ---------- */ function toggleFav(id) { var idx = state.favs.indexOf(id); if (idx > -1) { state.favs.splice(idx, 1); } else { state.favs.push(id); } saveFavs(state.favs); updateFavCount(); renderCards(); // 弹窗内的按钮也同步 var modalBtn = $('#modalMask .fav-btn'); if (modalBtn) { var on = state.favs.indexOf(id) > -1; modalBtn.classList.toggle('on', on); modalBtn.textContent = on ? '★ 已收藏' : '☆ 收藏'; } } function updateFavCount() { var el = $('#favCount'); if (el) el.textContent = state.favs.length; } /* ---------- 事件绑定 ---------- */ function bindEvents() { // 分类 $('#sections').addEventListener('click', function (e) { var btn = e.target.closest('[data-section]'); if (!btn) return; state.section = btn.getAttribute('data-section'); renderSections(); renderCards(); }); // 搜索 var searchInput = $('#search'); var timer = null; searchInput.addEventListener('input', function (e) { clearTimeout(timer); var val = e.target.value; timer = setTimeout(function () { state.keyword = val; renderCards(); }, 150); }); // 排序 $('#sort').addEventListener('change', function (e) { state.sort = e.target.value; renderCards(); }); // 只看收藏 $('#onlyFav').addEventListener('click', function (e) { state.onlyFav = !state.onlyFav; e.currentTarget.classList.toggle('on', state.onlyFav); renderCards(); }); // 卡片区域:点击卡片展开 / 收藏 $('#cards').addEventListener('click', function (e) { var favBtn = e.target.closest('[data-fav]'); if (favBtn) { e.stopPropagation(); toggleFav(favBtn.getAttribute('data-fav')); return; } var card = e.target.closest('.card'); if (card) { openDetail(card.getAttribute('data-id')); } }); // 弹窗关闭(事件委托,因弹窗是后插入的) document.addEventListener('click', function (e) { if (e.target.id === 'modalMask' || e.target.id === 'modalClose') { closeDetail(); return; } var favBtn = e.target.closest('#modalMask .fav-btn'); if (favBtn) { toggleFav(favBtn.getAttribute('data-fav')); } }); // ESC 关闭弹窗 document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeDetail(); }); // 返回顶部 $('#backTop').addEventListener('click', function () { window.scrollTo({ top: 0, behavior: 'smooth' }); }); window.addEventListener('scroll', function () { var btn = $('#backTop'); btn.classList.toggle('show', window.scrollY > 400); }); } /* ---------- 启动 ---------- */ function init() { renderSections(); renderCards(); updateFavCount(); bindEvents(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();