JS+H5打字练习器

实现功能

1.导入.TXT文件到打字练习内容(部分浏览器可能出于安全问题限制了这一步操作)
2.输入文本到打字练习内(弹出输入框,将要练习的内容输入至输入框)
3. 开始练习,并根据正误在打字练习内容文本上修改颜色(某个字正确显示绿色,某个字错误,显示红色,某个字没有打显示灰色)
4.开始练习、结束练习,全部输入完成后自动结束练习,也可提前手动结束练习,结束练习后弹窗用显示时和正确率。(区别在于一个全部输入完成且全部正确,一个提前结束无其他条件)

效果图

在这里插入图片描述在这里插入图片描述

分析

HTML 结构解析

  1. 根元素<!DOCTYPE html> 声明文档类型为HTML。
  2. html 元素:包含整个页面的内容,并设置语言为中文(lang="zh-CN")。
  3. head 元素:包含页面的元数据,如字符集(charset="UTF-8")、视口设置(viewport)和标题(<title>)。
  4. style 元素:包含页面的CSS样式。
    • 设置页面背景颜色、字体、布局样式等。
  5. body 元素:包含页面的可见内容。
    • 使用Flexbox布局使内容居中。
  6. .container:包含文本输入框和按钮,并设置样式。
  7. .buttons:包含一组按钮,并设置布局样式。
  8. .text-to-type:包含待输入的文本,并设置样式。
  9. input 元素:一个文本输入框,用于用户输入。

JavaScript 解析

  1. 变量声明

    • textToType:获取待输入文本的DOM元素。
    • inputText:获取用户输入文本的DOM元素。
    • importTextBtnaddTextBtnstartPracticeBtnendPracticeBtn:获取按钮的DOM元素。
    • originalText:存储原始待输入的文本。
    • startTime:存储开始练习的时间。
    • alertCount:存储弹窗次数。
    • correctCount:存储正确的字符数。
  2. inputText 输入事件监听器

    • 当用户在输入框中输入时,会触发此事件。
    • 获取用户当前输入的文本。
    • 初始化一个空字符串updatedText用于构建更新后的文本。
    • 如果startTime已设置,遍历原始文本和用户输入的文本:
      • 如果字符匹配,将字符标记为correct
      • 如果字符不匹配,将字符标记为wrong
    • 更新textToType的HTML内容。
    • 如果用户输入的文本与原始文本完全匹配,计算总用时和正确率,并通过弹窗显示结果。
    • 重置输入框和startTime
  3. importTextBtn 点击事件监听器

    • 创建一个文件输入元素,允许用户选择.txt文件。
    • 当文件被选中后,使用FileReader读取文件内容。
    • 读取完成后,将文本内容赋值给originalText并更新textToType
  4. addTextBtn 点击事件监听器

    • 弹出一个输入框让用户输入文本。
    • 如果用户输入了文本,将其赋值给originalText并更新textToType
  5. startPracticeBtn 点击事件监听器

    • 设置startTime为当前时间。
    • 清空输入框。
    • 更新textToType的HTML内容为原始文本。
  6. 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>
  • 41
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

睿智的海鸥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值