HTML5购物网站源码解析:登录、购物车与本地存储实战
简介这是一套面向前端初学者与进阶练习者的HTML5静态购物网站完整源码围绕登录注册、商品展示、详情页与购物车等核心模块展开帮助读者理解从页面搭建到交互逻辑的完整实现路径。压缩包共48个文件约5.28MB包含4个html页面、5个css样式表、8个js脚本以及16个png与14个jpg图片素材和1个json配置文件涵盖页面结构、样式美化、轮播与三级菜单交互、购物车增删改查等模块图片资源可直接用于页面展示。已有1041人学习下载说明其结构清晰、上手门槛适中。读者可据此掌握HTML5表单验证与本地存储、CSS3过渡动画与响应式布局、jQuery事件处理与DOM动态操作等知识点并参考现有目录组织方式快速搭建自己的购物网站原型适合课程设计、毕业设计或前端练手项目参考。1. 从零手搓一个带登录的 HTML5 购物网站这套静态页面源码能帮你省下多少试错时间如果你正在找一份能直接跑起来、结构清晰、还带完整登录流程的 HTML5 购物网站源码这套资源大概率能让你少熬两个晚上。它不是那种只有首页和几个死链接的“演示壳子”而是把商品列表、详情页、购物车、结算页、登录注册、订单确认这些电商核心页面都串了起来。技术栈走的是最经典的 HTML CSS JavaScript jQuery 组合没有构建工具没有 npm install浏览器双击就能看效果。适合谁前端新手拿来练手完整项目流程熟手拿来当静态站模板快速改造成活动页或产品展示站。我拆过不少类似源码很多号称“购物网站”的压缩包解压后只有三个 HTML 文件这套的页面完整度算是能打的。2. 项目结构拆解从文件目录看一个静态电商站该怎么组织2.1 目录层级与文件职责划分拿到源码包后我习惯先看目录结构这比读代码更快判断一个项目的成熟度。这套购物网站的目录组织遵循了静态站点的经典分层思路没有用 webpack 或 vite 那套构建体系所有资源都是浏览器直接可读的格式。典型的目录结构长这样shopping-site/ ├── index.html # 首页商品推荐与导航入口 ├── login.html # 登录页表单验证与跳转逻辑 ├── register.html # 注册页与登录页共享校验逻辑 ├── product-list.html # 商品列表页支持分类筛选 ├── product-detail.html # 商品详情页含规格选择与加购 ├── cart.html # 购物车页数量增减与金额计算 ├── checkout.html # 结算页收货信息与订单确认 ├── css/ │ ├── reset.css # 浏览器默认样式重置 │ ├── common.css # 公共样式头部、底部、按钮、表单 │ └── pages.css # 各页面专属样式 ├── js/ │ ├── jquery.min.js # jQuery 库文件 │ ├── common.js # 公共逻辑导航、登录态检查 │ ├── login.js # 登录注册表单校验与提交 │ ├── cart.js # 购物车增删改查与本地存储 │ └── product.js # 商品渲染与筛选 ├── images/ # 商品图、图标、占位图 └── data/ └── products.json # 商品模拟数据部分版本内联在 JS 中这个结构的好处是职责边界清楚。common.css管全站复用的按钮、表单、卡片样式pages.css只管单个页面的特殊布局。JS 也是同理common.js里放登录态检查、导航栏渲染这类每个页面都要跑的逻辑login.js只处理登录注册页的表单。我见过不少新手把几千行 CSS 全塞一个文件里改一个按钮颜色要翻半天这套源码的分层方式值得借鉴。2.2 页面间的跳转关系与登录态传递静态购物网站最容易被忽略的就是页面间的状态传递。没有后端 Session登录态怎么保持这套源码用的是localStorage存用户信息和登录令牌每个页面加载时由common.js统一检查。核心逻辑是这样的// common.js - 登录态检查与页面守卫 (function checkAuth() { // 从 localStorage 读取登录令牌 var token localStorage.getItem(shop_token); var userInfo localStorage.getItem(shop_user); // 需要登录才能访问的页面列表 var protectedPages [cart.html, checkout.html]; var currentPage window.location.pathname.split(/).pop(); // 如果当前页在保护列表里且没有登录态跳转到登录页 if (protectedPages.indexOf(currentPage) -1 !token) { // 记录来源页登录后跳回 localStorage.setItem(redirect_after_login, currentPage); window.location.href login.html; return; } // 已登录则更新页面上的用户信息展示 if (token userInfo) { var user JSON.parse(userInfo); $(.js-username).text(user.nickname || user.phone); $(.js-login-link).hide(); $(.js-user-info).show(); } })();这段代码做了三件事第一从localStorage读取登录令牌和用户信息第二判断当前页面是否属于需要登录才能访问的页面如果是且未登录就跳转第三已登录状态下更新页面头部的用户信息展示区域。参数说明shop_token是登录成功后写入的模拟令牌实际项目中应该由后端下发shop_user存的是用户昵称、手机号等展示信息redirect_after_login用来记录用户是从哪个页面被踢到登录页的登录成功后跳回去这个细节很多简易源码都漏了。注意静态站的登录态完全依赖浏览器本地存储清缓存或换浏览器就会丢失这是纯前端方案的天然限制不是代码 bug。2.3 商品数据的组织方式商品数据这块不同版本的源码处理方式不一样。有的把数据直接内联在product.js里有的单独抽了products.json。我更推荐后者因为改商品不用翻 JS 逻辑。// product.js - 从 JSON 加载商品并渲染列表 function loadProducts(category) { $.getJSON(data/products.json, function(data) { var list data.products; // 按分类筛选category 为 all 时展示全部 if (category category ! all) { list list.filter(function(item) { return item.category category; }); } // 渲染商品卡片到列表容器 var html ; $.each(list, function(index, item) { html div classproduct-card>!-- login.html 登录表单核心结构 -- form idlogin-form classform-box novalidate div classform-item label forlogin-phone手机号/label input typetel idlogin-phone namephone placeholder请输入11位手机号 maxlength11 autocompleteoff span classerror-tip idphone-error/span /div div classform-item label forlogin-password密码/label input typepassword idlogin-password namepassword placeholder请输入密码 maxlength20 span classerror-tip idpassword-error/span /div button typesubmit classbtn-submit登 录/button p classform-link还没有账号a hrefregister.html立即注册/a/p /form结构上用了novalidate关掉浏览器默认校验因为默认提示样式不可控而且不同浏览器表现不一致。每个输入框下面配一个error-tip的span用来显示自定义错误信息。maxlength限制输入长度autocompleteoff防止浏览器自动填充干扰测试。3.2 jQuery 表单校验与登录逻辑校验逻辑写在login.js里用 jQuery 绑定提交事件逐项校验后决定是否放行。// login.js - 登录表单校验与提交 $(function() { var $form $(#login-form); var $phone $(#login-phone); var $password $(#login-password); // 手机号校验11位数字1开头 function validatePhone() { var val $.trim($phone.val()); var reg /^1\d{10}$/; if (!val) { $(#phone-error).text(手机号不能为空); return false; } if (!reg.test(val)) { $(#phone-error).text(手机号格式不正确); return false; } $(#phone-error).text(); return true; } // 密码校验6-20位不能纯数字 function validatePassword() { var val $password.val(); if (!val) { $(#password-error).text(密码不能为空); return false; } if (val.length 6 || val.length 20) { $(#password-error).text(密码长度需在6-20位之间); return false; } if (/^\d$/.test(val)) { $(#password-error).text(密码不能为纯数字); return false; } $(#password-error).text(); return true; } // 失焦时即时校验提升体验 $phone.on(blur, validatePhone); $password.on(blur, validatePassword); // 提交时统一校验 $form.on(submit, function(e) { e.preventDefault(); // 阻止表单默认提交 var isPhoneOk validatePhone(); var isPwdOk validatePassword(); if (!isPhoneOk || !isPwdOk) { return false; } // 模拟登录请求实际项目应替换为 AJAX 调用后端接口 var phone $.trim($phone.val()); var password $password.val(); // 从 localStorage 读取已注册用户列表 var users JSON.parse(localStorage.getItem(shop_users) || []); var matched users.filter(function(u) { return u.phone phone u.password password; }); if (matched.length 0) { $(#password-error).text(手机号或密码错误); return false; } // 登录成功写入令牌和用户信息 var user matched[0]; localStorage.setItem(shop_token, token_ Date.now()); localStorage.setItem(shop_user, JSON.stringify({ phone: user.phone, nickname: user.nickname || (用户 phone.slice(-4)) })); // 跳回来源页或首页 var redirect localStorage.getItem(redirect_after_login) || index.html; localStorage.removeItem(redirect_after_login); window.location.href redirect; }); });逻辑说明校验分两层失焦时即时提示提交时统一拦截。手机号用正则/^1\d{10}$/校验密码要求 6-20 位且不能纯数字。登录验证从localStorage里读已注册用户列表做匹配匹配成功写入shop_token和shop_user然后跳转。参数说明shop_users是注册时写入的用户数组每个元素包含phone、password、nicknameshop_token用时间戳模拟实际项目应由后端生成redirect_after_login在页面守卫里写入登录成功后读取并清除。注意把密码明文存在 localStorage 里是纯前端演示的权宜之计真实项目必须走后端接口密码在传输和存储时都要加密。3.3 注册模块与登录的联动注册页和登录页共享大部分校验逻辑区别在于注册需要多存一份用户数据并且要检查手机号是否已注册。// register.js - 注册核心逻辑省略与登录重复的校验函数 $(#register-form).on(submit, function(e) { e.preventDefault(); // 复用登录页的校验函数实际项目中可抽到 common.js if (!validatePhone() || !validatePassword()) { return false; } var phone $.trim($(#reg-phone).val()); var password $(#reg-password).val(); var nickname $.trim($(#reg-nickname).val()) || (用户 phone.slice(-4)); // 读取已有用户列表 var users JSON.parse(localStorage.getItem(shop_users) || []); // 检查手机号是否已注册 var exists users.some(function(u) { return u.phone phone; }); if (exists) { $(#phone-error).text(该手机号已注册); return false; } // 写入新用户 users.push({ phone: phone, password: password, nickname: nickname }); localStorage.setItem(shop_users, JSON.stringify(users)); // 注册成功后自动登录并跳转 localStorage.setItem(shop_token, token_ Date.now()); localStorage.setItem(shop_user, JSON.stringify({ phone: phone, nickname: nickname })); window.location.href index.html; });注册逻辑比登录多了一步查重用Array.some判断手机号是否已存在。注册成功后直接写入登录态并跳首页省去用户再登录一次的操作。这里有个细节nickname如果用户没填就用手机号后四位拼一个默认昵称避免页面显示空白。4. 购物车与结算本地存储驱动的增删改查和金额计算4.1 购物车数据结构设计购物车是静态电商站最核心的交互模块数据怎么存直接决定了后续逻辑好不好写。这套源码用localStorage存一个数组每个元素是一条购物车记录。// cart.js - 购物车数据结构与初始化 // 单条购物车记录结构 // { // id: 1001, // 商品ID // name: 商品名称, // price: 99.00, // 单价 // image: images/1.jpg, // count: 2, // 购买数量 // spec: 红色 // 规格可选 // } // 读取购物车数据不存在则返回空数组 function getCart() { return JSON.parse(localStorage.getItem(shop_cart) || []); } // 写入购物车数据 function saveCart(cart) { localStorage.setItem(shop_cart, JSON.stringify(cart)); updateCartBadge(); // 更新头部购物车角标 } // 更新导航栏购物车数量角标 function updateCartBadge() { var cart getCart(); var total cart.reduce(function(sum, item) { return sum item.count; }, 0); $(.js-cart-count).text(total); }数据结构里id是商品唯一标识加购时如果已存在相同id的记录就增加count不存在就 push 新记录。updateCartBadge用reduce算总件数更新头部角标这个函数在加购、删除、修改数量后都要调用。4.2 加购、修改数量与删除的实现加购逻辑绑定在商品列表和详情页的按钮上通过事件委托处理动态渲染出来的按钮。// cart.js - 加购逻辑 $(document).on(click, .btn-add-cart, function() { var id $(this).data(id); // 从商品数据里找到对应商品实际项目中可能从缓存或接口取 $.getJSON(data/products.json, function(data) { var product data.products.filter(function(p) { return String(p.id) String(id); })[0]; if (!product) return; var cart getCart(); var existing cart.filter(function(item) { return String(item.id) String(id); })[0]; if (existing) { existing.count 1; // 已存在则数量加一 } else { cart.push({ id: product.id, name: product.name, price: product.price, image: product.image, count: 1, spec: }); } saveCart(cart); alert(已加入购物车); }); }); // 修改数量加号 $(document).on(click, .js-count-plus, function() { var id $(this).data(id); var cart getCart(); cart.forEach(function(item) { if (String(item.id) String(id)) { item.count 1; } }); saveCart(cart); renderCart(); // 重新渲染购物车列表 }); // 修改数量减号数量为1时不再减 $(document).on(click, .js-count-minus, function() { var id $(this).data(id); var cart getCart(); cart.forEach(function(item) { if (String(item.id) String(id) item.count 1) { item.count - 1; } }); saveCart(cart); renderCart(); }); // 删除商品 $(document).on(click, .js-remove-item, function() { var id $(this).data(id); var cart getCart().filter(function(item) { return String(item.id) ! String(id); }); saveCart(cart); renderCart(); });逻辑说明加购用事件委托绑定在document上因为商品卡片是动态渲染的直接绑在按钮上会失效。修改数量和删除都是先读购物车数组操作后再写回并重新渲染。减号操作加了count 1的判断防止数量减到 0 或负数。参数说明>// cart.js - 金额计算避免浮点数精度问题 function calcTotal(cart) { // 用整数分计算最后再转回元 var totalFen cart.reduce(function(sum, item) { return sum Math.round(item.price * 100) * item.count; }, 0); return (totalFen / 100).toFixed(2); } // 渲染购物车列表 function renderCart() { var cart getCart(); var $list $(#cart-list); if (cart.length 0) { $list.html(p classempty-tip购物车还是空的去逛逛吧/p); $(#cart-total).text(0.00); return; } var html ; cart.forEach(function(item) { html div classcart-item style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />

