Son aktivite 1 month ago

清空掘金代码块头部右侧原有按钮,仅保留一个自定义“复制代码段”按钮,点击后复制纯净代码文本(无行号、无HTML样式)

juejin.user.js Ham
1// ==UserScript==
2// @name 掘金复制代码段
3// @namespace https://docs.scriptcat.org/
4// @version 0.1.0
5// @description 免登录复制掘金代码片段
6// @author You
7// @match https://juejin.cn/post/*
8// @icon https://www.google.com/s2/favicons?sz=64&domain=juejin.cn
9// @downloadURL https://gist.asfd.cn/jetsung/juejin/raw/HEAD/juejin.user.js
10// @updateURL https://gist.asfd.cn/jetsung/juejin/raw/HEAD/juejin.user.js
11// @grant none
12// ==/UserScript==
13
14(function() {
15 'use strict';
16
17 // 自定义按钮样式
18 const customBtnStyle = {
19 display: 'inline-block',
20 padding: '2px 10px',
21 marginLeft: '8px',
22 fontSize: '13px',
23 color: '#fff',
24 backgroundColor: '#007fff',
25 borderRadius: '6px',
26 cursor: 'pointer',
27 userSelect: 'none',
28 transition: 'all 0.3s',
29 boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
30 };
31
32 function addCustomCopyButton() {
33 document.querySelectorAll('.code-block-extension-header').forEach(header => {
34 const headerRight = header.querySelector('.code-block-extension-headerRight');
35 if (!headerRight || headerRight.querySelector('.juejin-custom-copy-btn')) return;
36
37 // 清空右侧所有原有按钮
38 headerRight.innerHTML = '';
39
40 // 创建自定义按钮
41 const copyBtn = document.createElement('div');
42 copyBtn.className = 'juejin-custom-copy-btn';
43 copyBtn.textContent = '复制代码段';
44 Object.assign(copyBtn.style, customBtnStyle);
45
46 // 悬停与成功效果
47 copyBtn.addEventListener('mouseenter', () => copyBtn.style.backgroundColor = '#0066cc');
48 copyBtn.addEventListener('mouseleave', () => {
49 if (copyBtn.textContent !== '复制成功') copyBtn.style.backgroundColor = '#007fff';
50 });
51
52 headerRight.appendChild(copyBtn);
53
54 // 复制逻辑(更稳健的纯文本提取)
55 copyBtn.addEventListener('click', async () => {
56 const codeContainer = header.parentElement.querySelector('pre code') ||
57 header.nextElementSibling?.querySelector('code') ||
58 header.parentElement.querySelector('code.hljs');
59
60 if (!codeContainer) {
61 copyBtn.textContent = '无代码';
62 setTimeout(() => copyBtn.textContent = '复制代码段', 2000);
63 return;
64 }
65
66 let pureCode = '';
67
68 // 优先尝试直接取 code 的纯文本(最干净)
69 if (codeContainer.textContent) {
70 pureCode = codeContainer.textContent.trim();
71 } else {
72 // 备选:逐行提取
73 const lines = Array.from(codeContainer.querySelectorAll('div, span, .code-block-extension-codeLine'))
74 .map(el => el.textContent || '');
75 pureCode = lines.join('\n').trim();
76 }
77
78 // 去除可能的行号前缀(如果还有残留)
79 pureCode = pureCode.replace(/^\s*\d+\s*/gm, '').trim();
80
81 try {
82 await navigator.clipboard.writeText(pureCode);
83 copyBtn.textContent = '复制成功 ✓';
84 copyBtn.style.backgroundColor = '#52c41a';
85 setTimeout(() => {
86 copyBtn.textContent = '复制代码段';
87 copyBtn.style.backgroundColor = '#007fff';
88 }, 2000);
89 } catch (err) {
90 console.error('复制失败', err);
91 copyBtn.textContent = '复制失败';
92 setTimeout(() => copyBtn.textContent = '复制代码段', 2000);
93 }
94 });
95 });
96 }
97
98 // 页面初次加载
99 window.addEventListener('load', addCustomCopyButton);
100
101 // 动态监听(掘金是 SPA,内容会异步加载)
102 const observer = new MutationObserver(addCustomCopyButton);
103 observer.observe(document.body, { childList: true, subtree: true });
104})();