[js][css] 分享一个类语雀代码块折叠 JS

image.png

image.png

PixPin20250312155832.gif

.code-block { position: relative; margin: 1.5rem 0; border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; overflow: hidden; z-index: 1; background-color: #f5f5f5; padding: 1rem; border: 2px solid transparent; } @keyframes neon-border { 0% { border-color: #ff0080; box-shadow: 0 0 5px #ff0080; } 25% { border-color: #00ffff; box-shadow: 0 0 5px #00ffff; } 50% { border-color: #00ff00; box-shadow: 0 0 5px #00ff00; } 75% { border-color: #ffff00; box-shadow: 0 0 5px #ffff00; } 100% { border-color: #ff0080; box-shadow: 0 0 5px #ff0080; } } .code-block:hover:not([data-fullscreen="true"]) { animation: neon-border 5s infinite; transform: translateY(-3px) scale(1.02); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15); z-index: 10; } .code-block pre { margin: 0; padding: 0; } .code-block-header .code-block-title { font-weight: 500; } .theme-dark .code-block { background-color: #1e1e1e; color: #d4d4d4; } .code-block .protyle-action { opacity: 0; transition: opacity 0.3s ease; } .code-block:hover .protyle-action { opacity: 1; } .code-block[data-fullscreen="true"] { border: none; animation: none; transform: none !important; box-shadow: none; border-radius: 0; margin: 0; padding: 0; } .code-block[data-fullscreen="true"] .hljs { height: calc(100vh - 45px); padding: 1rem; box-sizing: border-box; } .code-block[data-fullscreen="true"] .code-block-header { padding: 8px 15px; background-color: rgba(0, 0, 0, 0.1); border-bottom: 1px solid rgba(127, 127, 127, 0.3); } .code-block[data-fullscreen="true"] .code-block-header { border-left: 4px solid #ff0080; transition: border-color 0.5s ease; } .code-block[data-fullscreen="true"] .code-block-header:hover { border-left-color: #00ffff; } .code-fullscreen-button { opacity: 0.6; transition: transform 0.3s ease, opacity 0.3s ease; } .code-fullscreen-button:hover { opacity: 1; transform: rotate(90deg); } .code-block.animate .hljs { transition: max-height 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .code-arrow { transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } @media (max-width: 768px) { .code-block:hover:not([data-fullscreen="true"]) { transform: translateY(-2px) scale(1.01); } .code-fullscreen-button { right: 20px; } }
(() => { const config = { defaultTitle: "Code", arrowExpandedHTML: '<svg class="code-arrow" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8.59 16.59L13.17 12L8.59 7.41L10 6L16 12L10 18L8.59 16.59Z"></path></svg>', arrowCollapsedHTML: '<svg class="code-arrow" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8.59 16.59L13.17 12L8.59 7.41L10 6L16 12L10 18L8.59 16.59Z"></path></svg>', fullscreenHTML: '<svg class="code-fullscreen" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"></path></svg>', exitFullscreenHTML: '<svg class="code-fullscreen" width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"></path></svg>', headerMinWidth: 100, collapseAnimation: true, animationDuration: 300, }; function addStyles() { const styles = ` .code-block-header { display: flex; align-items: center; padding: 4px 8px; cursor: pointer; background-color: var(--b3-code-block-bg); color: var(--b3-protyle-inline-code-color); border-bottom: 1px solid rgba(127, 127, 127, 0.2); font-size: 13px; font-family: var(--b3-font-family-code); position: relative; } .code-block-title { margin-left: 8px; flex-grow: 1; line-height: 1.5; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .code-arrow { width: 35px; height: 35px; margin-left: 20px; transition: transform 0.2s ease; } .code-fullscreen-button { position: absolute; right: 50px; width: 35px; height: 35px; display: flex; align-items: center; justify-content: center; cursor: pointer; opacity: 0.7; transition: all 0.3s ease; } .code-fullscreen-button:hover { opacity: 1; transform: scale(1.1); } .code-fullscreen { width: 24px; height: 24px; } .code-block-title:empty:before { content: "${config.defaultTitle}"; opacity: 0.6; } .code-block[data-collapsed="true"] .code-block-header .code-arrow { transform: rotate(0deg); } .code-block[data-collapsed="false"] .code-block-header .code-arrow { transform: rotate(90deg); } .code-block[data-collapsed="true"] .code-block-header { border-bottom: none; opacity: 0.8; } .code-block-title:focus { outline: none; border-bottom: 1px dotted var(--b3-protyle-inline-code-color); } .code-block[data-collapsed="true"] .hljs { display: none; } .code-block[data-fullscreen="true"] { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 9999; background-color: var(--b3-code-block-bg); display: flex; flex-direction: column; overflow: hidden; } .code-block[data-fullscreen="true"] .hljs { flex-grow: 1; overflow: auto; max-height: none !important; display: block !important; height: auto !important; opacity: 1 !important; } .code-block[data-fullscreen="true"] .code-block-header { position: sticky; top: 0; z-index: 1; } `; const styleEl = document.createElement('style'); styleEl.innerHTML = styles; document.head.appendChild(styleEl); } function createCodeBlockHeader(codeBlock) { if (codeBlock.querySelector('.code-block-header')) { return; } const header = document.createElement('div'); header.className = 'code-block-header protyle-custom'; header.innerHTML = config.arrowExpandedHTML; const title = document.createElement('div'); title.className = 'code-block-title'; title.contentEditable = 'true'; title.spellcheck = false; const language = getCodeBlockLanguage(codeBlock); if (language) { title.textContent = language; } header.appendChild(title); const fullscreenButton = document.createElement('div'); fullscreenButton.className = 'code-fullscreen-button'; fullscreenButton.innerHTML = config.fullscreenHTML; fullscreenButton.title = "全屏显示代码"; header.appendChild(fullscreenButton); header.addEventListener('click', (e) => { if ((e.target === title && document.activeElement === title) || fullscreenButton.contains(e.target)) { return; } toggleCodeBlock(codeBlock); }); title.addEventListener('click', (e) => { e.stopPropagation(); }); title.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); title.blur(); } }); fullscreenButton.addEventListener('click', (e) => { e.stopPropagation(); toggleFullscreen(codeBlock, fullscreenButton); }); codeBlock.insertBefore(header, codeBlock.firstChild); codeBlock.setAttribute('data-collapsed', 'false'); codeBlock.setAttribute('data-fullscreen', 'false'); } function toggleFullscreen(codeBlock, button) { const isFullscreen = codeBlock.getAttribute('data-fullscreen') === 'true'; const hljs = codeBlock.querySelector('.hljs'); if (isFullscreen) { codeBlock.setAttribute('data-fullscreen', 'false'); button.innerHTML = config.fullscreenHTML; button.title = "全屏显示代码"; if (codeBlock._wasCollapsedBeforeFullscreen) { if (hljs) { hljs.style.display = ''; hljs.style.maxHeight = ''; hljs.style.height = ''; hljs.style.opacity = ''; hljs.style.overflow = ''; delete hljs.dataset.animating; } setTimeout(() => { codeBlock.setAttribute('data-collapsed', 'true'); if (hljs) { hljs.style.display = 'none'; } delete codeBlock._wasCollapsedBeforeFullscreen; }, 50); } if (window._scrollPositionBeforeFullscreen !== undefined) { window.scrollTo(0, window._scrollPositionBeforeFullscreen); delete window._scrollPositionBeforeFullscreen; } } else { window._scrollPositionBeforeFullscreen = window.scrollY; if (codeBlock.getAttribute('data-collapsed') === 'true') { codeBlock._wasCollapsedBeforeFullscreen = true; codeBlock.setAttribute('data-collapsed', 'false'); if (hljs) { hljs.style.display = 'block'; hljs.style.maxHeight = 'none'; hljs.style.height = 'auto'; hljs.style.opacity = '1'; hljs.style.overflow = 'auto'; if (hljs.dataset.animating === 'true') { delete hljs.dataset.animating; } } } codeBlock.setAttribute('data-fullscreen', 'true'); button.innerHTML = config.exitFullscreenHTML; button.title = "退出全屏"; setTimeout(() => { if (hljs) { hljs.scrollTop = 0; } }, 0); } } function getCodeBlockLanguage(codeBlock) { const hljsElement = codeBlock.querySelector('.hljs'); if (!hljsElement) return null; const classes = hljsElement.className.split(' '); for (const cls of classes) { if (cls !== 'hljs' && cls.length > 0) { return cls; } } return null; } function toggleCodeBlock(codeBlock) { if (codeBlock.getAttribute('data-fullscreen') === 'true') { return; } const isCollapsed = codeBlock.getAttribute('data-collapsed') === 'true'; const hljs = codeBlock.querySelector('.hljs'); if (!hljs) return; if (hljs.dataset.animating === 'true') { return; } hljs.dataset.animating = 'true'; hljs.style.transition = 'none'; codeBlock.classList.remove('animate'); if (isCollapsed) { hljs.style.display = 'block'; hljs.style.height = '0px'; hljs.style.overflow = 'hidden'; hljs.style.opacity = '0'; const targetHeight = hljs.scrollHeight; const heightAnimation = hljs.animate([ { height: '0px', opacity: 0 }, { height: targetHeight + 'px', opacity: 1 } ], { duration: 250, easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)', fill: 'forwards' }); heightAnimation.onfinish = () => { hljs.style.height = ''; hljs.style.overflow = ''; hljs.style.opacity = ''; hljs.style.transition = ''; delete hljs.dataset.animating; }; } else { const startHeight = hljs.offsetHeight; hljs.style.height = startHeight + 'px'; hljs.style.overflow = 'hidden'; const opacityAnimation = hljs.animate([ { opacity: 1 }, { opacity: 0 } ], { duration: 120, easing: 'ease-out', fill: 'forwards' }); setTimeout(() => { const heightAnimation = hljs.animate([ { height: startHeight + 'px' }, { height: '0px' } ], { duration: 180, easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)', fill: 'forwards' }); heightAnimation.onfinish = () => { hljs.style.display = 'none'; hljs.style.height = ''; hljs.style.opacity = ''; hljs.style.overflow = ''; hljs.style.transition = ''; delete hljs.dataset.animating; }; }, 70); } codeBlock.setAttribute('data-collapsed', !isCollapsed); } function processCodeBlocks(element) { const codeBlocks = element.querySelectorAll('.code-block:not([data-processed="true"])'); codeBlocks.forEach(codeBlock => { createCodeBlockHeader(codeBlock); codeBlock.setAttribute('data-processed', 'true'); }); } function observeCodeBlockAddition(container) { const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.classList && node.classList.contains('code-block')) { createCodeBlockHeader(node); node.setAttribute('data-processed', 'true'); } if (node.querySelectorAll) { processCodeBlocks(node); } } }); } }); }); observer.observe(container, { childList: true, subtree: true }); return observer; } function setupKeyboardHandlers() { document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { const fullscreenCodeBlock = document.querySelector('.code-block[data-fullscreen="true"]'); if (fullscreenCodeBlock) { const fullscreenButton = fullscreenCodeBlock.querySelector('.code-fullscreen-button'); if (fullscreenButton) { toggleFullscreen(fullscreenCodeBlock, fullscreenButton); e.preventDefault(); } } } }); } function waitForElement(selector) { return new Promise(resolve => { if (document.querySelector(selector)) { return resolve(document.querySelector(selector)); } const observer = new MutationObserver(() => { if (document.querySelector(selector)) { observer.disconnect(); resolve(document.querySelector(selector)); } }); observer.observe(document.body, { childList: true, subtree: true }); }); } function isMobile() { return !!document.getElementById("sidebar"); } async function init() { addStyles(); setupKeyboardHandlers(); const container = await waitForElement(isMobile() ? '.protyle-content' : '.layout__center'); processCodeBlocks(container); observeProtyleAddition(container, protyles => { protyles.forEach(protyle => { if (!protyle.classList.contains('protyle')) { protyle = protyle.closest('.protyle'); } if (protyle) { processCodeBlocks(protyle); observeCodeBlockAddition(protyle); } }); }); } function observeProtyleAddition(container, callback) { const observer = new MutationObserver(mutations => { const protyles = []; mutations.forEach(mutation => { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if (node.classList && (node.classList.contains('protyle') || node.classList.contains('protyle-content'))) { protyles.push(node); } } }); } }); if (protyles.length > 0) { callback(protyles); } }); observer.observe(container, { childList: true, subtree: true }); return observer; } init(); })();
  • 思源笔记

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

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

    26277 引用 • 109250 回帖
  • 代码片段

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

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

    199 引用 • 1421 回帖 • 2 关注
