[js] 在面包屑旁添加全屏按钮,快捷切换全屏

效果是在面包屑旁添加一个全屏按钮,快捷切换页签的全屏状态:

JS 片段:

// 在面包屑旁添加全屏按钮,快捷切换全屏 JS片段 // author by JeffreyChen https://ld246.com/article/1731698559408 // 参考 @wilsons 的方法进行了优化 https://ld246.com/article/1731683038390/comment/1731693866270#comments ,不再需要全屏切换快捷键 (function() { function addFullScreenButton(protyleElement) { // 检查该 protyle 是否已经有了 fullScreen_simulate 按钮 if (protyleElement.querySelector('.fullScreen_simulate')) { return; // 如果已存在,直接返回 } let mode = protyleElement.querySelector('.protyle-breadcrumb .block__icon[data-type="readonly"]'); if (mode) { mode.insertAdjacentHTML( "beforebegin", '<button class="fullScreen_simulate block__icon fn__flex-center ariaLabel" aria-label="全屏切换"></button>' ); let fullScreenBtn = protyleElement.querySelector(".fullScreen_simulate"); fullScreenBtn.innerHTML = `<svg><use xlink:href="#iconFullscreen"></use></svg>`; fullScreenBtn.addEventListener("click", function (e) { // 获取 .layout-tab-container > .protyle .protyle-breadcrumb__space 元素 const breadcrumbSpace = protyleElement.querySelector('.protyle-breadcrumb__space'); // 如果元素存在,则模拟点击,聚焦当前页签 if (breadcrumbSpace) { breadcrumbSpace.click(); } toggleFullScreen(protyleElement, fullScreenBtn); // 切换全屏状态 }); } } // 切换全屏状态的函数 function toggleFullScreen(protyle, fullScreenBtn) { if (!window.siyuan.editorIsFullscreen) { enterFullScreen(protyle, fullScreenBtn); } else { exitFullScreen(protyle, fullScreenBtn); } } function enterFullScreen(protyle, fullScreenBtn) { protyle.classList.add("fullscreen"); window.siyuan.editorIsFullscreen = true; updateFullScreenButton(fullScreenBtn, true); // 更新按钮 } function exitFullScreen(protyle, fullScreenBtn) { protyle.classList.remove("fullscreen"); window.siyuan.editorIsFullscreen = false; updateFullScreenButton(fullScreenBtn, false); // 更新按钮 } function updateFullScreenButton(fullScreenBtn, isFullScreen) { const iconUse = fullScreenBtn.querySelector('use'); // 切换图标 iconUse.setAttribute('xlink:href', isFullScreen ? '#iconFullscreenExit' : '#iconFullscreen'); fullScreenBtn.setAttribute('aria-label', isFullScreen ? '退出全屏' : '全屏'); } // 定期检查 .layout__center 是否存在于 DOM 中 function checkForLayoutCenter() { const targetNode = document.querySelector('.layout__center'); if (targetNode) { startObserving(targetNode); // 立即检查一次 protyle-breadcrumb checkProtyleElements(targetNode); } else { setTimeout(checkForLayoutCenter, 200); } } function checkProtyleElements(targetNode) { const protyles = targetNode.querySelectorAll('.layout-tab-container > .protyle'); protyles.forEach(protyle => { addFullScreenButton(protyle); }); } function startObserving(targetNode) { const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { // 检查添加的节点是否是 .protyle 元素 if (node.nodeType === 1 && node.matches('.layout-tab-container > .protyle')) { addFullScreenButton(node); } }); } else if (mutation.type === 'attributes' && mutation.target.matches('.layout-tab-container > .protyle')) { // 如果已有的 protyle 的类名发生变化,尝试添加全屏按钮 addFullScreenButton(mutation.target); } }); }); // 配置并开始观察 const config = { childList: true, subtree: true, attributes: true }; observer.observe(targetNode, config); } checkForLayoutCenter(); })();
旧方案(全屏切换需要设置快捷键)
// 在面包屑旁添加全屏按钮,快捷切换全屏 JS片段 // author by JeffreyChen https://ld246.com/article/1731698559408 // 注意:需要为全屏切换设置一个快捷键 (function() { function addFullScreenButton(protyleElement) { // 检查该 protyle 是否已经有了 fullScreen_simulate 按钮 if (protyleElement.querySelector('.fullScreen_simulate')) { return; // 如果已存在,直接返回 } let mode = protyleElement.querySelector('.protyle-breadcrumb .block__icon[data-type="readonly"]'); if (mode) { mode.insertAdjacentHTML( "beforebegin", '<button class="fullScreen_simulate block__icon fn__flex-center ariaLabel" aria-label="全屏切换"></button>' ); let fullScreenBtn = protyleElement.querySelector(".fullScreen_simulate"); fullScreenBtn.innerHTML = `<svg><use xlink:href="#iconFullscreen"></use></svg>`; fullScreenBtn.addEventListener("click", function (e) { // 获取 .layout-tab-container > .protyle .protyle-breadcrumb__space 元素 const breadcrumbSpace = protyleElement.querySelector('.protyle-breadcrumb__space'); // 如果元素存在,则模拟点击,聚焦当前页签 if (breadcrumbSpace) { breadcrumbSpace.click(); } dispatchKeyEvent(); // 切换图标 const iconUse = fullScreenBtn.querySelector('use'); if (iconUse.getAttribute('xlink:href') === '#iconFullscreen') { iconUse.setAttribute('xlink:href', '#iconFullscreenExit'); } else { iconUse.setAttribute('xlink:href', '#iconFullscreen'); } }); } } function dispatchKeyEvent() { let keyInit = parseHotKeyStr(window.top.siyuan.config.keymap.editor.general.fullscreen.custom); keyInit["bubbles"] = true; let keydownEvent = new KeyboardEvent('keydown', keyInit); document.getElementsByTagName("body")[0].dispatchEvent(keydownEvent); let keyUpEvent = new KeyboardEvent('keyup', keyInit); document.getElementsByTagName("body")[0].dispatchEvent(keyUpEvent); } /** * @param {*} hotkeyStr 思源hotkey格式 Refer: https://github.com/siyuan-note/siyuan/blob/d0f011b1a5b12e5546421f8bd442606bf0b5ad86/app/src/protyle/util/hotKey.ts#L4 * @returns KeyboardEventInit Refer: https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/KeyboardEvent */ function parseHotKeyStr(hotkeyStr) { let result = { ctrlKey: false, altKey: false, metaKey: false, shiftKey: false, key: 'A', keyCode: 0 } if (hotkeyStr == "" || hotkeyStr == undefined || hotkeyStr == null) { console.error("解析快捷键设置失败", hotkeyStr); throw new Error("解析快捷键设置失败"); } let onlyKey = hotkeyStr; if (hotkeyStr.indexOf("⌘") != -1) { result.ctrlKey = true; onlyKey = onlyKey.replace("⌘", ""); } if (hotkeyStr.indexOf("⌥") != -1) { result.altKey = true; onlyKey = onlyKey.replace("⌥", ""); } if (hotkeyStr.indexOf("⇧") != -1) { result.shiftKey = true; onlyKey = onlyKey.replace("⇧", ""); } // 未处理 windows btn (MetaKey) result.key = onlyKey; // 在https://github.com/siyuan-note/siyuan/commit/70acd57c4b4701b973a8ca93fadf6c003b24c789#diff-558f9f531a326d2fd53151e3fc250ac4bd545452ba782b0c7c18765a37a4e2cc // 更改中,思源改为使用keyCode判断快捷键按下事件,这里进行了对应的转换 // 另请参考该提交中涉及的文件 result.keyCode = keyCodeList[result.key]; console.assert(result.keyCode != undefined, `keyCode转换错误,key为${result.key}`); switch (result.key) { case "→": { result.key = "ArrowRight"; break; } case "←": { result.key = "ArrowLeft"; break; } case "↑": { result.key = "ArrowUp"; break; } case "↓": { result.key = "ArrowDown"; break; } case "⌦": { result.key = "Delete"; break; } case "⌫": { result.key = "Backspace"; break; } case "↩": { result.key = "Enter"; break; } } return result; } const keyCodeList = { "⌫": 8, "⇥": 9, "↩": 13, "⇧": 16, "⌘": 91, "⌥": 18, "Pause": 19, "CapsLock": 20, "Escape": 27, " ": 32, "PageUp": 33, "PageDown": 34, "End": 35, "Home": 36, "←": 37, "↑": 38, "→": 39, "↓": 40, "PrintScreen": 44, "Insert": 45, "⌦": 46, "0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, "A": 65, "B": 66, "C": 67, "D": 68, "E": 69, "F": 70, "G": 71, "H": 72, "I": 73, "J": 74, "K": 75, "L": 76, "M": 77, "N": 78, "O": 79, "P": 80, "Q": 81, "R": 82, "S": 83, "T": 84, "U": 85, "V": 86, "W": 87, "X": 88, "Y": 89, "Z": 90, "ContextMenu": 93, "MyComputer": 182, "MyCalculator": 183, ";": 186, "=": 187, ",": 188, "-": 189, ".": 190, "/": 191, "`": 192, "[": 219, "\\": 220, "]": 221, "'": 222, "*": 106, "+": 107, "-": 109, ".": 110, "/": 111, "F1": 112, "F2": 113, "F3": 114, "F4": 115, "F5": 116, "F6": 117, "F7": 118, "F8": 119, "F9": 120, "F10": 121, "F11": 122, "F12": 123, "NumLock": 144, "ScrollLock": 145 }; // 定期检查 .layout__center 是否存在于 DOM 中 function checkForLayoutCenter() { const targetNode = document.querySelector('.layout__center'); if (targetNode) { startObserving(targetNode); // 立即检查一次 protyle-breadcrumb checkProtyleElements(targetNode); } else { setTimeout(checkForLayoutCenter, 200); } } function checkProtyleElements(targetNode) { const protyles = targetNode.querySelectorAll('.layout-tab-container > .protyle'); protyles.forEach(protyle => { addFullScreenButton(protyle); }); } function startObserving(targetNode) { const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { // 检查添加的节点是否是 .protyle 元素 if (node.nodeType === 1 && node.matches('.layout-tab-container > .protyle')) { addFullScreenButton(node); } }); } else if (mutation.type === 'attributes' && mutation.target.matches('.layout-tab-container > .protyle')) { // 如果已有的 protyle 的类名发生变化,尝试添加全屏按钮 addFullScreenButton(mutation.target); } }); }); // 配置并开始观察 const config = { childList: true, subtree: true, attributes: true }; observer.observe(targetNode, config); } checkForLayoutCenter(); })();
打赏 50 积分后可见
50 积分 • 6 打赏
  • 思源笔记

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

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

    25471 引用 • 105343 回帖
  • 代码片段

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

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

    169 引用 • 1145 回帖
