[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(); })();
  • 思源笔记

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

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

    25695 引用 • 106306 回帖
  • 代码片段

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

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

    173 引用 • 1202 回帖
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

    太宽了哈哈

请输入回帖内容 ...

推荐标签 标签

  • Markdown

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

    171 引用 • 1535 回帖
  • Spark

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

    74 引用 • 46 回帖 • 565 关注
  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    52 引用 • 228 回帖 • 1 关注
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    6 引用 • 26 回帖 • 542 关注
  • abitmean

    有点意思就行了

    33 关注
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 1 关注
  • CloudFoundry

    Cloud Foundry 是 VMware 推出的业界第一个开源 PaaS 云平台,它支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构的问题。

    5 引用 • 18 回帖 • 184 关注
  • ZeroNet

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

    1 引用 • 21 回帖 • 649 关注
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    227 引用 • 476 回帖
  • 负能量

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

    89 引用 • 1251 回帖 • 398 关注
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    186 引用 • 1020 回帖
  • LaTeX

    LaTeX(音译“拉泰赫”)是一种基于 ΤΕΧ 的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在 20 世纪 80 年代初期开发,利用这种格式,即使使用者没有排版和程序设计的知识也可以充分发挥由 TeX 所提供的强大功能,能在几天,甚至几小时内生成很多具有书籍质量的印刷品。对于生成复杂表格和数学公式,这一点表现得尤为突出。因此它非常适用于生成高印刷质量的科技和数学类文档。

    13 引用 • 59 回帖 • 6 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    63 引用 • 289 回帖
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 670 关注
  • ZooKeeper

    ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 HBase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    59 引用 • 29 回帖 • 3 关注
  • 脑图

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

    32 引用 • 99 回帖
  • sts
    2 引用 • 2 回帖 • 232 关注
  • 创业

    你比 99% 的人都优秀么?

    82 引用 • 1395 回帖
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    188 引用 • 319 回帖 • 250 关注
  • 正则表达式

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

    31 引用 • 94 回帖 • 1 关注
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    91 引用 • 384 回帖
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 2 关注
  • Sublime

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

    10 引用 • 5 回帖 • 1 关注
  • ReactiveX

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

    1 引用 • 2 回帖 • 183 关注
  • OnlyOffice
    4 引用 • 25 关注
  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    200 引用 • 543 回帖 • 3 关注