相关新闻

Flask+WebUploader大文件分片上传实战:断点续传与秒传实现

Flask+WebUploader大文件分片上传实战:断点续传与秒传实现

简介:这是一份面向Python Web开发者的分段上传与流式下载实战示例,基于Flask框架与WebUploader前端组件实现大文件分片上传、服务端合并及低内存流式下载,适合需要处理大文件传输场景的中级开发者参考学习。压缩包共26个文件,约42…

2026/10/18 13:06:46 阅读更多 →
MySQL购物网站数据库设计:事务、锁与防库存超卖完整方案

MySQL购物网站数据库设计:事务、锁与防库存超卖完整方案

简介:MyShop 商城系统数据库设计资源包,面向需完成 MySQL 课程设计、毕业设计或希望掌握电商数据库设计方法的初学者与开发者。资源围绕用户、地址、商品、购物车、订单与订单项六大核心数据模块展开,并从用户管理、商品管理、购物车管理、订…

2026/10/18 13:06:46 阅读更多 →
JMeter并发测试核心参数配置:线程数、Ramp-Up与Duration实战

JMeter并发测试核心参数配置:线程数、Ramp-Up与Duration实战

跑过几次JMeter并发测试之后,你会发现真正决定结果靠不靠谱的,往往不是脚本写得有多花哨,而是线程组里那几个参数怎么搭配。线程数、Ramp-Up、循环次数、Duration,每一项单独看都很好理解,但组合在一起就成了最容易踩坑…

