Shikimori — Комментарии

HTML

<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">

<header><div class="logo">shiki<span>comments</span></div></header>

<div id="id-section">
  <label>Topic ID — ID темы на Shikimori</label>
  <div class="row">
    <input type="number" id="tid" placeholder="например: 3238" min="1">
    <button class="btn" id="go-btn">Показать</button>
  </div>
</div>

<div id="comm-section">
  <div class="topbar">
    <button class="back-btn" id="back-btn">← Сменить ID</button>
    <span class="count-lbl" id="cnt"></span>
  </div>
  <div class="box">
    <div class="box-head">
      <span>Комментарии</span>
      <span class="tid" id="tid-lbl"></span>
    </div>
    <div id="lm-row">
      <button class="lm-btn" id="lm-btn">⬆ Загрузить ещё (более старые)</button>
    </div>
    <div id="clist"></div>
  </div>
</div>

<div id="dbg"></div>

CSS

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
  :root {
    --bg: #0f1117; --surface: #181c27; --surface2: #1e2435;
    --border: #2a3050; --accent: #2e86de;
    --text: #dce4f0; --muted: #6b7a99; --link: #5ba4f5;
    --r: 10px;
  }
  body {
    background: var(--bg); color: var(--text);
    font-family: 'Nunito', sans-serif;
    min-height: 100vh; display: flex; flex-direction: column;
    align-items: center; padding: 36px 16px 60px;
  }
  header { width: 100%; max-width: 720px; margin-bottom: 24px; }
  .logo { font-size: 20px; font-weight: 800; }
  .logo span { color: var(--accent); }

  #id-section {
    width: 100%; max-width: 720px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r); padding: 22px 22px 20px;
    margin-bottom: 20px;
  }
  #id-section label {
    display: block; font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .08em;
    color: var(--muted); margin-bottom: 10px;
  }
  .row { display: flex; gap: 8px; }
  input[type=number] {
    flex: 1; background: var(--bg); border: 1.5px solid var(--border);
    border-radius: 7px; color: var(--text);
    font-family: 'JetBrains Mono', monospace; font-size: 15px;
    padding: 10px 14px; outline: none; transition: border-color .2s;
    -moz-appearance: textfield;
  }
  input[type=number]::-webkit-inner-spin-button { display: none; }
  input[type=number]:focus { border-color: var(--accent); }
  input[type=number].err { border-color: #f06292; }

  .btn {
    background: var(--accent); color: #fff; border: none;
    border-radius: 7px; font-family: 'Nunito', sans-serif;
    font-weight: 700; font-size: 14px; padding: 10px 20px;
    cursor: pointer; white-space: nowrap;
    transition: background .15s, transform .1s;
  }
  .btn:hover { background: #1a6fc4; }
  .btn:active { transform: scale(.97); }
  .btn:disabled { opacity: .45; pointer-events: none; }

 ...

JavaScript

const BASE = 'https://shikimori.one';
const LIMIT = 30;

let topicId = null;
let totalLoaded = 0;
let oldPage = 2;
let noMore = false;

const idSec   = document.getElementById('id-section');
const commSec = document.getElementById('comm-section');
const tidInp  = document.getElementById('tid');
const goBtn   = document.getElementById('go-btn');
const backBtn = document.getElementById('back-btn');
const cntEl   = document.getElementById('cnt');
const tidLbl  = document.getElementById('tid-lbl');
const lmRow   = document.getElementById('lm-row');
const lmBtn   = document.getElementById('lm-btn');
const clist   = document.getElementById('clist');
const dbg     = document.getElementById('dbg');

function log(s) {
  dbg.style.display = 'block';
  dbg.textContent += s + '\n';
}

function ago(d) {
  const s = Math.floor((Date.now() - new Date(d)) / 1000);
  if (s < 5)    return 'только что';
  if (s < 20)   return 'несколько секунд назад';
  if (s < 90)   return 'минуту назад';
  if (s < 150)  return '2 минуты назад';
  if (s < 270)  return '4 минуты назад';
  if (s < 600)  return `${Math.round(s/60)} минут назад`;
  if (s < 2100) return 'полчаса назад';
  if (s < 3600) return `${Math.round(s/60)} минут назад`;
  if (s < 5400) return 'час назад';
  if (s < 7200) return 'полтора часа назад';
  if (s < 10800) return '2 часа назад';
  if (s < 86400) return `${Math.round(s/3600)} часов назад`;
  if (s < 130000) return 'вчера';
  if (s < 220000) return '2 дня назад';
  if (s < 310000) return '3 дня назад';
  if (s < 530000) return '4 дня назад';
  if (s < 700000) return 'неделю назад';
  if (s < 1300000) return 'полторы недели назад';
  if (s < 1900000) return '2 недели назад';
  if (s < 2500000) return '3 недели назад';
  if (s < 4000000) return 'месяц назад';
  if (s < 8000000) return 'несколько месяцев назад';
  if (s < 16000000) return 'полгода назад';
  const y = Math.round(s/31536000);
  if (y === 1) return 'год...