有可以让obsidian支持html格式的锚点跳转的方式/插件吗

现在我对这问题有了些新理解

下文中:
blockref” 指 Obsidian 里对普通段落的链接语法 ^xxx-id
html 锚点” 指在 md 里写 html 语法 <a name="xxx" /> 或者 <div id="yyy"></div>

个人觉得, 其实 blockref 就是 html 锚点的替代品, 这俩方案还有几个相似点:

  • 使用场景类似: blockref 和 html 锚点, 对于普通段落都默认不添加, 而仅当用户需要定位到该段落时, 才需要手动添加
    • 即都是预设只有少量段落才需要这标记; 而绝大多数段落都是无需拥有名字的
  • 都是 默认隐藏: blockref 在 Ob 阅读视图里, 以及 Ob 输出 pdf 后, 也是默认不显示

blockref 在大部分情况下是够用的, 唯一的问题是跟别的工具协作时:

  • Ob 不认 html 锚点
  • 其他 md 编辑器不认 blockref

所以只要用最小代价把这差异消除就行了,
目前想到了这几个方案, 楼主按照自己的实际需求取舍


A 在导出时做兼容

Ob 里只用 blockref, 把兼容工作推迟到 “导出时”

大多数导出工具, 都有允许用户自己定制一些细节; 即使没让定制, 也可以在字符串层次做预处理和后处理

简单说就是在导出之前, 检测到段落最后有个 ^xxx 就给替换成 <a name="xxx" />

注: Obsidian md 转 CommonMark md, 也可以看作导出


B 两种锚点语法同时写

编辑笔记时, 同一段落里 blockref 和 html anchor 全给写上, 如

# 原先写法是
段落正文. 段落正文. 段落正文. 段落正文 ^xxx

# 全都自动替换成
<a name="xxx" /> <div id="xxx"></div> 段落正文. 段落正文. 段落正文. 段落正文 ^xxx

各家软件乐意认哪个都随便, 反正都指向同一个地方

少量的手动写就完了, 大量的考虑自动化实时替换


C 修改链接点击操作

思路是点击链接时, 先找是否存在 html 风格锚点, 找到就跳转过去, 找不到就走默认的点击行为

原理借鉴这篇帖子 在新窗口打开内链的快捷键是什么? - 疑问解答

点击展开测试笔记

### 链接源头

1
[[#^testanchor1]] 双链语法

2
[[#^testanchor2|别名]] 双链语法, 带别名

3
[标准 md 语法 指向页内锚点](#testanchor3)


TODO 跨笔记



### 锚点 (链接目标)


**1 链接是 最简Ob双链写法**

普通段落, Ob 风格块链接目标  ^testanchor1

自闭合 name anchor 做为锚点 <a name="testanchor1" /> 其内容形如 `<a name="xxx" />`


**2 链接里 Ob双链别名写法**

普通段落, Ob 风格块链接目标  ^testanchor2

自闭合 name anchor 做为锚点 <a name="testanchor2" /> 其内容形如 `<a name="xxx" />`


**3 链接是 标准 md 写法**

普通段落, Ob 风格块链接目标  ^testanchor3

自闭合 name anchor 做为锚点 <a name="testanchor3" /> 其内容形如 `<a name="xxx" />`

TODO 跨笔记

上面文档贴 Ob 里, 点击这些链接后 1 2 会成功, 3 会无效


然后 Ctrl+Shift+i 打开控制台, 粘贴如下脚本

展开脚本
async function locate_anchor_name(token, note_path) {
	const anchor = `<a name="${token.startsWith('^') ? token.slice(1, ) : token}" />`;
	console.log(`locating name ${anchor}`);
	// debugger;
	const current_content = await app.vault.read(note_path);
	console.log(`current_note ${note_path}; current_content ${current_content.slice(0, 50)}`);
	const paragraphs = current_content.split('\n');
	for (let line = 0; line < paragraphs.length; line += 1) {
		const pos = paragraphs[line].indexOf(anchor); 
		if (pos >= 0) {
			return { 
				from: {line: line, ch: pos}, 
				to: {line: line, ch: pos + anchor.length} 
			};
		}
	}
	return null;
}


const originTriggerClickableToken = app.workspace.activeLeaf.view.editMode.triggerClickableToken.bind(app.workspace.activeLeaf.view.editMode);


app.workspace.activeLeaf.view.editMode.triggerClickableToken= async (e, t) => {

	console.log('current e=', e);
	console.log('current t=', t);

	if (e && e.type==='internal-link' && e.text.startsWith('#')) {

		const current_note = app.workspace.getActiveFile();
		const token = e.text.slice(1, );
		const selection = await locate_anchor_name(token, current_note);

		if (selection) {
			console.log('loc', selection);
			const editor = app.workspace.getActiveFileView().editor;
			// console.log(editor);
			editor.setCursor({ line: selection.from.line, ch: 0});
			editor.transaction({
				selection: selection,
			});
			editor.scrollIntoView(selection, true);
		} else {
			// 调用原有函数
			originTriggerClickableToken(e, t);
		}
	}
}

此后再次点击测试链接, 则 1 2 3 都会成功, 且有歧义时会优先寻找 html 锚点

注: 仅支持实时阅览模式, 仅支持同笔记里导航,
脚本我写的不好, 只是提供一种思路, 这想要写完善了, 得考虑许多细节
但是这思路可以顺带解决小标题的 slugify 以及 urlencoding 之类问题

2 个赞