Tampermonkey 脚本开发实战:10个JS函数实现中国大学MOOC题目一键复制

Tampermonkey 脚本开发实战:10个JS函数实现中国大学MOOC题目一键复制

Tampermonkey脚本开发实战:构建中国大学MOOC题目复制工具

在当今在线教育蓬勃发展的时代,中国大学MOOC作为国内领先的在线学习平台,为无数学习者提供了优质课程资源。然而,许多用户在学习过程中面临一个共同痛点:无法便捷地复制课程测试题和选项内容。本文将带你从零开始,开发一个功能完善的Tampermonkey脚本,解决这一实际问题。

1. 脚本开发环境准备

Tampermonkey是一款强大的浏览器用户脚本管理器,支持Chrome、Firefox、Safari等主流浏览器。在开始开发前,我们需要做好以下准备工作:

  1. 安装Tampermonkey扩展

    • Chrome用户可以通过Chrome应用商店搜索安装
    • Firefox用户可通过Firefox附加组件商店获取
    • 其他浏览器用户可访问Tampermonkey官网下载对应版本
  2. 配置开发环境

    // ==UserScript== // @name MOOC题目复制工具 // @namespace http://your-namespace.com // @version 0.1 // @description 一键复制中国大学MOOC网站的测试题和选项 // @author YourName // @match https://www.icourse163.org/* // @grant GM_setClipboard // ==/UserScript==
  3. 基础开发工具

    • 现代浏览器(推荐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 未来功能扩展方向

  1. 题目搜索功能:将复制的题目自动在搜索引擎中查询
  2. 本地存储:保存历史题目便于复习
  3. 智能分类:根据题目类型自动分类
  4. OCR支持:处理图片形式的题目
// 伪代码:题目搜索功能示例 function setupSearchButton() { createButton(container, '搜索题目', () => { const currentQuestion = getSelectedQuestion(); if (currentQuestion) { window.open(`https://www.baidu.com/s?wd=${encodeURIComponent(currentQuestion)}`); } }); }

通过本文介绍的方法,你已经掌握了开发一个实用Tampermonkey脚本的核心技术。从基础DOM操作到高级工程化实践,这些技能不仅能应用于MOOC题目复制工具,也能迁移到其他浏览器自动化场景中。