/
klg
/
mytest
Обзор
Документация
Войти
/
klg
/
mytest
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
accent.html
848 строк
27 KB
pgdrozdov
Первый коммит
11 июн 2026, 12:46
11 июн 2026, 12:46
505f2cd
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <title>Тренировка ударения: ставим ударение правильно</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; font-family: system-ui, 'Segoe UI', 'Roboto', 'Helvetica Neue', sans-serif; } body { background: linear-gradient(135deg, #eef2f3 0%, #d9e4e8 100%); margin: 0; padding: 1.5rem; display: flex; justify-content: center; align-items: center; min-height: 100vh; } .test-container { max-width: 1400px; width: 100%; background: rgba(255, 255, 250, 0.96); border-radius: 2.5rem; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1), 0 4px 12px rgba(0, 0, 0, 0.05); overflow: hidden; display: flex; flex-wrap: wrap; gap: 1.5rem; padding: 1.5rem; } .left-col { flex: 2.5; min-width: 280px; } .right-col { flex: 1.4; min-width: 260px; } .theory-card { background: #fef9ef; border-radius: 1.8rem; border: 1px solid #f0e2cf; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.05); padding: 1.5rem 1.3rem; backdrop-filter: blur(2px); position: sticky; top: 1rem; } .theory-card h3 { font-size: 1.5rem; font-weight: 700; color: #4a3b1c; margin-bottom: 1rem; border-left: 5px solid #c27e3a; padding-left: 0.8rem; display: flex; align-items: center; gap: 0.5rem; } .theory-card h3::before { content: "📖"; font-size: 1.4rem; } .theory-content { font-size: 0.96rem; line-height: 1.5; color: #2c2b26; } .theory-content p { margin-bottom: 0.9rem; } .rule-box { background: #fff3e0; border-radius: 1rem; padding: 0.8rem 1rem; margin: 1rem 0; border-left: 4px solid #c27e3a; } .top-bar { background: #ffffffcc; backdrop-filter: blur(8px); border-radius: 2rem; padding: 0.8rem 1.2rem; margin-bottom: 1.2rem; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.8rem; border: 1px solid rgba(200, 180, 140, 0.5); } #timer { font-size: 1.6rem; font-weight: 800; background: #3b5c3a; color: #fff0cf; padding: 0.3rem 1rem; border-radius: 60px; font-family: monospace; letter-spacing: 1px; min-width: 180px; text-align: center; } .fio-block { flex: 2; min-width: 200px; } .fio-block label { font-weight: 700; color: #6b4c2c; margin-right: 0.5rem; } .fio-block input { padding: 0.5rem 1rem; border-radius: 40px; border: 1.5px solid #e0cfb6; background: white; font-size: 0.9rem; width: 240px; transition: 0.2s; } .fio-block input:focus { border-color: #b97f44; outline: none; box-shadow: 0 0 0 3px rgba(185, 127, 68, 0.2); } .error, .server-response { border-radius: 60px; padding: 10px 20px; font-weight: 500; text-align: center; margin-bottom: 1rem; } .error { background: #ffe6e5; color: #b12a1f; border-left: 6px solid #c0392b; } .server-response { background: #e1f3e8; border-left: 6px solid #2c7a47; color: #1f5437; } .test-header { background: #7e5a3c; padding: 1rem 1.5rem; color: white; border-radius: 1.5rem; margin-bottom: 1.2rem; } .test-header h1 { margin: 0 0 0.35rem; font-weight: 600; font-size: 1.6rem; } .question-card { background: white; border-radius: 1.2rem; margin-bottom: 1rem; padding: 0.8rem 1.2rem; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); border: 1px solid #e7dbc8; transition: 0.1s; } .question-row { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; justify-content: space-between; } .word-display { font-size: 1.4rem; font-weight: 700; background: #fef4e6; padding: 0.2rem 1rem; border-radius: 50px; display: inline-block; color: #3b2a1a; letter-spacing: 0.5px; } .options { flex: 1.2; min-width: 220px; text-align: right; } .stress-select { padding: 0.6rem 1rem; border-radius: 40px; border: 1.5px solid #dbcbaa; background: white; font-size: 0.9rem; width: 100%; max-width: 280px; cursor: pointer; font-weight: 500; } .stress-select:disabled { background: #f3efe7; cursor: not-allowed; } .score-area { background: #fef7ed; border-radius: 1.8rem; padding: 0.8rem 1.5rem; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem; margin-top: 0.5rem; } .score-text { font-size: 1.3rem; font-weight: 700; background: #e9dfcf; padding: 0.3rem 1rem; border-radius: 60px; } .button-group { display: flex; gap: 0.8rem; flex-wrap: wrap; } button { border: none; background: #7e5a3c; color: white; padding: 0.6rem 1.4rem; border-radius: 2rem; font-weight: 600; font-size: 0.9rem; cursor: pointer; transition: 0.2s; } button:hover { background: #5f4129; transform: scale(0.97); } .reset-btn { background: #9b7b5c; } .reset-btn:hover { background: #7a5c3f; } .restart-btn { background: #528a6e; } .restart-btn:hover { background: #3d6b53; } .footer-note { padding: 0.8rem 0 0.2rem; font-size: 0.75rem; color: #8b6f47; border-top: 1px solid #eedfc5; margin-top: 0.6rem; } #results { margin-top: 1.8rem; background: #fffff5ec; border-radius: 2rem; padding: 1.5rem; box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1); border: 1px solid #eadbbc; overflow-x: auto; } .results-table { width: 100%; border-collapse: collapse; border-radius: 1rem; font-size: 0.85rem; } .results-table th { background: #f3e5ce; color: #5e3a1f; padding: 12px 8px; font-weight: 700; text-align: center; border: 1px solid #e2cfb0; } .results-table td { border: 1px solid #e2cfb0; padding: 10px 8px; vertical-align: top; } .correct-row { background-color: #e2f3e4; } .wrong-row { background-color: #ffe0de; } .status-badge { font-weight: 700; padding: 0.2rem 0.6rem; border-radius: 40px; display: inline-block; font-size: 0.75rem; } .status-correct { background: #2c7a47; color: white; } .status-wrong { background: #b12a1f; color: white; } @media (max-width: 780px) { body { padding: 1rem; } .test-container { flex-direction: column; } .top-bar { flex-direction: column; align-items: stretch; } .fio-block input { width: 100%; } .question-row { flex-direction: column; align-items: flex-start; } .options { width: 100%; text-align: left; } .stress-select { max-width: 100%; } } </style> </head> <body> <div style="width:100%; max-width:1400px; margin:0 auto;"> <div class="top-bar"> <div id="timer">⏱️ Время: 0 сек</div> <div class="fio-block"> <label for="fio">📝 Имя / Фамилия:</label> <input type="text" id="fio" name="fio" list="student-list" autocomplete="off" placeholder="Введите имя"> <datalist id="student-list"> <option>Анна</option> <option>Даня</option> <option>Егор</option> <option>Ксюша</option> </datalist> </div> </div> <div id="errorMessage" class="error" style="display: none;"></div> <div id="serverResponse" class="server-response" style="display: none;"></div> <form id="myForm" method="POST" action="save.php"> <input type="hidden" name="predmet" value="Русский язык"> <input type="hidden" name="tdate" id="tdate"> <input type="hidden" name="nazv" value="Ударение: тренировка"> <input type="hidden" name="time" id="time_input" value="0"> <input type="hidden" name="grade" id="grade_input" value=""> </form> <div class="test-container"> <div class="left-col"> <div class="test-header"> <h1>🎯 Поставь ударение!</h1> <p>Выбери, на какой слог / гласную падает ударение. Помни: буква Ё всегда ударная, знак не ставится. В словах из одного слога ударение тоже не ставим.</p> </div> <div id="questionsArea"></div> <div class="score-area"> <div class="score-text" id="scoreDisplay">🎯 0 / 0</div> <div class="button-group"> <button id="checkAndFinishBtn">✅ ЗАВЕРШИТЬ ТЕСТ</button> <button id="resetAnswersBtn" class="reset-btn">⟳ СБРОСИТЬ ОТВЕТЫ</button> <button id="restartTestBtn" class="restart-btn">🔄 НОВЫЙ ТЕСТ</button> </div> </div> <div class="footer-note"> ★ Если в слове есть Ё — ударение не ставим (знак не нужен).<br> ☆ Односложные слова (дом, сом, мыть…) — ударение не ставим.<br> • Для остальных слов выбери номер ударной гласной. </div> </div> <div class="right-col"> <div class="theory-card"> <h3>📚 Правила ударения</h3> <div class="theory-content"> <p><strong>🎤 Ударный слог</strong> произносится с большей силой. В русском языке ударение <strong>разноместное</strong> и <strong>подвижное</strong>.</p> <div class="rule-box"> <strong>✅ Буква Ё — всегда под ударением!</strong><br> В словах с буквой Ё знак ударения <strong>не ставят</strong>.<br> Примеры: клёны, ёжики, поёт. </div> <div class="rule-box"> <strong>✅ Слова из одного слога</strong><br> В словах, состоящих из одного слога, ударение <strong>не ставят</strong>.<br> Примеры: дом, том, сом, мыть, лень. </div> <p><strong>📌 Как выполнять:</strong><br> • Для слов с Ё / односложных выбирай вариант <strong>«Не ставить ударение»</strong>.<br> • Для остальных — выбери номер ударной гласной (например, «гласная А (2-я)»).</p> <p><strong>💡 Важно:</strong> В словах с несколькими одинаковыми гласными важно выбрать именно ту позицию, на которую падает ударение (лопАта — вторая «а»).</p> </div> </div> </div> </div> <div id="results" style="display: none;"></div> </div> <script> // ----- СЛОВАРЬ СЛОВ С ПРАВИЛЬНЫМИ ОТВЕТАМИ ----- // correctStress: null — ударение не ставим (Ё или 1 слог) // иначе номер гласной в слове (1,2,3...) на которую падает ударение const wordsData = [ { word: "Дом", correctStress: null, reason: "односложное" }, { word: "клёны", correctStress: null, reason: "буква Ё" }, { word: "том", correctStress: null, reason: "односложное" }, { word: "весна", correctStress: 2, vowels: ["е","а"] }, { word: "ёжики", correctStress: null, reason: "буква Ё" }, { word: "лень", correctStress: null, reason: "односложное" }, { word: "вёсла", correctStress: null, reason: "буква Ё" }, { word: "тень", correctStress: null, reason: "односложное" }, { word: "лопата", correctStress: 2, vowels: ["о","а","а"] }, { word: "совёнок", correctStress: null, reason: "буква Ё" }, { word: "рубашка", correctStress: 2, vowels: ["у","а","а"] }, { word: "Оля", correctStress: 1, vowels: ["О","я"] }, { word: "лисёнок", correctStress: null, reason: "буква Ё" }, { word: "сом", correctStress: null, reason: "односложное" }, { word: "звуки", correctStress: 1, vowels: ["у","и"] }, { word: "шаги", correctStress: 2, vowels: ["а","и"] }, { word: "мыть", correctStress: null, reason: "односложное" }, { word: "оленёнок", correctStress: null, reason: "буква Ё" }, { word: "её", correctStress: null, reason: "буква Ё" }, { word: "солнце", correctStress: 1, vowels: ["о","е"] }, { word: "поёт", correctStress: null, reason: "буква Ё" }, { word: "сосны", correctStress: 1, vowels: ["о","ы"] }, { word: "ястреб", correctStress: 1, vowels: ["я","е"] }, { word: "рыбка", correctStress: 1, vowels: ["ы","а"] }, { word: "пилить", correctStress: 2, vowels: ["и","и"] }, { word: "телёнок", correctStress: null, reason: "буква Ё" }, { word: "завидно", correctStress: 2, vowels: ["а","и","о"] }, { word: "компас", correctStress: 1, vowels: ["о","а"] }, { word: "пароплан", correctStress: 3, vowels: ["а","о","а"] } ]; const TOTAL_QUESTIONS = wordsData.length; let startTime = null; let timerInterval = null; let isTestFinished = false; // ----- Вспомогательные функции ----- function startTimer() { if (timerInterval) clearInterval(timerInterval); startTime = Date.now(); timerInterval = setInterval(() => { if (isTestFinished) return; const elapsed = Math.floor((Date.now() - startTime) / 1000); const timerDiv = document.getElementById('timer'); if (timerDiv) timerDiv.textContent = `⏱️ Время: ${elapsed} сек`; }, 200); } function stopTimerAndGetSeconds() { if (timerInterval) { clearInterval(timerInterval); timerInterval = null; } if (startTime) return Math.floor((Date.now() - startTime) / 1000); return 0; } function getCurrentDateTime() { const now = new Date(); const Y = now.getFullYear(); const M = String(now.getMonth() + 1).padStart(2, '0'); const D = String(now.getDate()).padStart(2, '0'); const h = String(now.getHours()).padStart(2, '0'); const m = String(now.getMinutes()).padStart(2, '0'); const s = String(now.getSeconds()).padStart(2, '0'); return `${Y}-${M}-${D} ${h}:${m}:${s}`; } function escapeHtml(str) { if (!str) return ''; return str.replace(/[&<>]/g, function (m) { if (m === '&') return '&'; if (m === '<') return '<'; if (m === '>') return '>'; return m; }); } // Генерация вариантов для select function generateOptionsForWord(idx) { const data = wordsData[idx]; const select = document.createElement('select'); select.id = `select_${idx}`; select.className = 'stress-select'; if (isTestFinished) select.disabled = true; // базовый вариант const noneOption = document.createElement('option'); noneOption.value = 'none'; noneOption.textContent = '⚡ Не ставить ударение (правило)'; select.appendChild(noneOption); if (data.correctStress !== null && data.vowels) { // для слов с ударением: перебираем гласные по позициям for (let i = 0; i < data.vowels.length; i++) { const vowel = data.vowels[i]; const option = document.createElement('option'); option.value = `pos_${i+1}`; option.textContent = `гласная ${vowel.toUpperCase()} (${i+1}-я)`; select.appendChild(option); } } else { // слова без ударения: добавляем отвлекающие варианты (гласные, если есть) if (data.word) { const letters = data.word.toLowerCase().split(''); const vowelSet = new Set(); for (let ch of letters) { if ('аеёиоуыэюя'.includes(ch)) vowelSet.add(ch); } let counter = 1; for (let v of vowelSet) { const opt = document.createElement('option'); opt.value = `fake_${v}`; opt.textContent = `буква ${v.toUpperCase()} (поставить ударение)`; select.appendChild(opt); } } } return select; } // Подсчет текущих правильных ответов function computeCurrentScore() { let correct = 0; for (let i = 0; i < TOTAL_QUESTIONS; i++) { const select = document.getElementById(`select_${i}`); if (!select) continue; const selectedVal = select.value; const wordInfo = wordsData[i]; let isCorrect = false; if (wordInfo.correctStress === null) { // правильно, если выбрали 'none' isCorrect = (selectedVal === 'none'); } else { // проверка: должно быть pos_<номер> const expected = `pos_${wordInfo.correctStress}`; isCorrect = (selectedVal === expected); } if (isCorrect) correct++; } return correct; } function updateScoreDisplay() { const correct = computeCurrentScore(); const scoreSpan = document.getElementById("scoreDisplay"); if (scoreSpan) scoreSpan.innerHTML = `🎯 ${correct} / ${TOTAL_QUESTIONS}`; } function refreshLiveScore() { updateScoreDisplay(); } // Оценка всех ответов (детализация) function evaluateAllAnswers() { let detailed = {}; let correctCount = 0; for (let i = 0; i < TOTAL_QUESTIONS; i++) { const select = document.getElementById(`select_${i}`); const selectedVal = select ? select.value : "none"; const wordInfo = wordsData[i]; let isCorrect = false; let expectedText = ""; if (wordInfo.correctStress === null) { isCorrect = (selectedVal === 'none'); expectedText = "Не ставить ударение (Ё или 1 слог)"; } else { const expectedPos = `pos_${wordInfo.correctStress}`; isCorrect = (selectedVal === expectedPos); const vowelIdx = wordInfo.correctStress - 1; const vowelLetter = wordInfo.vowels[vowelIdx] || "?"; expectedText = `гласная ${vowelLetter.toUpperCase()} (${wordInfo.correctStress}-я)`; } if (isCorrect) correctCount++; let userAnswerText = ""; if (selectedVal === 'none') userAnswerText = "Не ставить ударение"; else if (selectedVal.startsWith('pos_')) { const pos = selectedVal.split('_')[1]; if (wordInfo.vowels && wordInfo.vowels[parseInt(pos)-1]) { userAnswerText = `гласная ${wordInfo.vowels[parseInt(pos)-1].toUpperCase()} (${pos}-я)`; } else userAnswerText = selectedVal; } else userAnswerText = "Поставить ударение (неверно)"; detailed[i] = { word: wordInfo.word, userAnswer: userAnswerText, correctAnswer: expectedText, isCorrect: isCorrect }; } const wrongCount = TOTAL_QUESTIONS - correctCount; const percent = (correctCount / TOTAL_QUESTIONS) * 100; let grade = 2; if (percent >= 90) grade = 5; else if (percent >= 70) grade = 4; else if (percent >= 50) grade = 3; else grade = 2; return { wrongCount, correctCount, percent, grade, detailed }; } function renderResultsTable(fio, timeSec, correct, wrong, percent, grade, detailedMap) { let html = `<div><h3>📊 РЕЗУЛЬТАТЫ ТРЕНИРОВКИ УДАРЕНИЯ</h3> <p><strong>👤 Участник:</strong> ${escapeHtml(fio)}</p> <p><strong>⏱️ Время:</strong> ${timeSec} секунд</p> <p><strong>✅ Верно расставлено ударений:</strong> ${correct} из ${TOTAL_QUESTIONS}</p> <p><strong>❌ Ошибок:</strong> ${wrong}</p> <p><strong>📈 Процент:</strong> ${percent.toFixed(1)}%</p> <p><strong>🎓 ОЦЕНКА:</strong> <span style="font-size:1.4rem; font-weight:bold;">${grade}</span></p> <h4>📋 Подробный разбор:</h4> <div style="overflow-x:auto;"> <table class="results-table"> <thead><tr><th>Слово</th><th>Ваш выбор</th><th>Правильный ответ</th><th>Результат</th></tr></thead> <tbody>`; for (let idx = 0; idx < TOTAL_QUESTIONS; idx++) { const data = detailedMap[idx]; const rowClass = data.isCorrect ? 'correct-row' : 'wrong-row'; const statusBadge = data.isCorrect ? '<span class="status-badge status-correct">✔ Верно</span>' : '<span class="status-badge status-wrong">✘ Ошибка</span>'; html += `<tr class="${rowClass}"> <td><strong>${escapeHtml(data.word)}</strong></td> <td>${escapeHtml(data.userAnswer)}</td> <td>${escapeHtml(data.correctAnswer)}</td> <td style="text-align:center;">${statusBadge}</td> </tr>`; } html += `</tbody></table></div></div>`; const resultsDiv = document.getElementById('results'); resultsDiv.innerHTML = html; resultsDiv.style.display = 'block'; resultsDiv.scrollIntoView({ behavior: 'smooth', block: 'start' }); } function setSelectsEnabled(enabled) { for (let i = 0; i < TOTAL_QUESTIONS; i++) { const sel = document.getElementById(`select_${i}`); if (sel) sel.disabled = !enabled; } } async function sendToServer(formData) { try { const response = await fetch('save.php', { method: 'POST', body: formData }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const serverDiv = document.getElementById('serverResponse'); serverDiv.innerHTML = `<strong>✅ Результаты сохранены на сервере.</strong>`; serverDiv.style.display = 'block'; setTimeout(() => { if (serverDiv) serverDiv.style.display = 'none'; }, 4000); } catch (err) { console.warn(err); const errorDiv = document.getElementById('errorMessage'); errorDiv.innerHTML = `⚠️ Ошибка отправки: ${escapeHtml(err.message)}. Данные сохранены локально.`; errorDiv.style.display = 'block'; setTimeout(() => { if (errorDiv) errorDiv.style.display = 'none'; }, 5000); } } async function finishTest() { if (isTestFinished) return; document.getElementById('errorMessage').style.display = 'none'; document.getElementById('serverResponse').style.display = 'none'; const fioInput = document.getElementById('fio'); const userFio = fioInput.value.trim(); if (!userFio) { const errDiv = document.getElementById('errorMessage'); errDiv.textContent = '❗ Пожалуйста, введите ваше имя перед завершением теста.'; errDiv.style.display = 'block'; setTimeout(() => errDiv.style.display = 'none', 4000); return; } const spentSeconds = stopTimerAndGetSeconds(); isTestFinished = true; document.getElementById('time_input').value = spentSeconds; document.getElementById('tdate').value = getCurrentDateTime(); const { wrongCount, correctCount, percent, grade, detailed } = evaluateAllAnswers(); document.getElementById('grade_input').value = grade; setSelectsEnabled(false); renderResultsTable(userFio, spentSeconds, correctCount, wrongCount, percent, grade, detailed); const formElement = document.getElementById('myForm'); const formData = new FormData(formElement); formData.set('fio', userFio); for (let i = 0; i < TOTAL_QUESTIONS; i++) { const select = document.getElementById(`select_${i}`); const answer = select ? select.value : ''; formData.append(`answer_${i}`, answer); formData.append(`word_${i}`, wordsData[i].word); } formData.append('total_correct', correctCount); formData.append('total_questions', TOTAL_QUESTIONS); formData.append('percent', percent.toFixed(2)); await sendToServer(formData); } function resetAllSelections() { if (isTestFinished) { const errDiv = document.getElementById('errorMessage'); errDiv.textContent = '⚠️ Тест завершён. Нажмите «НОВЫЙ ТЕСТ», чтобы начать заново.'; errDiv.style.display = 'block'; setTimeout(() => errDiv.style.display = 'none', 3000); return; } for (let i = 0; i < TOTAL_QUESTIONS; i++) { const select = document.getElementById(`select_${i}`); if (select) select.value = "none"; } updateScoreDisplay(); const resultsDiv = document.getElementById('results'); if (resultsDiv) resultsDiv.style.display = 'none'; } function fullRestart() { location.reload(); } function renderTest() { const container = document.getElementById("questionsArea"); if (!container) return; container.innerHTML = ""; for (let i = 0; i < wordsData.length; i++) { const data = wordsData[i]; const card = document.createElement("div"); card.className = "question-card"; const row = document.createElement("div"); row.className = "question-row"; const wordSpan = document.createElement("div"); wordSpan.className = "word-display"; wordSpan.textContent = data.word; const optionsDiv = document.createElement("div"); optionsDiv.className = "options"; const selectEl = generateOptionsForWord(i); selectEl.addEventListener('change', () => refreshLiveScore()); optionsDiv.appendChild(selectEl); row.appendChild(wordSpan); row.appendChild(optionsDiv); card.appendChild(row); container.appendChild(card); } refreshLiveScore(); } function initEventListeners() { document.getElementById("checkAndFinishBtn")?.addEventListener("click", finishTest); document.getElementById("resetAnswersBtn")?.addEventListener("click", resetAllSelections); document.getElementById("restartTestBtn")?.addEventListener("click", fullRestart); document.getElementById('myForm')?.addEventListener('keypress', (e) => { if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA') e.preventDefault(); }); } function initPage() { renderTest(); initEventListeners(); startTimer(); isTestFinished = false; document.getElementById('tdate').value = getCurrentDateTime(); setSelectsEnabled(true); updateScoreDisplay(); } if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initPage); else initPage(); </script> </body> </html>