实现功能
1.导入.TXT文件到打字练习内容(部分浏览器可能出于安全问题限制了这一步操作)
2.输入文本到打字练习内(弹出输入框,将要练习的内容输入至输入框)
3. 开始练习,并根据正误在打字练习内容文本上修改颜色(某个字正确显示绿色,某个字错误,显示红色,某个字没有打显示灰色)
4.开始练习、结束练习,全部输入完成后自动结束练习,也可提前手动结束练习,结束练习后弹窗用显示时和正确率。(区别在于一个全部输入完成且全部正确,一个提前结束无其他条件)
效果图
分析
HTML 结构解析
- 根元素:
<!DOCTYPE html>
声明文档类型为HTML。 html
元素:包含整个页面的内容,并设置语言为中文(lang="zh-CN"
)。head
元素:包含页面的元数据,如字符集(charset="UTF-8"
)、视口设置(viewport
)和标题(<title>
)。style
元素:包含页面的CSS样式。- 设置页面背景颜色、字体、布局样式等。
body
元素:包含页面的可见内容。- 使用Flexbox布局使内容居中。
.container
类:包含文本输入框和按钮,并设置样式。.buttons
类:包含一组按钮,并设置布局样式。.text-to-type
类:包含待输入的文本,并设置样式。input
元素:一个文本输入框,用于用户输入。
JavaScript 解析
-
变量声明:
textToType
:获取待输入文本的DOM元素。inputText
:获取用户输入文本的DOM元素。importTextBtn
、addTextBtn
、startPracticeBtn
、endPracticeBtn
:获取按钮的DOM元素。originalText
:存储原始待输入的文本。startTime
:存储开始练习的时间。alertCount
:存储弹窗次数。correctCount
:存储正确的字符数。
-
inputText
输入事件监听器:- 当用户在输入框中输入时,会触发此事件。
- 获取用户当前输入的文本。
- 初始化一个空字符串
updatedText
用于构建更新后的文本。 - 如果
startTime
已设置,遍历原始文本和用户输入的文本:- 如果字符匹配,将字符标记为
correct
。 - 如果字符不匹配,将字符标记为
wrong
。
- 如果字符匹配,将字符标记为
- 更新
textToType
的HTML内容。 - 如果用户输入的文本与原始文本完全匹配,计算总用时和正确率,并通过弹窗显示结果。
- 重置输入框和
startTime
。
-
importTextBtn
点击事件监听器:- 创建一个文件输入元素,允许用户选择
.txt
文件。 - 当文件被选中后,使用
FileReader
读取文件内容。 - 读取完成后,将文本内容赋值给
originalText
并更新textToType
。
- 创建一个文件输入元素,允许用户选择
-
addTextBtn
点击事件监听器:- 弹出一个输入框让用户输入文本。
- 如果用户输入了文本,将其赋值给
originalText
并更新textToType
。
-
startPracticeBtn
点击事件监听器:- 设置
startTime
为当前时间。 - 清空输入框。
- 更新
textToType
的HTML内容为原始文本。
- 设置
-
endPracticeBtn
点击事件监听器:- 如果
startTime
已设置,计算总用时和正确率,并通过弹窗显示结果。 - 重置
startTime
和输入框。
- 如果
完成源代码
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>打字练习</title> <style> body { font-family: Arial, sans-serif; background-color: #f0f0f0; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; } .container { background-color: white; padding: 20px; border-radius: 15px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); max-width: 600px; width: 100%; text-align: center; } .buttons { display: flex; justify-content: space-around; margin-bottom: 20px; } .buttons button { background-color: black; color: white; border: none; border-radius: 5px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); padding: 10px 20px; cursor: pointer; font-size: 14px; } .text-to-type { color: gray; margin-bottom: 20px; font-size: 20px; } .text-to-type span.correct { color: green; } .text-to-type span.wrong { color: red; } input[type="text"] { width: 100%; padding: 10px; border-radius: 5px; border: 1px solid #ccc; font-size: 18px; } </style> </head> <body> <div class="container"> <div class="buttons"> <button id="importText">导入文本</button> <button id="addText">添加文本</button> <button id="startPractice">开始练习</button> <button id="endPractice">结束练习</button> </div> <div class="text-to-type" id="textToType">这是一个打字练习的例子</div> <input type="text" id="inputText" placeholder="开始打字..." /> </div> <script> const textToType = document.getElementById('textToType'); const inputText = document.getElementById('inputText'); const importTextBtn = document.getElementById('importText'); const addTextBtn = document.getElementById('addText'); const startPracticeBtn = document.getElementById('startPractice'); const endPracticeBtn = document.getElementById('endPractice'); let originalText = textToType.innerText; let startTime = null; let alertCount = 0; let correctCount = 0; inputText.addEventListener('input', () => { const typedText = inputText.value; let updatedText = ''; correctCount = 0; if (startTime) { for (let i = 0; i < originalText.length; i++) { if (i < typedText.length) { if (typedText[i] === originalText[i]) { updatedText += `<span class="correct">${originalText[i]}</span>`; correctCount++; } else { updatedText += `<span class="wrong">${originalText[i]}</span>`; } } else { updatedText += originalText[i]; } } textToType.innerHTML = updatedText; } if (typedText === originalText && startTime) { const endTime = new Date(); const timeTaken = ((endTime - startTime) / 1000).toFixed(2); const accuracy = ((correctCount / originalText.length) * 100).toFixed(2); alert(`总用时间: ${timeTaken} 秒\n正确率: ${accuracy}%`); alertCount++; if (alertCount < 3) { alert(`这是第 ${alertCount} 次弹窗`); } inputText.value = ''; startTime = null; } }); importTextBtn.addEventListener('click', () => { const fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.accept = '.txt'; fileInput.onchange = e => { const file = e.target.files[0]; const reader = new FileReader(); reader.onload = event => { originalText = event.target.result.trim(); textToType.innerText = originalText; }; reader.readAsText(file); }; fileInput.click(); }); addTextBtn.addEventListener('click', () => { const userInput = prompt('请输入要练习的文本:'); if (userInput) { originalText = userInput.trim(); textToType.innerText = originalText; } }); startPracticeBtn.addEventListener('click', () => { startTime = new Date(); inputText.value = ''; textToType.innerHTML = originalText; }); endPracticeBtn.addEventListener('click', () => { if (startTime) { const endTime = new Date(); const timeTaken = ((endTime - startTime) / 1000).toFixed(2); const accuracy = ((correctCount / originalText.length) * 100).toFixed(2); alert(`练习结束\n总用时间: ${timeTaken} 秒\n正确率: ${accuracy}%`); startTime = null; inputText.value = ''; } else { alert('请先开始练习!'); } }); </script> </body> </html>
复制