2 操作
JeffreyChen 在 2024-11-16 03:51:10 更新了该帖
JeffreyChen 在 2024-11-16 03:49:59 更新了该帖

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • wilsons 3

    建议加上其他事件切换时(比如按快捷键,文档菜单全屏等)也自动切换按钮,类似我下面这样的代码

    // 监听其他元素全屏事件 observeClassAddition(protyle, 'fullscreen', (eventType) => { if(eventType === 'fullscreen'){ fullScreenBtn.innerHTML = exitFullscreenSvg; fullScreenBtn.setAttribute('aria-label', '退出全屏'); } else { fullScreenBtn.innerHTML = fullscreenSvg; fullScreenBtn.setAttribute('aria-label', '全屏'); } });
  • wilsons 2

    刚才试了下,通过监听 window.siyuan.editorIsFullscreen 对象的变化也可以判断是否全屏了,这样就不需要监听 protyle 样式的变化了

    // 定义一个可观察的属性 window.siyuan._editorIsFullscreen = window.siyuan.editorIsFullscreen || false; Object.defineProperty(window.siyuan, 'editorIsFullscreen', { get: function() { return this._editorIsFullscreen; }, set: function(value) { const oldValue = this._editorIsFullscreen; this._editorIsFullscreen = value; // value true是全屏,false是退出全屏 console.log(`editorIsFullscreen changed from ${oldValue} to ${value}`); }, configurable: true, enumerable: true });
  • wujianzhong via macOS

    有 Bug,,在 macOS 下,全屏后右上角的退出全屏按钮(整排按钮)都不可用,无论鼠标如何点都不起作用

    1 回复
  • 试试新建工作空间,只使用这个代码片段,看看会不会有问题

    1 回复
  • wujianzhong via macOS

    试了,,在空白的新空间可以使用,,但安装了插件之后就出问题了

    1 回复
  • 你排查一下跟哪个插件冲突

    1 回复
  • wujianzhong

    跟 移除按钮 和 移除按钮-经典 两个插件冲突

    1 回复
  • 你是两个插件都打开了吗?还是只打开了一个

