























在不重写 Typecho 编辑器的前提下,给前台文章内容增加更丰富的 Markdown 渲染能力:
<figure><img><figcaption> 1分两层:
alt/title 解析问题,确保输出 HTML 属性正确。footer.php + style.css)figure,把 ruby/spoiler 文本标记转换成结构化标签。目录:/var/Utils/HyperDown.php
Typecho 原有1.3.0版本里,图片回调会把 title 字符串错误塞进 alt,导致:
alt 异常(例如出现 title="..." 残片)title/alt修正代码(约487行):
// image
$text = preg_replace_callback(
"/!\\[((?:[^\\]]|\\\\\\]|\\\\\\[)*?)\\]\\(((?:[^\\)]|\\\\\\)|\\\\\\()+?)\\)/",
function ($matches) {
$escaped = htmlspecialchars($this->escapeBracket($matches[1]));
$url = $this->escapeBracket($matches[2]);
[$url, $title] = $this->cleanUrl($url, true);
$title = empty($title) ? '' : " title=\"{$title}\"";
return $this->makeHolder(
"<img src=\"{$url}\" alt=\"{$escaped}\"{$title}>"
);
},
$text
);修正结果:
预览:

会得到正确的 img alt="Logo" title="Markdown",供后续 figure 逻辑使用。
目录:/usr/themes/HansJack/footer.php
<figure><img ...><figcaption>Logo</figcaption></figure>[](https://commonmark.org/)<figure><a ...><img ...></a><figcaption>Markdown</figcaption></figure>代码:
(function () {
var content = document.querySelector(".hj-article-content");
if (!content) return;
function hasOnlyImageParagraph(p, carrier) {
var nodes = p.childNodes || [];
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
if (!n) continue;
if (n.nodeType === 3 && String(n.textContent || "").trim() !== "") return false;
if (n.nodeType === 1 && n !== carrier) return false;
}
return true;
}
var imgs = Array.prototype.slice.call(content.querySelectorAll("img"));
imgs.forEach(function (img) {
if (!img || !img.parentNode) return;
if (img.closest && img.closest("figure")) return;
var carrier = img;
var p = img.parentNode;
if (p && p.tagName === "A") {
carrier = p;
p = p.parentNode;
}
if (!p || p.tagName !== "P" || !hasOnlyImageParagraph(p, carrier)) return;
var caption = String(img.getAttribute("title") || "").trim();
if (!caption) caption = String(img.getAttribute("alt") || "").trim();
img.setAttribute("tabindex", "0");
img.setAttribute("loading", "lazy");
if (img.hasAttribute("title")) img.removeAttribute("title");
if (carrier.tagName === "A") {
carrier.setAttribute("target", "_blank");
carrier.setAttribute("rel", "noopener noreferrer");
}
var figure = document.createElement("figure");
figure.appendChild(carrier);
if (caption) {
var figcaption = document.createElement("figcaption");
figcaption.textContent = caption;
figure.appendChild(figcaption);
}
p.parentNode.replaceChild(figure, p);
});
})();{base:ruby1|ruby2}目录:/usr/themes/主题名/footer.php
语法:
{正反対な君と僕:相反的你和我}渲染:
<ruby>正反対な君と僕<rt>相反的你和我</rt></ruby>预览:
正反対な君と僕
逻辑:
code/pre/a/script/style/textarea/ruby/rt)。{...} 且内部包含 :。: 左侧为 base,右侧按 | 切分成多个 rt。<ruby><rt> 节点替换原文本片段。构建:
function buildRuby(baseText, annotations) {
var ruby = document.createElement("ruby");
ruby.appendChild(document.createTextNode(baseText));
for (var i = 0; i < annotations.length; i++) {
var rt = document.createElement("rt");
rt.textContent = annotations[i];
ruby.appendChild(rt);
}
return ruby;
}!!隐藏内容!!文件:/usr/themes/主题名/footer.php + style.css
语法:
好喜欢 !!蠢蠢欲动!!渲染结果:
<span class="hj-term hj-term-spoiler spoiler">蠢蠢欲动</span>好喜欢 蠢蠢欲动
构建:
function buildSpoiler(text) {
var span = document.createElement("span");
span.className = "hj-term hj-term-spoiler spoiler";
span.setAttribute("tabindex", "0");
span.textContent = text;
return span;
}CSS:
.hj-article-content .hj-term-spoiler,
.hj-article-content .spoiler {
cursor: pointer;
filter: blur(var(--hj-spoiler-blur));
transition: filter var(--hj-spoiler-transition) ease;
}
.hj-article-content .hj-term-spoiler:hover,
.hj-article-content .hj-term-spoiler:focus-visible,
.hj-article-content .hj-term-spoiler.is-open,
.hj-article-content .spoiler:hover,
.hj-article-content .spoiler:focus-visible,
.hj-article-content .spoiler.is-open {
filter: blur(0);
}变量:
.hj-article-content {
--hj-spoiler-blur: 3.5px;
--hj-spoiler-transition: 0.24s;
}文件:/usr/themes/主题名/footer.php + style.css
写法:
[Boostnote](https://github.com/BoostIO/Boostnote "This is Boostnote's repository")逻辑:
a[title]var titleLinks = content.querySelectorAll("a[title]");
for (var i = 0; i < titleLinks.length; i++) {
var a = titleLinks[i];
var tip = String(a.getAttribute("title") || "").trim();
if (!tip) continue;
a.classList.add("hj-term-tooltip");
a.setAttribute("data-hj-term", tip);
a.removeAttribute("title");
}CSS:
.hj-article-content .hj-term-tooltip {
display: inline-block;
cursor: help;
text-decoration-line: underline;
text-decoration-style: dotted;
}
.hj-article-content .hj-term-tooltip::before {
content: attr(data-hj-term);
position: absolute;
left: 50%;
bottom: calc(100% - 0.02rem);
transform: translate(-50%, 0.04rem);
opacity: 0;
visibility: hidden;
transition: opacity 0.14s ease, transform 0.14s ease, visibility 0.14s ease;
}
.hj-article-content .hj-term-tooltip:hover::before,
.hj-article-content .hj-term-tooltip:focus-visible::before,
.hj-article-content .hj-term-tooltip.is-open::before {
opacity: 1;
visibility: visible;
transform: translate(-50%, 0);
}注意:
a::after,tooltip 也用 ::after 会冲突;::before,可与外链图标共存。文件:blog/usr/themes/HansJack/style.css
.hj-article-content figure {
display: block;
width: fit-content;
max-width: 100%;
margin: 1rem 0;
}
.hj-article-content figure img {
display: block;
max-width: 100%;
height: auto;
margin: 0;
}
.hj-article-content figcaption {
margin-top: 0.45rem;
font-size: 0.88rem;
text-align: center;
}.hj-article-content ruby {
ruby-position: over;
}
.hj-article-content ruby rt {
font-size: 0.66em;
line-height: 1.05;
}alt/title 输出。a::after 冲突;tooltip 建议用 ::before。code/pre。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。