☰
原生前端三件套实战:手写模拟登录窗口项目
2026/10/15 23:47:07 网站建设 项目流程

简介:这是一份面向Web前端初学者与进阶练习者的实战素材,围绕HTML、CSS与JavaScript三件套,完整还原QQ登录窗口的视觉与交互效果,帮助读者在模仿经典界面中巩固布局、样式与脚本联动能力。压缩包共11个文件,约55KB,包含1个HTML页面、1个CSS样式表、1个JavaScript脚本,以及6张PNG和2张GIF图片素材,分别承担页面结构、视觉美化、交互逻辑与图标背景等角色,结构紧凑、开箱即用。目前已有860人学习下载,适合作为前端入门阶段的小型综合练习。通过拆解这套代码,读者可以掌握表单结构搭建、Flex或Grid响应式布局、输入框聚焦与按钮悬停过渡动画、表单非空校验、回车键触发登录、记住我本地存储以及自动聚焦等常见交互写法,同时体会图标引入与媒体查询适配移动端的处理思路,为后续独立开发登录类页面积累可复用的代码组织经验。

1. 从零手写一个模拟登录窗口:前端三件套的实战演练场

很多前端新手学完 HTML、CSS、JavaScript 的基础语法后,最大的困惑是“不知道能做什么”。课本上的例子要么太碎,要么太抽象,真正能拿得出手、放进作品集里的小项目少之又少。这个模拟登录窗口效果就是冲着这个痛点来的——它用最纯粹的原生三件套,复刻了一个大家每天都会见到的登录界面,包含输入框、密码框、登录按钮、记住密码勾选框,以及点击登录后的交互反馈。整个项目不依赖任何框架和第三方库,打开浏览器就能跑,代码量控制在几百行以内,非常适合拿来练手。如果你正在找第一个能完整跑通、又能讲清楚原理的前端小项目,这个资源值得花一个下午拆一遍。

2. 项目结构与环境准备:文件怎么放、浏览器怎么跑

2.1 目录结构与文件职责

拿到源码包后,第一件事是看清楚文件是怎么组织的。这个项目属于典型的静态页面结构,没有构建工具,没有 npm 依赖,解压后直接双击 HTML 文件就能在浏览器里看到效果。常见的目录布局是这样的:

qq-login-demo/ ├── index.html # 页面骨架,定义登录窗口的 DOM 结构 ├── css/ │ └── style.css # 所有样式规则,负责窗口外观与布局 ├── js/ │ └── main.js # 交互逻辑,处理点击、校验、状态切换 └── images/ └── avatar.png # 头像占位图(可选)

index.html只负责结构,不写行内样式;style.css集中管理所有视觉表现;main.js处理用户操作后的行为。这种分离方式虽然简单,但已经体现了前端工程化的基本思路——结构、表现、行为各司其职。如果你后续想改成 Vue 或 React 版本,这套拆分逻辑可以直接迁移过去。

2.2 运行环境与启动方式

这个项目对运行环境几乎没有要求。你只需要一个现代浏览器(Chrome、Edge、Firefox 都可以),不需要安装 Node.js,不需要配置本地服务器。直接找到index.html,双击打开,或者拖进浏览器窗口,就能看到登录界面。

如果你习惯用编辑器,VS Code 配合 Live Server 插件会更方便——右键选择“Open with Live Server”,改完代码保存后浏览器自动刷新,省去手动刷新的麻烦。常见做法是在项目根目录执行npx serve或python -m http.server,这样可以通过http://localhost:3000访问,避免某些浏览器对本地文件路径的限制。

提示:如果双击打开后样式没生效,先检查link标签的href路径是否正确,相对路径写错是新手最容易翻车的地方。

2.3 页面骨架的搭建要点

打开index.html,你会看到一个居中的登录卡片。核心结构大致如下:

<div class="login-box"> <div class="avatar"> <img src="images/avatar.png" alt="用户头像"> </div> <form class="login-form" id="loginForm"> <div class="input-group"> <label for="username">账号</label> <input type="text" id="username" placeholder="请输入账号" autocomplete="off"> </div> <div class="input-group"> <label for="password">密码</label> <input type="password" id="password" placeholder="请输入密码"> </div> <div class="options"> <label><input type="checkbox" id="remember"> 记住密码</label> <a href="#" class="forgot">忘记密码?</a> </div> <button type="submit" class="login-btn">登 录</button> </form> </div>

这里有几个细节值得注意。autocomplete="off"是为了防止浏览器自动填充干扰演示效果;type="password"让输入内容显示为圆点;label的for属性与input的id对应,点击文字也能聚焦输入框。这些看似不起眼的属性,在实际项目里直接影响用户体验。表单用form包裹而不是纯div,是为了后续可以监听submit事件,同时支持回车键提交。

3. 样式实现拆解:从居中布局到输入框动效

3.1 登录卡片的居中与阴影

让一个盒子在页面正中间,是 CSS 面试里的经典题。这个项目用的是 flex 方案,代码简洁且兼容性好:

