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

遇到的问题

上一条提出这个问题的貌似还是23年3月。

简单来说,就是,在obsidian中添加html格式的跳转锚点是无效的(但是之前用过的所有md编辑器都支持这一点)。虽然自然是可以用大标题跳转但是还是让我非常不快,而且总不能在跳转的地方全都加一个大标题吧。写非常大段的说明文档时这一点非常伤。

上一次提出这个问题的帖子也是用跳转标题解决的,想问一下时隔一年多有没有更好的解决方案……官方看上去是已经不理这个问题了。

类似的似乎是用块链接,然后跳转到对应的块

是像之前加上^tag 这样的方式吗,我去试试看。但是说实在话跳转本身需要加个块本身也让人多少感觉有点不适应(手头有很多文档都要改成那样了挺烦的)

自己顶一顶,下午写文档的时候仍然觉得很痛苦,真的没有其他解决方法了嘛

如果使用块链接

你可以想写一段话然后框住一部分使用 copy block link就好了,这是一个还比较好用的插件,有类似能力的还有Outliner.md,这两个都是优化块链接的体验。

你对跳转有什么很高的需求么,为什么说明性文档不能使用标题的形式;而且一共有6种标题,大标题确实可能影响排版但是可以用小一点的?

1 个赞

主要还是,原本他就只是在文章中出现的一个小字眼,比方说一个名字,文章中出现了,总不能把这名字写成一个标题吧?但是我又希望能在文章开头点击这个名字他就能跳转到这个地方。
其他的还有挺多需求,然后为了追求最大md兼容性我是最希望使用html跳转的,但是结果没想到居然用不了。看有些人说是用自带的引用,但是我是真不喜欢我的笔记和软件强绑定……那不就和思源是一个套路了么?

好的,我之后去用一下。主要是我写的文档一般不只是给自己看,所以确实想希望别人用自己的软件打开也能跳转,这样

如果需求比较大的话,还是以需求为主,你觉得其他md编辑器可以实现需求就用其他的,Obsidian毕竟也不是全能

比较接近的是outliner.md用注释来包裹文本, 要是想用html标签来包裹, 可能得照着outliner.md源码自己写一个插件出来了, 而且还得额外处理ob对链接的行为.

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

下文中:
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 个赞