[js] 批量折叠和展开标题,标题批量转换不再是梦

缘起:

看到有些小伙伴们希望有标题批量折叠或展开功能(比如 思源能否添加折叠或展开指定层级标题下内容的功能?怎样快速调整大纲级别? ),这个功能有时候确实挺有用的,比如批量转换等。

今天试着研究了下,于是整了一个 js 代码片段。

该代码主要有以下几个功能:

  1. alt + 点击标题前的箭头按钮,折叠/展开所有同级标题
  2. ctrl/meta + alt + 点击标题前的箭头按钮,折叠/展开所有标题
  3. 选择情况下,仅折叠/展开已选择的部分的标题

效果如下:

r98.gif

代码

https://gitee.com/wish163/mysoft/blob/main/%E6%80%9D%E6%BA%90/%E5%BF%AB%E9%80%9F%E6%8A%98%E5%8F%A0%E5%92%8C%E5%B1%95%E5%BC%80%E6%A0%87%E9%A2%98.js

思源的坑:

这里顺便吐槽下思源获取当前文档的坑,,看到很多小伙伴们使用下面的方法获取的

document.querySelector('[data-type="wnd"].layout__wnd--active .protyle:not(.fn__none)')

这样获取乍一看没问题,也能满足绝大多数场景,包括分屏。

但这种方法不适合思源刚刚启动或刚刚刷新后的情况,因为刚刚重启或刷新后的页面中并不存在 layout__wnd--active 这个类,导致这种方法失效。

那如果换成这样呢?

document.querySelector('[data-type="wnd"].layout__wnd--active .protyle:not(.fn__none)')||document.querySelector('[data-type="wnd"] .protyle:not(.fn__none)');

这种方式,当刚刚刷新或重启情况下,默认取第一个分屏的文档。

但,这在分屏情况下,不一定准确,用户正在操作的可能不是第一个分屏。

实时上,在分屏情况下是无解的,因为用户尚未操作,无法知道哪个文档是当前文档,换句话说不存在当前文档。这在逻辑上是没错的,从这个角度看也不算思源的坑。

所以,分屏情况下有没有准确获取用户操作的当前文档的办法呢?

我觉得这要具体问题具体分析了。

  1. 如果你确定你的操作被执行时,文档一定是被激活了的,可以用上面的方法实现。
  2. 如果有鼠标事件的操作,可以用 document.elementFromPoint(mouseX, mouseY) 方法实现,具体可以参考本代码中的获取方法。
  3. 如果 1 和 2 都无法确保,可以用 mouseover 或 mousemove 事件写入一个全局变量来实现。