4 操作
Luuxcyzz 在 2025-03-12 16:02:38 更新了该帖
Luuxcyzz 在 2025-03-12 15:48:47 更新了该帖
JeffreyChen 在 2025-03-12 12:08:18 更新了该帖
JeffreyChen 在 2025-03-12 12:07:46 更新了该帖

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • jacob111 via macOS

    试了下很不错呀,唯一一点是折叠的时候和展开的时候 code 左边的符号是不是应该有些区别。

    参考了下 confluence,折叠块折叠起来的时候如果有更明显的标志知道这个块是折叠的可能会比较好。

    1 回复
  • sangshaoxuan

    .code-block[data-collapsed="true"] .code-block-header .code-arrow {
    transform: rotate(0) !important;
    }
    code-block[data-collapsed="false"] .code-block-header .code-arrow {
    transform: rotate(90deg) !important;
    }

    修改下 JS 的样式表就行了

    1 回复
  • jacob111 via macOS

    看起来楼主的代码里是有这部分的,但是没生效

    1 回复
  • jacob111 via macOS

    而且如果 reload 或者换主题,就会失效,写在 code 标题里的部分也没了

    1 回复
  • Luuxcyzz 1 赞同
    作者

    更新过了 再看看

  • Luuxcyzz
    作者

    后面也会慢慢更新自己用的觉得好看的样式,如果有同样需要且常用代码段记录的同学也可以贴一下自己的 css

  • sangshaoxuan 1 赞同

    被其他样式覆盖了,加上!important 就行了

  • wilsons 1 赞同

    我也分享一个折叠代码块的 [js] 折叠代码块

    image.png

  • Sakurarinkos via Android

    这个看起来不错

  • ZQ11

    太宽了哈哈

