Tampermonkey 脚本开发实战:10个JS函数实现中国大学MOOC题目一键复制
Tampermonkey脚本开发实战:构建中国大学MOOC题目复制工具
在当今在线教育蓬勃发展的时代,中国大学MOOC作为国内领先的在线学习平台,为无数学习者提供了优质课程资源。然而,许多用户在学习过程中面临一个共同痛点:无法便捷地复制课程测试题和选项内容。本文将带你从零开始,开发一个功能完善的Tampermonkey脚本,解决这一实际问题。
1. 脚本开发环境准备
Tampermonkey是一款强大的浏览器用户脚本管理器,支持Chrome、Firefox、Safari等主流浏览器。在开始开发前,我们需要做好以下准备工作:
安装Tampermonkey扩展:
- Chrome用户可以通过Chrome应用商店搜索安装
- Firefox用户可通过Firefox附加组件商店获取
- 其他浏览器用户可访问Tampermonkey官网下载对应版本
配置开发环境:
// ==UserScript== // @name MOOC题目复制工具 // @namespace http://your-namespace.com // @version 0.1 // @description 一键复制中国大学MOOC网站的测试题和选项 // @author YourName // @match https://www.icourse163.org/* // @grant GM_setClipboard // ==/UserScript==基础开发工具:
- 现代浏览器(推荐Chrome或Firefox)
- 代码编辑器(VS Code、Sublime Text等)
- 浏览器开发者工具(F12打开)
提示:在脚本开发过程中,建议开启Tampermonkey的"允许访问文件URL"选项,方便本地调试。
2. 核心功能模块设计与实现
2.1 DOM元素操作基础
中国大学MOOC平台的题目和选项通常以特定class或id的DOM元素呈现。我们需要先掌握基本的DOM操作技术:
// 获取元素常用方法 const byId = document.getElementById('elementId'); const byClass = document.getElementsByClassName('className'); const bySelector = document.querySelectorAll('div.question'); // 删除元素示例 function removeElements(selector) { document.querySelectorAll(selector).forEach(el => el.remove()); }2.2 动态按钮创建与事件绑定
为用户提供直观的操作入口,我们需要在页面上动态添加功能按钮:
function createButton(parent, text, onClick, styles = {}) { const btn = document.createElement('button'); btn.textContent = text; btn.addEventListener('click', onClick); // 应用样式 Object.assign(btn.style, { padding: '8px 12px', margin: '5px', borderRadius: '4px', cursor: 'pointer', ...styles }); parent.appendChild(btn); return btn; }2.3 题目内容提取与格式化
提取题目内容并格式化为可复制的文本是核心功能:
function extractQuestions() { const questions = []; const questionElements = document.querySelectorAll('.question-content'); questionElements.forEach((q, index) => { const questionText = q.textContent.trim(); const options = Array.from(q.nextElementSibling.querySelectorAll('.option')) .map(opt => opt.textContent.trim()); questions.push({ number: index + 1, question: questionText, options: options }); }); return questions; } function formatQuestionsForCopy(questions) { return questions.map(q => { const optionsText = q.options.map((opt, i) => `${String.fromCharCode(65 + i)}. ${opt}`).join('\n'); return `第${q.number}题: ${q.question}\n${optionsText}\n`; }).join('\n'); }3. 高级功能实现与优化
3.1 智能内容识别与处理
不同课程可能使用不同的DOM结构,我们需要更健壮的识别逻辑:
function smartExtractQuestions() { const possibleSelectors = [ '.question-content', '.f-richEditorText', '.qa-question', '.exam-question' ]; let questions = []; possibleSelectors.forEach(selector => { const elements = document.querySelectorAll(selector); if (elements.length > 0) { elements.forEach(el => { // 更精细的内容提取逻辑 const text = el.textContent.replace(/\s+/g, ' ').trim(); if (text.length > 10) { // 简单过滤掉太短的文本 questions.push(text); } }); } }); return questions; }3.2 剪贴板操作与用户反馈
使用Tampermonkey提供的API实现安全的剪贴板操作:
function copyToClipboard(text) { return new Promise((resolve, reject) => { GM_setClipboard(text, 'text', () => { showToast('内容已复制到剪贴板'); resolve(); }); }); } function showToast(message, duration = 2000) { const toast = document.createElement('div'); toast.textContent = message; Object.assign(toast.style, { position: 'fixed', bottom: '20px', left: '50%', transform: 'translateX(-50%)', backgroundColor: 'rgba(0,0,0,0.7)', color: 'white', padding: '10px 20px', borderRadius: '4px', zIndex: '9999', transition: 'opacity 0.3s' }); document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, duration); }3.3 性能优化与防冲突处理
确保脚本运行高效且不影响页面原有功能:
// 使用MutationObserver监听DOM变化 function setupObserver() { const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.addedNodes.length) { initCopyButtons(); } }); }); observer.observe(document.body, { childList: true, subtree: true }); } // 防抖处理频繁操作 function debounce(func, delay) { let timeout; return function() { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, arguments), delay); }; }4. 工程化与可维护性提升
4.1 模块化代码结构
将功能拆分为独立模块,提高代码可维护性:
mooc-copy-tool/ ├── core/ │ ├── dom.js # DOM操作相关 │ ├── utils.js # 工具函数 │ └── events.js # 事件处理 ├── ui/ │ ├── buttons.js # 按钮创建与管理 │ └── toast.js # 用户反馈 └── main.js # 主入口文件4.2 配置系统设计
允许用户自定义脚本行为:
const defaultConfig = { copyFormat: 'full', // 'full'|'question-only'|'options-only' showNumbers: true, excludeTypes: ['drag-drop', 'match'], buttonPosition: 'top-right' }; function loadConfig() { return new Promise(resolve => { GM_getValue('moocCopyConfig', defaultConfig, config => { resolve(config); }); }); }4.3 错误处理与日志系统
健壮的错误处理机制对用户脚本尤为重要:
function setupErrorHandling() { window.addEventListener('error', e => { logError(e.message, e.filename, e.lineno); showToast('操作失败,请刷新页面重试'); }); } function logError(message, file, line) { const error = { timestamp: new Date().toISOString(), message, file, line, userAgent: navigator.userAgent }; console.error('MOOC Copy Error:', error); GM_setValue('lastError', error); }5. 实际应用与扩展思路
5.1 与流行学习工具集成
考虑与其他学习工具如Anki、Notion等集成:
function exportToAnki(questions) { const ankiFormat = questions.map(q => { return `${q.question}\t${q.options.join('<br>')}`; }).join('\n'); copyToClipboard(ankiFormat).then(() => { showToast('Anki格式已复制,可直接导入'); }); }5.2 跨平台兼容性处理
针对不同浏览器和MOOC版本进行适配:
| 平台/浏览器 | 适配要点 | 解决方案 |
|---|---|---|
| Chrome | 严格的CSP策略 | 使用GM_* API代替原生方法 |
| Firefox | 不同的API实现 | 特性检测和polyfill |
| Safari | 性能差异 | 优化DOM操作频率 |
| 移动端 | 触摸事件 | 添加触摸支持 |
5.3 未来功能扩展方向
- 题目搜索功能:将复制的题目自动在搜索引擎中查询
- 本地存储:保存历史题目便于复习
- 智能分类:根据题目类型自动分类
- OCR支持:处理图片形式的题目
// 伪代码:题目搜索功能示例 function setupSearchButton() { createButton(container, '搜索题目', () => { const currentQuestion = getSelectedQuestion(); if (currentQuestion) { window.open(`https://www.baidu.com/s?wd=${encodeURIComponent(currentQuestion)}`); } }); }通过本文介绍的方法,你已经掌握了开发一个实用Tampermonkey脚本的核心技术。从基础DOM操作到高级工程化实践,这些技能不仅能应用于MOOC题目复制工具,也能迁移到其他浏览器自动化场景中。
