#算术练习
这是一个基于浏览器的四则运算限时答题系统,用户可以自定义生成包含加减乘除运算的数学题目,在限定时间内完成答题挑战,系统会自动批改并统计成绩。
四则运算
#主要特性
智能题目生成
- 随机生成1-99范围内的整数四则运算题目
- 智能避免简单题目:不会出现除以1或结果为1的除法题
- 确保所有运算结果为正整数
- 可自定义题目数量(10-100题)
灵活配置选项
- 运算符选择:可自由组合加减乘除四种运算
- 数字范围:提供1-9、1-99、10-99三种范围选择
- 时间设置:答题时间30-600秒可调,默认3分钟
- 题目数量:10-100题可调,默认40题
实时答题体验
- 倒计时显示剩余时间,最后30秒变红提醒
- 实时答案验证:输入即显示正确/错误状态
- 答题统计面板:实时显示已答、正确、剩余题目数
- 键盘导航支持:Enter键跳转下一题,Ctrl+Enter提交
友好结果反馈
- 显示答对题目数/总题目数
- 统计错误题目数量(不提示具体位置,保护答题体验)
- 显示总用时
- 根据正确率提供不同的鼓励信息
- 完美通关有特别祝贺信息
响应式设计
- 适配桌面、平板、手机各种屏幕尺寸
- 自动调整题目网格布局
- 移动端优化操作体验
#使用场景
教育用途
- 小学数学课堂练习
- 心算能力训练
- 考试前复习
个人提升
- 大脑敏捷度训练
- 心算能力提升
- 注意力集中训练
趣味挑战
- 与朋友比拼计算速度
- 家庭亲子数学游戏
- 工作间隙脑力放松
#技术亮点
- 纯前端实现:无需后端,单HTML文件运行
- 智能算法:确保题目多样性和合理性
- 实时反馈:即时验证和统计
- 数据安全:所有操作均在浏览器本地完成
- 性能优化:流畅的答题体验,无页面刷新
#源码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>四则运算限时挑战</title>
<link rel="canonical" href="https://xplanc.org/primers/document/zh/03.HTML/90.实践案例/01.算术练习.md"/>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f5f7fa;
color: #333;
line-height: 1.6;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.container {
max-width: 1200px;
width: 100%;
margin: 0 auto;
padding: 30px;
background-color: white;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
header {
text-align: center;
margin-bottom: 40px;
}
h1 {
color: #2c3e50;
font-size: 2.5rem;
margin-bottom: 15px;
background: linear-gradient(90deg, #3498db, #2ecc71);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.description {
color: #7f8c8d;
font-size: 1.1rem;
max-width: 800px;
margin: 0 auto 20px;
}
.config-panel {
background-color: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-bottom: 30px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
align-items: center;
}
.config-item {
display: flex;
align-items: center;
gap: 10px;
}
.config-item label {
font-weight: 600;
color: #2c3e50;
}
.config-item input, .config-item select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 5px;
width: 100px;
text-align: center;
font-size: 1rem;
}
.control-panel {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 30px;
}
.btn {
padding: 15px 40px;
border: none;
border-radius: 8px;
font-size: 1.2rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
transform: translateY(-2px);
}
.btn-secondary {
background-color: #2ecc71;
color: white;
}
.btn-secondary:hover {
background-color: #27ae60;
transform: translateY(-2px);
}
.timer-container {
text-align: center;
margin-bottom: 30px;
}
.timer {
font-size: 3.5rem;
font-weight: 700;
color: #e74c3c;
padding: 10px 20px;
border-radius: 10px;
background-color: #fef5f5;
display: inline-block;
margin-bottom: 10px;
min-width: 180px;
}
.timer-label {
font-size: 1.2rem;
color: #7f8c8d;
font-weight: 600;
}
.questions-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.question-item {
padding: 15px;
background-color: #f8f9fa;
border-radius: 8px;
border-left: 5px solid #3498db;
display: flex;
align-items: center;
gap: 10px;
}
.question-number {
font-weight: 700;
color: #3498db;
min-width: 30px;
}
.question-text {
flex-grow: 1;
font-size: 1.1rem;
}
.question-answer {
width: 70px;
padding: 8px 10px;
border: 2px solid #ddd;
border-radius: 5px;
text-align: center;
font-size: 1rem;
transition: border-color 0.3s;
}
.question-answer:focus {
outline: none;
border-color: #3498db;
}
.question-answer.correct {
border-color: #2ecc71;
background-color: #eafaf1;
}
.question-answer.incorrect {
border-color: #e74c3c;
background-color: #fdedec;
}
.result-container {
text-align: center;
padding: 30px;
background-color: #f8f9fa;
border-radius: 10px;
margin-top: 30px;
display: none;
}
.result-title {
font-size: 2rem;
margin-bottom: 20px;
color: #2c3e50;
}
.result-score {
font-size: 3rem;
font-weight: 700;
color: #2ecc71;
margin-bottom: 20px;
}
.result-fail {
color: #e74c3c;
}
.result-details {
font-size: 1.2rem;
color: #7f8c8d;
}
.result-error {
color: #e74c3c;
font-weight: 600;
margin-top: 10px;
}
.hidden {
display: none !important;
}
.operator-filter {
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: center;
margin-top: 10px;
}
.operator-option {
display: flex;
align-items: center;
gap: 5px;
}
.operator-option input[type="checkbox"] {
width: 18px;
height: 18px;
}
.stats-panel {
display: flex;
justify-content: space-around;
margin-top: 20px;
margin-bottom: 20px;
padding: 15px;
background-color: #f1f8ff;
border-radius: 8px;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 1.8rem;
font-weight: 700;
color: #3498db;
}
.stat-label {
font-size: 0.9rem;
color: #7f8c8d;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0; /* 保证在一些浏览器中不会出现额外边距 */
}
@media (max-width: 768px) {
.container {
padding: 15px;
}
h1 {
font-size: 2rem;
}
.questions-container {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
}
.control-panel {
flex-direction: column;
align-items: center;
}
.btn {
width: 100%;
max-width: 300px;
}
.timer {
font-size: 2.8rem;
}
.stats-panel {
flex-direction: column;
gap: 15px;
}
}
@media (max-width: 480px) {
.questions-container {
grid-template-columns: 1fr;
}
.config-panel {
flex-direction: column;
align-items: flex-start;
}
.timer {
font-size: 2.2rem;
min-width: 150px;
}
.question-text {
font-size: 1rem;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>四则运算限时挑战</h1>
<p class="description">点击开始答题后,系统将生成四则运算填空题。</p>
<p class="description">您需要在限制时间内完成所有题目,全部答对即为挑战成功!</p>
<p class="description">来自:<a class="description" target="_parent" href="https://xplanc.org/primers/document/zh/03.HTML/90.实践案例/01.算术练习.md">Primers 编程伙伴</a></p>
<div class="operator-filter">
<div class="operator-option">
<input type="checkbox" id="addCheck" checked>
<label for="addCheck">加法 (+)</label>
</div>
<div class="operator-option">
<input type="checkbox" id="subCheck" checked>
<label for="subCheck">减法 (-)</label>
</div>
<div class="operator-option">
<input type="checkbox" id="mulCheck" checked>
<label for="mulCheck">乘法 (×)</label>
</div>
<div class="operator-option">
<input type="checkbox" id="divCheck" checked>
<label for="divCheck">除法 (÷)</label>
</div>
</div>
</header>
<div class="config-panel">
<div class="config-item">
<label for="timeLimit">答题时间:</label>
<input type="number" id="timeLimit" value="600" min="30" max="6000">
<label >秒</label>
</div>
<div class="config-item">
<label for="questionCount">题目数量:</label>
<input type="number" id="questionCount" value="30" min="10" max="100">
</div>
<div class="config-item">
<label for="numberRange">数字范围:</label>
<select id="numberRange">
<option value="1-9">1-9</option>
<option value="1-99" selected>1-99</option>
<option value="10-99">10-99</option>
<option value="100-999">100-999</option>
</select>
</div>
</div>
<div class="control-panel">
<button id="startBtn" class="btn btn-primary">开始答题</button>
<button id="submitBtn" class="btn btn-secondary hidden">提交答案</button>
</div>
<div class="timer-container">
<div class="timer" id="timer">--:--</div>
<div class="timer-label">剩余时间</div>
</div>
<div class="stats-panel hidden" id="statsPanel">
<div class="stat-item">
<div class="stat-value" id="answeredCount">0</div>
<div class="stat-label">已答题</div>
</div>
<div class="stat-item">
<div class="stat-value" id="remainingCount">40</div>
<div class="stat-label">剩余题</div>
</div>
</div>
<div id="questionsWrapper" class="hidden">
<div class="questions-container" id="questionsContainer">
<!-- 题目将动态生成在这里 -->
</div>
</div>
<div class="result-container" id="resultContainer">
<h2 class="result-title">答题结果</h2>
<div class="result-score" id="resultScore">40/40</div>
<p class="result-details">您总共回答了 <span id="totalQuestions">40</span> 道题目</p>
<p class="result-details">用时: <span id="timeUsed">00:00</span></p>
<p class="result-error" id="errorCountInfo"></p>
<p class="result-error" id="emptyCountInfo"></p>
<p id="resultMessage"></p>
</div>
</div>
<script>
// 全局变量
let timer;
let timeLeft;
let questions = [];
let userAnswers = {};
let isTestActive = false;
let startTime;
// DOM元素
const startBtn = document.getElementById('startBtn');
const submitBtn = document.getElementById('submitBtn');
const timerDisplay = document.getElementById('timer');
const questionsContainer = document.getElementById('questionsContainer');
const questionsWrapper = document.getElementById('questionsWrapper');
const resultContainer = document.getElementById('resultContainer');
const resultScore = document.getElementById('resultScore');
const totalQuestionsEl = document.getElementById('totalQuestions');
const timeUsedEl = document.getElementById('timeUsed');
const errorCountInfo = document.getElementById('errorCountInfo');
const emptyCountInfo = document.getElementById('emptyCountInfo');
const resultMessage = document.getElementById('resultMessage');
const timeLimitInput = document.getElementById('timeLimit');
const questionCountInput = document.getElementById('questionCount');
const numberRangeSelect = document.getElementById('numberRange');
const addCheck = document.getElementById('addCheck');
const subCheck = document.getElementById('subCheck');
const mulCheck = document.getElementById('mulCheck');
const divCheck = document.getElementById('divCheck');
const statsPanel = document.getElementById('statsPanel');
const answeredCountEl = document.getElementById('answeredCount');
const remainingCountEl = document.getElementById('remainingCount');
// 生成随机整数(根据范围)
function getRandomNumber(min, max) {
const MIN = Math.floor(min);
const MAX = Math.floor(max);
return Math.floor(Math.random() * (MAX - MIN + 1)) + MIN;
}
// 获取选中的运算符
function getSelectedOperators() {
const operators = [];
if (addCheck.checked) operators.push('+');
if (subCheck.checked) operators.push('-');
if (mulCheck.checked) operators.push('×');
if (divCheck.checked) operators.push('÷');
// 至少选择一个运算符
if (operators.length === 0) {
operators.push('+', '-', '×', '÷');
addCheck.checked = true;
subCheck.checked = true;
mulCheck.checked = true;
divCheck.checked = true;
}
return operators;
}
// 生成题目
function generateQuestions(count) {
const newQuestions = [];
const selectedOperators = getSelectedOperators();
const numberRange = numberRangeSelect.value;
const items = numberRange.split(/\D+/);
const min = parseInt(items[0]);
const max = parseInt(items[1]);
// 为每种运算符分配大致相等的题目数量
const operatorCounts = {};
selectedOperators.forEach(op => {
operatorCounts[op] = Math.floor(count / selectedOperators.length);
});
// 处理余数,确保总题目数正确
let remainder = count % selectedOperators.length;
if (remainder > 0) {
for (let i = 0; i < remainder; i++) {
const op = selectedOperators[i % selectedOperators.length];
operatorCounts[op]++;
}
}
// 生成每种运算符的题目
selectedOperators.forEach(operator => {
const opCount = operatorCounts[operator];
for (let i = 0; i < opCount; i++) {
let num1, num2, answer;
let isValid = false;
let attempts = 0;
// 确保生成有效题目(结果为正整数且不是简单题目)
while (!isValid && attempts < 100) {
attempts++;
switch (operator) {
case '+':
num1 = getRandomNumber(min, max);
num2 = getRandomNumber(min, max);
answer = num1 + num2;
break;
case '-':
// 确保结果为正数且不是0
num1 = getRandomNumber(min, max);
num2 = getRandomNumber(min, max);
if (num1 < num2) {
[num1, num2] = [num2, num1];
}
answer = num1 - num2;
break;
case '×':
// 限制乘法结果不超过1000且避免简单乘法
num1 = getRandomNumber(2, max / 2);
num2 = getRandomNumber(2, max / num1);
answer = num1 * num2;
break;
case '÷':
// 生成除法题目,确保能整除且除数不是1
// 生成一个除数(确保不是1)
num2 = getRandomNumber(2, max / 2);
// 生成一个除数(确保不是1)
answer = getRandomNumber(2, max / num2);
// 计算被除数
num1 = answer * num2;
break;
}
// 确保答案为正整数且不超过三位数
if (Number.isInteger(answer) && answer > 0 && answer <= 999) {
// 对于减法,确保结果不是0
if (operator === '-' && answer === 0) {
continue;
}
// 对于除法,确保除数不是1且答案大于1
if (operator === '÷' && (num2 === 1 || answer === 1)) {
continue;
}
// 确保不重复
if (newQuestions.some(q => q.num1 === num1 && q.num2 === num2 && q.operator === operator)) {
continue;
}
isValid = true;
}
}
// 无法生成不重复的题目
if (!isValid) {
isValid = true;
switch (operator) {
case '+':
num1 = getRandomNumber(min, max);
num2 = getRandomNumber(min, max);
answer = num1 + num2;
case '-':
// 确保结果为正数且不是0
num1 = getRandomNumber(min, max);
num2 = getRandomNumber(min, max);
if (num1 < num2) {
[num1, num2] = [num2, num1];
}
answer = num1 - num2;
case '×':
// 限制乘法结果不超过1000且避免简单乘法
num1 = getRandomNumber(2, max / 2);
num2 = getRandomNumber(2, max / num1);
answer = num1 * num2;
case '÷':
// 生成除法题目,确保能整除且除数不是1
// 生成一个除数(确保不是1)
num2 = getRandomNumber(2, max / 2);
// 生成一个除数(确保不是1)
answer = getRandomNumber(2, max / num2);
// 计算被除数
num1 = answer * num2;
}
}
if (isValid) {
newQuestions.push({
id: newQuestions.length + 1,
num1,
num2,
operator,
answer,
userAnswer: null,
isCorrect: false
});
}
}
});
// 打乱题目顺序
return newQuestions //.sort(() => Math.random() - 0.5).map((q, i) => ({...q, id: i + 1}));
}
// 渲染题目
function renderQuestions(questions) {
questionsContainer.innerHTML = '';
questions.forEach(question => {
const questionEl = document.createElement('div');
questionEl.className = 'question-item';
questionEl.innerHTML = `
<div class="question-number">${question.id}.</div>
<div class="question-text">${question.num1} ${question.operator} ${question.num2} = </div>
<input type="number" class="question-answer" id="answer-${question.id}" data-id="${question.id}" placeholder="?" min="0" max="999">
`;
questionsContainer.appendChild(questionEl);
});
// 添加输入事件监听器
document.querySelectorAll('.question-answer').forEach(input => {
input.addEventListener('input', function() {
const id = parseInt(this.dataset.id);
const value = this.value.trim();
if (value === '') {
delete userAnswers[id];
this.classList.remove('correct', 'incorrect');
} else {
const numValue = parseInt(value);
userAnswers[id] = numValue;
}
// 更新统计信息
updateStats();
});
// 允许使用键盘方向键在输入框之间导航
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
const nextInput = this.parentElement.nextElementSibling?.querySelector('.question-answer');
if (nextInput) {
nextInput.focus();
}
}
// Ctrl+Enter 提交答案
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
if (isTestActive) {
finishTest();
}
}
});
});
}
// 更新统计信息
function updateStats() {
let answered = 0;
let correct = 0;
questions.forEach(question => {
if (userAnswers[question.id] !== undefined) {
answered++;
if (userAnswers[question.id] === question.answer) {
correct++;
}
}
});
const total = questions.length;
const remaining = total - answered;
answeredCountEl.textContent = answered;
remainingCountEl.textContent = remaining;
}
// 开始测试
function startTest() {
const questionCount = parseInt(questionCountInput.value) || 40;
timeLeft = parseInt(timeLimitInput.value) || 180;
// 生成题目
questions = generateQuestions(questionCount);
userAnswers = {};
// 渲染题目
renderQuestions(questions);
// 显示题目区域和统计面板,隐藏结果区域
questionsWrapper.classList.remove('hidden');
statsPanel.classList.remove('hidden');
resultContainer.style.display = 'none';
// 显示提交按钮,隐藏开始按钮
startBtn.classList.add('hidden');
submitBtn.classList.remove('hidden');
// 开始计时
startTime = Date.now();
isTestActive = true;
updateTimerDisplay();
// 初始化统计信息
updateStats();
timer = setInterval(() => {
timeLeft--;
updateTimerDisplay();
if (timeLeft <= 0) {
finishTest();
}
}, 1000);
}
// 更新计时器显示
function updateTimerDisplay() {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
timerDisplay.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
// 时间少于30秒时改变颜色
if (timeLeft < 30) {
timerDisplay.style.color = '#e74c3c';
timerDisplay.style.backgroundColor = '#fef5f5';
} else if (timeLeft < 60) {
timerDisplay.style.color = '#f39c12';
timerDisplay.style.backgroundColor = '#fef9e7';
} else {
timerDisplay.style.color = '#3498db';
timerDisplay.style.backgroundColor = '#f5fafe';
}
}
// 完成测试
function finishTest() {
clearInterval(timer);
isTestActive = false;
// 计算得分
let correctCount = 0;
let errorCount = 0;
let unansweredCount = 0;
questions.forEach(question => {
const userAnswer = userAnswers[question.id];
question.userAnswer = userAnswer;
if (userAnswer === undefined || userAnswer === null) {
unansweredCount++;
} else if (userAnswer === question.answer) {
correctCount++;
question.isCorrect = true;
} else {
errorCount++;
question.isCorrect = false;
}
});
// 计算用时
const endTime = Date.now();
const timeUsedMs = endTime - startTime;
const timeUsedSeconds = Math.floor(timeUsedMs / 1000);
const minutesUsed = Math.floor(timeUsedSeconds / 60);
const secondsUsed = timeUsedSeconds % 60;
// 显示结果
questionsWrapper.classList.add('hidden');
statsPanel.classList.add('hidden');
resultContainer.style.display = 'block';
totalQuestionsEl.textContent = questions.length;
timeUsedEl.textContent = `${minutesUsed.toString().padStart(2, '0')}:${secondsUsed.toString().padStart(2, '0')}`;
resultScore.textContent = `${correctCount}/${questions.length}`;
// 显示错误数量信息
if (errorCount > 0) {
errorCountInfo.textContent = `错了 ${errorCount} 道题 `;
resultScore.classList.add('result-fail');
// 根据正确率显示不同信息
const accuracy = (correctCount / questions.length) * 100;
if (accuracy >= 90) {
resultMessage.textContent = "非常接近完美!再仔细一点就能全部答对了!";
} else if (accuracy >= 70) {
resultMessage.textContent = "表现不错!继续努力,下次会更好!";
} else {
resultMessage.textContent = "需要多加练习哦,继续加油!";
}
resultMessage.style.color = "#e74c3c";
}
if (unansweredCount > 0) {
emptyCountInfo.textContent = `有 ${unansweredCount} 道题未作答 `;
resultScore.classList.add('result-fail');
resultMessage.textContent = "有些题目还没完成哦。";
resultMessage.style.color = "#f39c12";
}
if (correctCount === questions.length) {
errorCountInfo.textContent = "";
resultScore.classList.remove('result-fail');
resultMessage.textContent = "恭喜!全部答对了!你是数学小天才!🎉";
resultMessage.style.color = "#2ecc71";
}
// 显示开始按钮,隐藏提交按钮
startBtn.classList.remove('hidden');
submitBtn.classList.add('hidden');
}
// 事件监听器
startBtn.addEventListener('click', startTest);
submitBtn.addEventListener('click', function() {
if (isTestActive) {
if (confirm('确定要提前提交答案吗?')) {
finishTest();
}
}
});
// 初始加载时显示开始按钮
window.addEventListener('DOMContentLoaded', () => {
startBtn.classList.remove('hidden');
submitBtn.classList.add('hidden');
// 生成示例题目(确保没有除以1的情况)
let sampleQuestion;
let attempts = 0;
do {
sampleQuestion = generateQuestions(1)[0];
attempts++;
// 防止无限循环
if (attempts > 10) break;
} while (sampleQuestion.operator === '÷' && (sampleQuestion.num2 === 1 || sampleQuestion.answer === 1));
const sampleEl = document.createElement('p');
sampleEl.className = 'description';
sampleEl.style.marginTop = '10px';
sampleEl.innerHTML = `示例题目: ${sampleQuestion.num1} ${sampleQuestion.operator} ${sampleQuestion.num2} = <span style="color:#3498db; font-weight:bold">${sampleQuestion.answer}</span>`;
document.querySelector('header').appendChild(sampleEl);
});
// 添加键盘快捷键支持
document.addEventListener('keydown', function(e) {
// 空格键开始/重新开始
if (e.code === 'Space' && !isTestActive) {
e.preventDefault();
startTest();
}
});
// 输入框限制:只允许输入数字
document.addEventListener('input', function(e) {
if (e.target.classList.contains('question-answer')) {
// 移除非数字字符
e.target.value = e.target.value.replace(/[^\d]/g, '');
// 限制最大值
if (e.target.value.length > 3) {
e.target.value = e.target.value.slice(0, 3);
}
}
});
</script>
</body>
</html>