在 LeetCode 学习计划页面为每个分类显示题目总数,并为每个题目添加序号。
当前为
// ==UserScript==
// @name LeetCode 学习计划体验增强(题目列表常驻、显示分类下题目个数)
// @namespace http://tampermonkey.net/
// @version 0.2.3
// @description 在 LeetCode 学习计划页面为每个分类显示题目总数,并为每个题目添加序号。
// @author tianyw0
// @match https://leetcode.cn/studyplan/*
// @match https://leetcode.cn/problems/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 监听页面变化,确保在内容加载后执行脚本
const observer = new MutationObserver((mutationsList, observer) => {
// 查找页面上所有符合“主容器”特征的模块
// 假设每个这样的模块都由一个 '.w-full.overflow-hidden.rounded-lg.border-\\[1\\.5px\\]' 类标识
const allProblemModules = document.querySelectorAll('.w-full.overflow-hidden.rounded-lg.border-\\[1\\.5px\\]');
// 只有当找到模块且至少有一个模块尚未处理时才执行
if (allProblemModules.length > 0 && !allProblemModules[0].dataset.processed) {
allProblemModules.forEach(module => {
// 防止重复处理
if (module.dataset.processed) {
return;
}
const categoryTitles = module.querySelectorAll('div.flex.h-10 > div.text-\\[12px\\]');
const problemItemsInModule = module.querySelectorAll('div.flex.flex-col.border-b-\\[1\\.5px\\] > div.flex.h-\\[52px\\]');
const problemCount = problemItemsInModule.length;
// 1. 在每个分类标题的文字后显示该模块的题目个数
categoryTitles.forEach(categoryTitleTextDiv => {
// 检查是否已经添加过计数,避免重复
if (!categoryTitleTextDiv.querySelector('.problem-count-span')) {
const countSpan = document.createElement('span');
countSpan.textContent = ` (${problemCount} 题)`;
countSpan.style.marginLeft = '5px';
countSpan.classList.add('problem-count-span'); // 添加一个类以便识别
categoryTitleTextDiv.appendChild(countSpan);
}
});
// 2. 在当前模块内的具体题目前显示序号
problemItemsInModule.forEach((item, index) => {
const titleContainer = item.querySelector('div.relative.flex.h-full.w-full.items-center > div.flex.w-0.flex-1.items-center.space-x-2');
if (titleContainer && !titleContainer.querySelector('.problem-index-span')) { // 检查是否已添加序号
const indexSpan = document.createElement('span');
indexSpan.textContent = `${index + 1}. `;
indexSpan.classList.add('problem-index-span'); // 添加一个类以便识别
titleContainer.prepend(indexSpan);
}
});
// 标记此模块为已处理
module.dataset.processed = 'true';
});
// 首次成功处理后,可以停止观察,如果页面后续还有动态加载,则需要更精细的控制
// observer.disconnect();
}
});
// 开始观察 body 元素下的 DOM 变化,特别是子节点的变化和子树的变化
observer.observe(document.body, { childList: true, subtree: true });
// 首次加载时也尝试执行一次,以防内容已经存在
// 由于MutationObserver可能在初始DOM解析后才触发,直接调用一次确保立即应用
// 稍作延迟,确保页面初始内容渲染完成
setTimeout(() => {
const initialModules = document.querySelectorAll('.w-full.overflow-hidden.rounded-lg.border-\\[1\\.5px\\]');
if (initialModules.length > 0) {
initialModules.forEach(module => {
if (!module.dataset.processed) {
const categoryTitles = module.querySelectorAll('div.flex.h-10 > div.text-\\[12px\\]');
const problemItemsInModule = module.querySelectorAll('div.flex.flex-col.border-b-\\[1\\.5px\\] > div.flex.h-\\[52px\\]');
const problemCount = problemItemsInModule.length;
categoryTitles.forEach(categoryTitleTextDiv => {
if (!categoryTitleTextDiv.querySelector('.problem-count-span')) {
const countSpan = document.createElement('span');
countSpan.textContent = ` (${problemCount} 题)`;
countSpan.style.marginLeft = '5px';
countSpan.classList.add('problem-count-span');
categoryTitleTextDiv.appendChild(countSpan);
}
});
problemItemsInModule.forEach((item, index) => {
const titleContainer = item.querySelector('div.relative.flex.h-full.w-full.items-center > div.flex.w-0.flex-1.items-center.space-x-2');
if (titleContainer && !titleContainer.querySelector('.problem-index-span')) {
const indexSpan = document.createElement('span');
indexSpan.textContent = `${index + 1}. `;
indexSpan.classList.add('problem-index-span');
titleContainer.prepend(indexSpan);
}
});
module.dataset.processed = 'true';
}
});
}
// 左侧学习计划的题目列表常驻
const left = document.querySelectorAll(".z-modal")[2];
// 修改 .z-modal 宽度
left.classList.remove('w-[600px]');
left.classList.add('w-[400px]');
left.classList.remove('transform');
left.classList.add('transform-none');
// 设置 body 样式
document.body.style.marginLeft = '400px';
document.body.style.width = `${window.innerWidth - 400}px`;
// 可选:保持响应式(窗口大小变时自动更新)
window.addEventListener('resize', () => {
document.body.style.width = `${window.innerWidth - 400}px`;
});
}, 500); // 延迟 500 毫秒执行,可以根据实际情况调整
})();