当然,或许还有更好的方法吧,请各位大佬们不吝赐教!!!

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    22776 引用 • 91434 回帖 • 3 关注
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    79 引用 • 438 回帖
1 操作
wilsons 在 2024-12-05 17:50:35 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • 优秀,

    获取当前文档,我觉得可以根据标题的 id,查询属于哪个文档

    1 回复
  • wilsons

    感谢大佬!

    我明白了,你是针对我这个功能说的吧,我这个功能确实可以。

    但吐槽这里主要说的是通用情况,非特定场景下的获取方法。

    我这个功能没用接口查询,主要觉得 dom 查询可能比接口查询快些吧。

    1 回复
    1 操作
    wilsons 在 2024-12-05 18:49:18 更新了该回帖
  • Achuan-2 1

    就是鼠标点击的块呀,获取其 id,查询属于哪个文档

    另外,你这个代码片段目前不适用动态加载的文档标题折叠和展开,可能需要用 api 获取当前文档的所有标题,而不是用 js 前端查询

    1 回复
  • wilsons

    嗯,感谢大佬,明白了,我刚刚更新了回复帖子,才看到你回复,我这个功能确实可以,吐槽部分主要说的是通用情况,比如没有鼠标事件或不知块 id 等。

    1 回复
  • 哦哦好的

  • wilsons 1

    关于获取当前文档,补充一点:

    1. 如果能拿到任何块 ID 或任何 dom 元素的情况,可以尝试用 document.querySelector('[data-node-id="your block id"]').closest('.protyle')或 element.closest('.protyle')获取。

    感谢 @Achuan-2 的指导和提醒!

    1 回复
  • 如果是插件开发的话,回调函数如果有 protyle 参数,可以用 protyle.block.rootID 获取当前操作文档 ID

    1 回复
  • wilsons 1

    学习了,还没研究过插件开发,页面刚刷新完也有吗?如果分屏情况,且用户未在任何窗口操作时,它怎么知道哪个文档是当前文档的呢?

    1 回复
  • Adaxi

    很有用,平时就很喜欢折叠标题,方便编辑的时候看大纲

  • Achuan-2 1

    插件是比如在块标菜单、工具栏的按钮的回调函数会返回 protyle,其实不适用于你说的没有任何窗口操作的

    也有可能是我不知道,我也是最近才入门插件开发的

    1 操作
    Achuan-2 在 2024-12-05 22:48:58 更新了该回帖
  • 帮了大忙了 对我这种超大单文档用法

  • Wetoria 1 1 评论

    emmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm

    拿去玩

    let dom = clickedDom
    while (dom || (dom.classList && !dom.classList.contains('protyle-content'))) {
      dom = dom.parentElement
    }
    let docId = null
    if (dom) {
      const protyleTop = dom.firstElementChild
      docId = protyleTop.firstElementChild.dataset.nodeId
    }
    
    拿 dom,这种就行,最后的 dom 就是 protyle
    Wetoria
  • Wetoria 1 1 评论

    上面的是针对思源渲染的文档,如果是自定义渲染的 block,sql 版本:

    const sqlStmt = `sql root_id from blocks where id = '${clickedDomNodeId}'`
    const res = await reqeust(`http://localhost:6806/api/query/sql`, {
      stmt: sqlStmt,
    })
    const docId = res[0]?.root_id
    
    没测试,自己拿去改吧。
    Wetoria
  • wilsons

    @Wetoria @Achuan-2 感谢帮助!

    其实我想寻找的是一种通用的方法,针对我这个功能的方法确实很多。

    之所以提这个问题,是因为之前总结了一些方法,但在不同的场景下还是需要调整的。就像前面说的,在刚刷新完的页面,如果用户完全没有操作,甚至没有鼠标移入,其实是没有当前文档的。

    不过,刚才重新测试发现,无论刷新还是重启,无论分屏还是不分屏,.layout__wnd--active 又都有了,这就很奇怪了,我保证不是因为看错或操作错误,因为我一直是刷新后在控制台用 document.querySelectorAll('[data-type="wnd"]')查询的,而且程序确实也因为这个问题报错过,而且碰到过不只一次。

    这就有点玄学了,算了,先不管了,以后碰到问题再说吧。

    1 回复
  • 如果用户本身就没有点击/鼠标移入过文档,那么本身就不应该算作一次选择了文档。

    不过写代码本身就是根据场景不同选择适用的代码,想一个代码解决所有场景,这属于比较理想的情况了。

    我就举一个很简单的场景,假如我左右分屏两个文档,然后刷新了思源,这个时候应该算左边的是当前文档,还是算右边的呢?

    而针对这类场景,queryAll(':not(.fn__none)'),找到所有的文档,如果有多个,则提示用户选择。

    1 回复
  • wilsons

    再次感谢大佬,受教了!

    确实是这个道理,这种情况下,即使有 layout__wnd--active,也未必是用户想选择的内容,确实有时候需要让用户选择,除非目标特征非常明显,且两边没有重复的情况。

    总之,如果有鼠标操作的利用鼠标事件最好了,仅有激活文档的情况,就要看场景了,一般情况是可以用激活文档的。想一个方法通用确实较是理想情况,即使能实现,反而会增加很多兼容判断,还不如针对不同场景用不同方法来的实惠。

    1 回复
  • wilsons 1

    买一赠一啦!

    借此贴分享下状态栏可拖动代码。

    功能介绍:

    让状态栏可拖动,双击可还原。

    我真的需要吗?

    让状态栏浮动后,你有没有发现,有时会遮挡住某些元素,有时挺烦人的。

    如果你曾有过这个烦恼,下面这个代码可以让你把状态栏在需要时拖动到别的地方,双击可还原。

    https://ld246.com/member/wilsons/breezemoons/1733192201673

  • image.png

    另一个记录的方法

    1 回复
  • wilsons

    感谢,学习了。

    这种模式貌似是点击编辑器后获取的,应该和.layout__wnd--active 是一样的。

    插件的好处是可以用事件和自带方法,js 片段就只能自己实现和摸索规律了,有时候看到官方的源码,想复制过来但又太复杂,这时候就想用插件了 😄 ,但估计插件也不是所有源码都是支持的。

请输入回帖内容 ...