请输入回帖内容 ...

推荐标签 标签

  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖 • 3 关注
  • 大疆创新

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

    2 引用 • 14 回帖
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    36 引用 • 200 回帖 • 39 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 734 关注
  • PWA

    PWA(Progressive Web App)是 Google 在 2015 年提出、2016 年 6 月开始推广的项目。它结合了一系列现代 Web 技术,在网页应用中实现和原生应用相近的用户体验。

    14 引用 • 69 回帖 • 184 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 83 关注
  • 脑图

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

    32 引用 • 99 回帖
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    42 引用 • 130 回帖 • 254 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 269 回帖 • 1 关注
  • SMTP

    SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。

    4 引用 • 18 回帖 • 636 关注
  • Eclipse

    Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。

    76 引用 • 258 回帖 • 625 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 3 关注
  • BookxNote

    BookxNote 是一款全新的电子书学习工具,助力您的学习与思考,让您的大脑更高效的记忆。

    笔记整理交给我,一心只读圣贤书。

    1 引用 • 1 回帖 • 5 关注
  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    10 引用 • 77 回帖
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    315 引用 • 547 回帖
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    8 引用 • 26 回帖
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 712 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    108 引用 • 153 回帖 • 1 关注
  • QQ

    1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。

    45 引用 • 557 回帖
  • abitmean

    有点意思就行了

    35 关注
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 651 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    201 引用 • 120 回帖 • 1 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 522 关注