// JavaScript趣味题应用 class JSQuizApp { constructor() { this.apiEndpoints = [ 'https://60s-cf.viki.moe', 'https://60s.viki.moe', 'https://60s.b23.run', 'https://60s.114128.xyz', 'https://60s-cf.114128.xyz' ]; this.currentApiIndex = 0; this.currentQuestion = null; this.selectedOption = null; this.isAnswered = false; this.initElements(); this.bindEvents(); this.loadQuestion(); } // 初始化DOM元素 initElements() { this.elements = { loading: document.getElementById('loading'), questionContainer: document.getElementById('questionContainer'), errorContainer: document.getElementById('errorContainer'), questionId: document.getElementById('questionId'), questionText: document.getElementById('questionText'), codeContent: document.getElementById('codeContent'), optionsContainer: document.getElementById('optionsContainer'), submitBtn: document.getElementById('submitBtn'), showAnswerBtn: document.getElementById('showAnswerBtn'), refreshBtn: document.getElementById('refreshBtn'), retryBtn: document.getElementById('retryBtn'), exportBtn: document.getElementById('exportBtn'), resultContainer: document.getElementById('resultContainer'), resultStatus: document.getElementById('resultStatus'), correctAnswer: document.getElementById('correctAnswer'), explanation: document.getElementById('explanation'), errorMessage: document.getElementById('errorMessage') }; } // 绑定事件 bindEvents() { this.elements.submitBtn.addEventListener('click', () => this.submitAnswer()); this.elements.showAnswerBtn.addEventListener('click', () => this.showAnswer()); this.elements.refreshBtn.addEventListener('click', () => this.loadQuestion()); this.elements.retryBtn.addEventListener('click', () => this.loadQuestion()); this.elements.exportBtn.addEventListener('click', () => this.exportToMarkdown()); } // 显示加载状态 showLoading() { this.elements.loading.style.display = 'block'; this.elements.questionContainer.style.display = 'none'; this.elements.errorContainer.style.display = 'none'; } // 显示题目 showQuestion() { this.elements.loading.style.display = 'none'; this.elements.questionContainer.style.display = 'block'; this.elements.errorContainer.style.display = 'none'; } // 显示错误 showError(message) { this.elements.loading.style.display = 'none'; this.elements.questionContainer.style.display = 'none'; this.elements.errorContainer.style.display = 'block'; this.elements.errorMessage.textContent = message; } // 获取当前API地址 getCurrentApiUrl() { return `${this.apiEndpoints[this.currentApiIndex]}/v2/awesome-js`; } // 切换到下一个API switchToNextApi() { this.currentApiIndex = (this.currentApiIndex + 1) % this.apiEndpoints.length; } // 加载题目 async loadQuestion() { this.showLoading(); this.resetQuestion(); let attempts = 0; const maxAttempts = this.apiEndpoints.length; while (attempts < maxAttempts) { try { const response = await fetch(this.getCurrentApiUrl(), { method: 'GET', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, timeout: 10000 }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (data.code === 200 && data.data) { this.currentQuestion = data.data; this.displayQuestion(); return; } else { throw new Error(data.message || '数据格式错误'); } } catch (error) { console.warn(`API ${this.getCurrentApiUrl()} 请求失败:`, error.message); attempts++; if (attempts < maxAttempts) { this.switchToNextApi(); console.log(`切换到备用API: ${this.getCurrentApiUrl()}`); } else { this.showError(`所有API接口都无法访问,请检查网络连接后重试。\n最后尝试的错误: ${error.message}`); return; } } } } // 重置题目状态 resetQuestion() { this.selectedOption = null; this.isAnswered = false; this.elements.resultContainer.style.display = 'none'; this.elements.submitBtn.disabled = true; this.elements.submitBtn.textContent = '提交答案'; this.elements.showAnswerBtn.style.display = 'inline-block'; // 清空选项容器,防止重复显示 this.elements.optionsContainer.innerHTML = ''; // 移除所有选项的事件监听器 const existingOptions = document.querySelectorAll('.option'); existingOptions.forEach(option => { option.removeEventListener('click', this.selectOption); }); } // 显示题目内容 displayQuestion() { const question = this.currentQuestion; console.log('显示题目:', question); // 设置题目ID this.elements.questionId.textContent = `题目 #${question.id}`; // 设置题目文本 this.elements.questionText.innerHTML = `
$1');
formatted = formatted.replace(/```javascript\n([\s\S]*?)\n```/g, '$1');
formatted = formatted.replace(/```([\s\S]*?)```/g, '$1');
// 处理行内代码
formatted = formatted.replace(/`([^`]+)`/g, '$1');
// 处理换行
formatted = formatted.replace(/\n\n/g, '');
formatted = formatted.replace(/\n/g, '
');
// 包装段落
if (!formatted.includes('
')) { formatted = '
' + formatted + '
'; } return formatted; } // HTML转义 escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // 导出为Markdown exportToMarkdown() { if (!this.currentQuestion) { alert('请先加载题目后再导出!'); return; } const question = this.currentQuestion; const timestamp = new Date().toLocaleString('zh-CN'); // 构建Markdown内容 let markdown = `# JavaScript趣味题 #${question.id}\n\n`; markdown += `> 导出时间: ${timestamp}\n\n`; // 题目部分 markdown += `## 题目\n\n`; markdown += `${question.question}\n\n`; // 代码部分 markdown += `## 代码\n\n`; markdown += `\`\`\`javascript\n${question.code}\n\`\`\`\n\n`; // 选项部分 markdown += `## 选项\n\n`; question.options.forEach((option, index) => { const letter = String.fromCharCode(65 + index); // A, B, C, D const isCorrect = letter === question.answer; markdown += `${letter}. ${option}${isCorrect ? ' ✅' : ''}\n`; }); markdown += `\n`; // 答案部分 markdown += `## 正确答案\n\n`; markdown += `**${question.answer}**\n\n`; // 解析部分 markdown += `## 答案解析\n\n`; // 清理解析内容中的HTML标签,转换为Markdown格式 let explanation = question.explanation; explanation = explanation.replace(//gi, '\n');
explanation = explanation.replace(/<\/p>/gi, '\n');
explanation = explanation.replace(/]*>/gi, '`');
explanation = explanation.replace(/<\/code>/gi, '`');
explanation = explanation.replace(//gi, '\n```javascript\n');
explanation = explanation.replace(/<\/code><\/pre>/gi, '\n```\n');
explanation = explanation.replace(/<[^>]*>/g, ''); // 移除其他HTML标签
explanation = explanation.replace(/\n\s*\n/g, '\n\n'); // 清理多余空行
markdown += explanation.trim() + '\n\n';
// 添加页脚
markdown += `---\n\n`;
markdown += `*本题目来源于JavaScript趣味题集合*\n`;
markdown += `*导出工具: JavaScript趣味题网页版*\n`;
// 创建下载
this.downloadMarkdown(markdown, `JavaScript趣味题_${question.id}_${new Date().getTime()}.md`);
}
// 下载Markdown文件
downloadMarkdown(content, filename) {
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 清理URL对象
setTimeout(() => {
URL.revokeObjectURL(url);
}, 100);
// 显示成功提示
this.showExportSuccess(filename);
}
// 显示导出成功提示
showExportSuccess(filename) {
// 创建临时提示元素
const toast = document.createElement('div');
toast.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: linear-gradient(135deg, #4caf50, #45a049);
color: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
z-index: 10000;
font-size: 14px;
max-width: 300px;
word-wrap: break-word;
animation: slideInRight 0.3s ease-out;
`;
toast.innerHTML = `