请输入回帖内容 ...
JeffreyChen
思源是支持 Markdown 语法输入的块编辑器,不是 Markdown 文件编辑器; 思源笔记同步教程:https://ld246.com/article/1692089679062 爱发电:https://afdian.com/a/JeffreyChen

推荐标签 标签

  • Sublime

    Sublime Text 是一款可以用来写代码、写文章的文本编辑器。支持代码高亮、自动完成,还支持通过插件进行扩展。

    10 引用 • 5 回帖 • 6 关注
  • GitBook

    GitBook 使您的团队可以轻松编写和维护高质量的文档。 分享知识,提高团队的工作效率,让用户满意。

    3 引用 • 8 回帖
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    32 引用 • 99 回帖 • 1 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    9746 引用 • 44336 回帖 • 86 关注
  • 自由行
    2 关注
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖 • 3 关注
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    21 引用 • 31 回帖 • 1 关注
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    557 引用 • 675 回帖 • 1 关注
  • Spark

    Spark 是 UC Berkeley AMP lab 所开源的类 Hadoop MapReduce 的通用并行框架。Spark 拥有 Hadoop MapReduce 所具有的优点;但不同于 MapReduce 的是 Job 中间输出结果可以保存在内存中,从而不再需要读写 HDFS,因此 Spark 能更好地适用于数据挖掘与机器学习等需要迭代的 MapReduce 的算法。

    74 引用 • 46 回帖 • 563 关注
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    730 引用 • 1280 回帖
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 181 关注
  • InfluxDB

    InfluxDB 是一个开源的没有外部依赖的时间序列数据库。适用于记录度量,事件及实时分析。

    2 引用 • 91 关注
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    170 引用 • 1529 回帖
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1742 回帖 • 7 关注
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖 • 2 关注
  • Mobi.css

    Mobi.css is a lightweight, flexible CSS framework that focus on mobile.

    1 引用 • 6 回帖 • 759 关注
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    108 引用 • 295 回帖
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 499 关注
  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    89 引用 • 150 回帖
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    92 引用 • 752 回帖
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 390 关注
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 433 关注
  • 倾城之链
    23 引用 • 66 回帖 • 168 关注
  • 负能量

    上帝为你关上了一扇门,然后就去睡觉了....努力不一定能成功,但不努力一定很轻松 (° ー °〃)

    89 引用 • 1251 回帖 • 406 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 530 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 166 关注