首页 / astro-content-hub / 交互式 Markdown

交互式 Markdown

普通 .md 文件就能承载真正的交互:直接写在 Markdown 里的 <script> 标签会 原样输出并在浏览器中执行。本页是一个纯 .md 文件 —— 打开它的源码可以看到 没有任何组件。下面的每个 demo 都只是 HTML + 一点内联 JS,而且每个 demo 都展示了你在自己文档里要写的源码。

这是给从外部仓库同步的 docs 与 posts 添加交互的推荐方式:直接把 HTML

  • <script> 写进你的 Markdown。无需 hub 侧代码、无需共享库 —— 你的内容 自包含,由 hub 的 PR 审核把关什么能上线。

按钮

点按钮,看数值变化:

计数器 —— 经典示例。点击按钮。

0

源码 —— 一个带 onclick 的按钮,加一小段修改 <span> 的脚本:

<button class="btn btn-secondary" onclick="myCounter('plus')">+</button>
<span id="my-counter">0</span>

<script>
  var n = 0;
  function myCounter(action) {
    if (action === 'plus') n++;
    document.getElementById('my-counter').textContent = String(n);
  }
</script>

标签页

把内容分组到标签页 —— 点击切换面板:

包管理器 —— 点击标签。

npm install my-package
pnpm add my-package
yarn add my-package

模式 —— 标签栏加面板,通过 class 切换:

<div class="demo-tabs">
  <button class="demo-tab is-active" onclick="demoTabs('npm')">npm</button>
  <button class="demo-tab" onclick="demoTabs('pnpm')">pnpm</button>
</div>
<div class="demo-panel is-active" data-panel="npm">…</div>
<div class="demo-panel" data-panel="pnpm">…</div>

<script>
  function demoTabs(name) {
    document.querySelectorAll('.demo-tab').forEach(function (t) {
      t.classList.toggle('is-active', t.getAttribute('data-tab') === name);
    });
    document.querySelectorAll('.demo-panel').forEach(function (p) {
      p.classList.toggle('is-active', p.getAttribute('data-panel') === name);
    });
  }
</script>

图标

内联 SVG —— 无需图标库。静态图标原样渲染;按钮可在两个图标间切换:

切换图标 —— 点击在太阳与月亮间切换。

静态图标(内联 SVG,直接渲染)。

由 JS 函数生成的图表

写一个生成数据的函数,画成 SVG —— 无需图表库。点 Roll 重新生成 (同样的 seed → 同样的形状),或切换折线/柱状:

随机游走 —— 一个 JS 函数产生数据点。

0 个点

源码 —— 生成器是一个你能读懂的普通函数:

<svg id="demo-chart" class="demo-chart" viewBox="0 0 600 220"></svg>
<button onclick="demoChartRoll()">Roll</button>

<script>
  // 确定性 PRNG,同样的 seed 画出同样的图表。
  function mulberry32(a) {
    return function () {
      a |= 0; a = (a + 0x6d2b79f5) | 0;
      var t = Math.imul(a ^ (a >>> 15), 1 | a);
      t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
      return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
    };
  }
  var demoChartSeed = 7;
  var demoChartAsBar = false;
  function demoChartData() {
    var r = mulberry32(demoChartSeed);
    var v = 50, out = [];
    for (var i = 0; i < 24; i++) {
      v = Math.max(5, Math.min(95, v + (r() - 0.5) * 24));
      out.push(v);
    }
    return out;
  }
  function demoChartDraw() {
    var svg = document.getElementById('demo-chart');
    var values = demoChartData();
    var asBar = document.getElementById('demo-chart-bar').checked;
    var W = 600, H = 220, PAD = 8;
    var x = function (i) { return PAD + (i / 23) * (W - PAD * 2); };
    var y = function (v) { return H - PAD - (v / 100) * (H - PAD * 2); };
    var inner = '';
    if (!asBar) {
      var pts = values.map(function (p, i) {
        return x(i).toFixed(1) + ',' + y(p).toFixed(1);
      }).join(' ');
      inner += '<polyline points="' + pts + '" fill="none" stroke="var(--color-accent)" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round"/>';
      inner += values.map(function (p, i) {
        return '<circle cx="' + x(i).toFixed(1) + '" cy="' + y(p).toFixed(1) + '" r="2.5" fill="var(--color-accent)"/>';
      }).join('');
    } else {
      var bw = ((W - PAD * 2) / 24) * 0.7;
      inner = values.map(function (p, i) {
        var hh = Math.max(2, H - PAD - y(p));
        return '<rect x="' + (x(i) - bw / 2).toFixed(1) + '" y="' + (H - PAD - hh).toFixed(1) +
               '" width="' + bw.toFixed(1) + '" height="' + hh.toFixed(1) + '" rx="1.5" fill="var(--color-accent)"/>';
      }).join('');
    }
    svg.innerHTML = inner;
    document.getElementById('demo-chart-count').textContent = values.length + ' 个点';
  }
  function demoChartRoll() { demoChartSeed = Math.floor(Math.random() * 1e9); demoChartDraw(); }
  // 加载时绘制。
  demoChartDraw();
</script>

最佳实践

  • 保持自包含。 所有样式与脚本都在这个 .md 文件里。给 class 与 id 加上 demo-(或产品名)前缀,这样你的页面永远不会与 hub 的全局样式或 另一个页面冲突。
  • 无外部请求。 这里的交互是纯 DOM + 数学。避免在内容中使用 fetch/XMLHttpRequesteval 与 cookie 访问 —— hub 的审核会把关, 这也让你的页面更快更安全。
  • 末尾放一个 <script> 定义所有函数,比散落的内联处理器更好读, 也让页面更整洁。
  • 复用 hub 的 CSS 类(btnbtn-primarybtn-secondary),让你的按钮 与站点一致;只为自定义部分(卡片、标签页、图表)加一个小的 <style> 块。