2026/10/18 13:05:46 阅读更多 →

最新新闻

MySQL 定时 DDL 导致 mysqldump 备份失败:场景模拟、排查与优化建议

MySQL 定时 DDL 导致 mysqldump 备份失败:场景模拟、排查与优化建议

1. 问题背景生产环境中,MySQL 数据库每天凌晨 2 点执行定时 DDL 操作(如新增索引、修改表结构),随后触发 mysqldump 逻辑备份任务,但备份经常失败。本文通过模拟该场景,分析失败根因,并给出可落…

2026/10/18 13:45:06 阅读更多 →
前端教学3:文字中嵌入图片的几种方法

前端教学3:文字中嵌入图片的几种方法

图片嵌入文字的几种方法 图片与文字的排版关系&#xff0c;是网页设计中的基础技能。下面按"从简单到复杂"的顺序&#xff0c;逐一讲解每种方法的特点、代码和适用场景。 一、直接插入法&#xff08;<img> 行内图片&#xff09; 原理 <img> 是行内块…

2026/10/18 13:45:06 阅读更多 →
基于Python-CNN的玉米粒品质检测:图像分类模型实战指南

基于Python-CNN的玉米粒品质检测:图像分类模型实战指南

简介&#xff1a;这是一份基于 Python 与 PyTorch 的 CNN 玉米粒品质检测完整工程&#xff0c;面向入门深度学习的开发者、农业视觉方向研究者或需要快速搭建图像分类实训项目的学生。包内共256个文件&#xff0c;包含250张用于训练验证的玉米粒图像、3份环境说明与数据集标注文…