body { margin: 0; height: 100vh; display: flex; justify-content: center; align-items: center; background: linear-gradient(135deg, #667eea, #764ba2); font-family: "Microsoft YaHei", sans-serif; } .login-box { width: 360px; padding: 40px 30px; background: #fff; border-radius: 12px; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2); }

height: 100vh让 body 占满整个视口高度,justify-content和align-items分别控制水平和垂直居中。背景用了线性渐变,比纯色更有层次感。box-shadow的四个参数依次是水平偏移、垂直偏移、模糊半径、颜色透明度,数值越大阴影越柔和。我一般会把阴影颜色设成带透明度的黑色,而不是纯黑,这样看起来更自然。

3.2 输入框的聚焦状态与过渡

输入框是用户交互最频繁的区域,视觉反馈必须清晰。项目里给输入框加了聚焦时的边框变色和轻微上浮效果:

.input-group { position: relative; margin-bottom: 20px; } .input-group input { width: 100%; padding: 12px 15px; border: 1px solid #ddd; border-radius: 6px; outline: none; transition: border-color 0.3s, box-shadow 0.3s; box-sizing: border-box; } .input-group input:focus { border-color: #667eea; box-shadow: 0 0 8px rgba(102, 126, 234, 0.3); }

outline: none去掉了浏览器默认的蓝色轮廓,改用自定义的边框和阴影来提示聚焦状态。transition让颜色变化有 0.3 秒的过渡,不会显得突兀。box-sizing: border-box确保 padding 不会撑大输入框宽度,这是布局不跑偏的关键。很多新手忘记加这一行,结果两个输入框宽度对不齐,排查半天才发现是盒模型的问题。

3.3 按钮的悬停与点击反馈

登录按钮的交互感直接影响用户对页面“精致度”的判断。项目里给按钮加了渐变背景、悬停变亮、按下缩小的效果:

.login-btn { width: 100%; padding: 12px; border: none; border-radius: 6px; background: linear-gradient(90deg, #667eea, #764ba2); color: #fff; font-size: 16px; cursor: pointer; transition: transform 0.1s, opacity 0.3s; } .login-btn:hover { opacity: 0.9; } .login-btn:active { transform: scale(0.98); }

:hover降低透明度模拟变亮,:active在按下时缩小到 98%,给用户一个“按到了”的物理反馈。cursor: pointer让鼠标移上去变成手型,这是按钮可点击的基本暗示。这些细节单独看都很小,但组合在一起,页面的完成度会明显提升一个档次。

4. 交互逻辑实现:校验、状态与事件绑定

4.1 表单提交与基础校验

main.js的核心任务是监听表单提交,拿到用户输入,做非空校验,然后给出反馈。先看主流程:

const loginForm = document.getElementById('loginForm'); const usernameInput = document.getElementById('username'); const passwordInput = document.getElementById('password'); loginForm.addEventListener('submit', function (e) { e.preventDefault(); // 阻止表单默认提交行为 const username = usernameInput.value.trim(); const password = passwordInput.value.trim(); if (!username) { alert('请输入账号'); usernameInput.focus(); return; } if (!password) { alert('请输入密码'); passwordInput.focus(); return; } // 模拟登录请求 fakeLogin(username, password); });

e.preventDefault()是必须的,否则表单会尝试刷新页面。trim()去掉首尾空格,防止用户输入一串空格绕过校验。校验失败时用focus()把光标移回出错的输入框,减少用户操作成本。fakeLogin是一个模拟函数,用setTimeout模拟网络请求延迟:

function fakeLogin(username, password) { const btn = document.querySelector('.login-btn'); btn.textContent = '登录中...'; btn.disabled = true; setTimeout(function () { btn.textContent = '登 录'; btn.disabled = false; if (username === 'admin' && password === '123456') { alert('登录成功'); } else { alert('账号或密码错误'); } }, 1000); }

按钮在请求期间禁用并改文字,防止重复点击。1 秒后根据写死的账号密码判断结果。真实项目里这里会换成fetch请求后端接口,但演示项目用setTimeout足够说明流程。

4.2 记住密码与本地存储

“记住密码”勾选框用localStorage实现,刷新页面后账号密码还在:

const rememberCheckbox = document.getElementById('remember'); // 页面加载时回填 window.addEventListener('load', function () { const saved = localStorage.getItem('loginInfo'); if (saved) { const info = JSON.parse(saved); usernameInput.value = info.username; passwordInput.value = info.password; rememberCheckbox.checked = true; } }); // 登录成功时保存 function saveLoginInfo(username, password) { if (rememberCheckbox.checked) { localStorage.setItem('loginInfo', JSON.stringify({ username, password })); } else { localStorage.removeItem('loginInfo'); } }

localStorage存的是字符串,所以用JSON.stringify和JSON.parse做序列化。页面加载时检查有没有保存过的信息,有就回填并勾选。注意这里存的是明文密码,真实项目里绝对不能这么做,演示项目为了直观才这样写。常见做法是只存账号,密码交给浏览器或 token 管理。

4.3 回车提交与输入框联动

除了点击按钮,用户习惯按回车提交。表单里的button type="submit"天然支持回车触发,但如果你用的是div模拟按钮,就需要手动监听键盘事件:

passwordInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') { loginForm.dispatchEvent(new Event('submit')); } });

dispatchEvent手动触发submit事件,复用已有的校验逻辑,避免重复写代码。另外,账号输入框按回车时自动跳到密码框,也是提升体验的小技巧:

usernameInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); passwordInput.focus(); } });

这些联动逻辑不复杂,但能让操作流畅很多。写交互的时候多问自己一句“用户下一步想干什么”,往往就能找到优化点。

5. 避坑与常见问题排查

5.1 样式不生效或布局错乱

现象:打开页面后登录框没有居中,或者输入框宽度超出卡片。原因:最常见的是box-sizing没设成border-box,导致 padding 撑大元素;其次是link标签路径写错,CSS 根本没加载。解决:在 CSS 顶部加全局重置* { box-sizing: border-box; },然后打开浏览器开发者工具的 Network 面板,看style.css是否返回 200。如果路径不对,根据实际目录调整href。

5.2 表单提交后页面刷新

现象:点击登录按钮,页面闪一下,控制台没有输出。原因:form的默认提交行为没有被阻止,浏览器刷新了页面。解决:确认submit事件回调里第一行就是e.preventDefault()。如果用的是button而不是input type="submit",检查type是否误写成了submit导致触发表单提交。

5.3 localStorage 数据不更新

现象:取消勾选“记住密码”后,刷新页面账号密码还在。原因:只写了保存逻辑,没有写清除逻辑,或者清除时 key 写错了。解决:在登录成功回调里判断勾选框状态,未勾选时调用localStorage.removeItem('loginInfo')。可以在开发者工具的 Application 面板里查看 localStorage 的实际内容,确认 key 和值是否正确。

5.4 移动端点击延迟与适配

现象:在手机上点击按钮有 300ms 延迟,或者页面宽度超出屏幕。原因:没有设置 viewport meta 标签,或者按钮用了click事件在移动端有延迟。解决:在head里加<meta name="viewport" content="width=device-width, initial-scale=1.0">。按钮点击可以改用touchstart事件,或者引入fastclick库。更简单的做法是给按钮加touch-action: manipulation,现代浏览器会自动消除延迟。

5.5 密码框输入中文导致校验异常

现象:在密码框里输入中文,提交后提示密码错误,但用户觉得自己没输错。原因:密码框虽然显示为圆点,但输入法状态下可能输入了中文字符。解决:在密码输入框上加inputmode="text"并监听compositionstart和compositionend事件,在中文输入法组合期间不做校验。更直接的做法是提交前用正则过滤掉非 ASCII 字符,或者提示用户切换到英文输入法。

6. 二次开发改造:从演示到可复用组件的思路

这个项目跑通之后,最有价值的部分是把它改造成自己能复用的东西。我一般会从三个方向动手:组件化、接口对接、视觉定制。

组件化改造:把登录框封装成一个可配置的函数或类,调用时传入标题、按钮文字、主题色等参数。比如:

function createLoginBox(options) { const { title = '登录', themeColor = '#667eea', onSubmit } = options; // 动态生成 DOM,绑定事件,返回容器元素 // 调用方只需要 document.body.appendChild(createLoginBox({...})) }

这样同一个登录框可以在不同页面复用,改主题色不用翻 CSS 文件。如果项目用 Vue 或 React,直接把模板和逻辑拆成单文件组件,props接收配置,emit抛出登录事件。

接口对接:把fakeLogin里的setTimeout换成真实的fetch请求:

async function realLogin(username, password) { const btn = document.querySelector('.login-btn'); btn.disabled = true; btn.textContent = '登录中...'; try { const res = await fetch('/api/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); const data = await res.json(); if (data.success) { // 跳转或更新状态 } else { alert(data.message || '登录失败'); } } catch (err) { alert('网络异常,请稍后重试'); } finally { btn.disabled = false; btn.textContent = '登 录'; } }

注意finally里恢复按钮状态,无论成功失败都要执行。错误处理要区分网络异常和业务失败,给用户的提示不一样。

视觉定制:改主题色只需要调整 CSS 变量。把颜色抽成:root里的自定义属性:

:root { --primary: #667eea; --primary-dark: #764ba2; --radius: 12px; }

然后所有用到颜色的地方改成var(--primary)。换主题时只改这几行,整个页面的配色就变了。这个习惯我从写第一个项目就开始养,后面维护成本低很多。

验证方法:改完之后,我会强制走一遍完整流程——空账号提交、空密码提交、错误密码、正确密码、勾选记住密码后刷新、取消勾选后刷新、手机浏览器打开看布局。每次改交互逻辑,这几步都跑一遍,能挡住大部分低级问题。从那以后我每次改表单相关的代码,都强制走一遍这个清单,省了很多返工的麻烦。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询