2026/10/18 13:45:06 阅读更多 →
多智能体AI重构国际化战略评估:从静态打分到动态博弈

多智能体AI重构国际化战略评估:从静态打分到动态博弈

1. 传统国际化评估方法&#xff0c;为什么在智能化公司这里失灵了1.1 我们过去那套"标准动作"到底卡在了哪我最早接触国际化战略评估&#xff0c;是在一家做企业级软件的公司。当时我们用的还是那套经典框架&#xff1a;SWOT分析、波特五力、再加上一个"目标国家…

2026/10/18 13:45:06 阅读更多 →
交换机接口类型:从物理层原理到真实组网决策

交换机接口类型:从物理层原理到真实组网决策

1. 这不是考背诵&#xff0c;是考网络底层思维的真实战场“交换机的接口类型”这八个字&#xff0c;表面看只是网络设备基础知识点&#xff0c;但放在面试场景里&#xff0c;它瞬间变成一块试金石——不是测你能不能复述“RJ-45、SFP、SFP、QSFP28”这些名词&#xff0c;而是看…

2026/10/18 13:45:06 阅读更多 →
快鹭办公、通达OA、奥哲、360织语怎么选:关注上手成本与日常易用性的对比横评

快鹭办公、通达OA、奥哲、360织语怎么选:关注上手成本与日常易用性的对比横评

从上手成本和日常易用性出发&#xff0c;一站式 AI 智能办公平台选哪个&#xff0c;关键看搭建入口是否降低门槛、日常操作是否集中、以及上线到习惯的迁移代价。本文围绕这条主线&#xff0c;比较快鹭办公、蓝信、360织语、奥哲、通达OA 五个同产品线候选&#xff1a;快鹭办公…

2026/10/18 13:44:05 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →