简介:这套仿京东数码频道的动态网页项目,以前端三大核心语言 HTML、CSS、JavaScript 完成,主要面向前端初学者、电商页面仿写练习者,以及需要课程设计或期末作品参考的高校学生。项目以数码商品展示为主线,完整还原电商首页常见的导航栏、搜索区、轮播 Banner、商品分类区、商品瀑布流与购物车入口等模块,能够直观看到静态布局如何通过样式和脚本变成可点击、可切换的动态页面。压缩包内共 20 个文件,包括 2 个 HTML 页面、1 个 CSS 样式文件,以及 17 张 JPG/PNG 图片素材,整体仅 503KB,体积小巧,解压后无需复杂配置即可在浏览器中打开运行。目前已有 134 人学习,资源虽小,却覆盖了响应式布局、CSS 伪类交互、DOM 操作和轮播切换等关键知识点,适合边看边改边练。对照页面结构可以学习电商导航和商品展示区的组织方式,结合 JavaScript 代码还能理清点击响应、滚动加载、动态渲染列表等常见交互的实现脉络,是快速提升前端综合技能的实用练手资料。
1. 仿京东数码动态电商页:先搞清楚这套模板里有什么
如果你接过电商首页的私活,或者正处于“HTML+CSS+JS 学完了但不知道能做什么”的阶段,这套仿京东数码的静态页资源会是一个很合适的练手样本。它没有 Vue 也没有 React,就是纯 HTML + CSS + JavaScript 写出来的整页电商模板,包含主页面、二级页面、完整的切片图、背景图、按钮箭头素材。你能从里面拆出电商首页最常见的模块:顶部导航、搜索区、商品楼层、轮播位、hover 动效,以及一套可以直接换皮的图片资源目录。适合三类人:想复现电商页面布局的前端初学者、需要快速出 demo 的接活开发者、以及想研究早年电商页面“纯静态也能做出动态感”的老玩法的人。
2. 从 HTML 骨架到页面结构:把京东数码.html 拆成能改的模块
2.1 头部与导航:搜索框、logo、用户工具区的常规写法
打开京东数码.html,你会发现它的头部结构高度接近真实电商首页:左上角 logo,中间是搜索框,右侧是“我的订单”“购物车”这类用户工具入口。常见的实现方式是用<div class="header">包住三个子容器,然后靠 CSS 的 flex 或 float 把三者排到同一行。
<div class="header"> <div class="header-logo"> <a href="#"><img src="images/logo.jpg" alt="logo"></a> </div> <div class="header-search"> <input type="text" placeholder="搜索数码商品"> <button class="search-btn">搜索</button> </div> <div class="header-user"> <a href="#">我的订单</a> <a href="#">购物车</a> </div> </div>这里我把素材里的logo.jpg放进了左侧 logo 区,nav_ba.jpg通常是导航栏的背景图,用来铺在搜索框或导航条底层。实际项目里nav_ba.jpg多半是一条细长重复纹理图,你可以把它平铺在导航条上,或者在 CSS 里直接background: url('images/nav_ba.jpg') repeat-x;。
新手最容易忽略的是:搜索按钮不要用<div>硬写,应该用<button>,这样回车键的默认提交行为才正常。用户工具区里的链接也不需要复杂逻辑,静态页阶段直接<a>就好。真正的交互(比如购物车角标变化)是后面 JavaScript 章节的事。
2.2 商品楼层与右侧分类导航:这页面的主体是靠重复结构撑起来的
往下滚动页面,你会发现主体是“楼层”结构——每一层一个标题栏、一张 banner 图、一排商品格子。素材里的banner.jpg、con_ima_r1_c1.jpg、con_ima_r1_c3.jpg、con_ima_r1_c5.jpg就是这些楼层里被反复使用的商品图或区块图。con_ima这个前缀我推测是 “content image” 的缩写,r1表示第 1 行,c1/c3/c5表示第 1/3/5 列,这种命名习惯在电商切图里很常见。
<div class="floor"> <div class="floor-title">数码配件</div> <div class="floor-banner"> <img src="images/banner.jpg" alt="楼层banner"> </div> <ul class="floor-goods"> <li><img src="images/con_ima_r1_c1.jpg" alt="商品1"></li> <li><img src="images/con_ima_r1_c3.jpg" alt="商品2"></li> <li><img src="images/con_ima_r1_c5.jpg" alt="商品3"></li> </ul> </div>写这段的时候我遇到过一个典型问题:把“楼层”理解成了“页面”,于是每个楼层都复制了完整的<header>,结果页面代码膨胀三倍。正确做法是<header>只写一次,楼层部分只是<body>内的一段重复区块。你如果想把某些楼层做成懒加载,可以在数据层面用 JS 数组存内容,再循环生成<li>,这个思路在第 4 章会展开讲。
2.3 京东数码.html 与 234.html:两个页面各自的定位与切换方式
这套资源里还有第二个页面234.html,对应预览图网页234 效果图.png。它跟主页面是什么关系?我拆完后的判断是:京东数码.html是完整的商城首页,234.html更像一个专题落地页或商品列表页——结构更简单,重点是某个商品系列或活动主题。
| 文件 | 定位 | 主要模块 |
|---|---|---|
| 京东数码.html | 商城首页 | 头部、导航、楼层、商品展示、banner |
| 234.html | 专题/列表页 | 精简头部、商品列表、活动说明图 |
你如果想让两个页面互相跳转,直接在链接里写相对路径就行:首页加href="234.html",专题页加href="京东数码.html"。注意文件名里的中文和大写字母——本地 Windows 不区分大小写,但部署到 Linux 服务器后JD.html和jd.html会被当成两个文件,这个坑我放在第 5 章避坑部分专门讲。
3. CSS 布局与动效落地:涟漪光圈、流光边框与悬停反馈
3.1 布局选型:Flex 为主,绝对定位为辅,别一上来就 Grid
拿到这套模板后,第一件事是先看它的 CSS 文件用什么布局方式。从这套页面里的结构来看,主体段落用 Flex 是合理的:头部三个区块横向排列、楼层商品横向滚动、导航条等分布局,Flex 的justify-content: space-between和flex-wrap: wrap能覆盖绝大多数场景。
.floor-goods { display: flex; flex-wrap: wrap; gap: 10px; list-style: none; padding: 0; margin: 0; } .floor-goods li { flex: 0 0 calc((100% - 20px) / 3); text-align: center; }这里flex: 0 0 calc((100% - 20px) / 3)的意思是:每个商品卡片占行宽的三分之一,20px是 2 个gap的总和,三个卡片之间刚好留出统一的间距。如果把flex写成1 1 200px,卡片就会随容器宽度自动伸缩换行,适合做响应式。这套模板里我建议用固定比例,因为它是固定宽度的整页设计稿。
关于 CSS 的五种布局方式——常规流、浮动、Flex、Grid、绝对定位——这套页面里其实只用了两三种。浮动主要用于图片文字混排,Grid 在这种固定稿里容易过度设计,除非你要做复杂二维布局。绝对定位主要用在 hover 浮层、返回顶部按钮这类脱离文档流的元素。我一般会先把主体 Flex 调通,再处理定位细节,顺序反了会非常难受。
3.2 悬停动效:涟漪光圈扩散、流光边框与按钮反馈
“纯 CSS 能做出什么动态效果?”这套页面给了个不错的答案——不需要 JavaScript,hover 加 transition 就能做出类似涟漪光圈扩散的感觉。原理很简单:一个伪元素从scale(0)放大到scale(1),同时透明度从 0.6 降到 0,视觉上就是光圈向外扩散。
.btn { position: relative; overflow: hidden; border: none; background: #e93b3b; color: #fff; padding: 10px 24px; cursor: pointer; } .btn::after { content: ""; position: absolute; left: 50%; top: 50%; width: 20px; height: 20px; background: rgba(255, 255, 255, 0.4); border-radius: 50%; transform: translate(-50%, -50%) scale(0); transition: transform 0.5s ease, opacity 0.5s ease; opacity: 0; } .btn:hover::after { transform: translate(-50%, -50%) scale(10); opacity: 0; }这里的scale(10)表示放大到 10 倍,20px 的原点放大后能覆盖整个按钮。overflow: hidden是必须的——没有它,光圈会跑到按钮外面去。过渡耗时0.5s,太快像闪一下,太慢显得拖拉。我给这个类效果取名叫涟漪光圈扩散,你在搜索 CSS 动效素材时经常能看到类似实现。
至于流光边框,常见做法是把一条渐变背景放在伪元素里,通过background-position的移动来模拟光线扫过。想做得更精致,可以叠加mask或clip-path,但这套页面用不上那么复杂的东西。记住一个原则:这类动效全用 CSS 做,比用 JS 改 style 的性能好,也不会触发 JavaScript 的布局抖动。
3.3 素材适配:jpg 与 png 的取舍,以及透明度问题的处理
这套模板里的图片资源既有产品图也有背景切片,大部分是 jpg 格式。jpg 适合照片级内容,文件小;png 适合需要透明背景的图标、按钮、箭头。素材里的arr.jpg和li_bg.jpg,一个显然是箭头图标,一个是列表背景。箭头如果带白底,放进深色按钮里就会出现白块,这就是原素材选错了格式的典型问题。
处理办法并不复杂:把arr.jpg导入到任意在线图片去背景工具中转成 png,或者直接在 CSS 里给箭头图片换背景色。代码层面能做的兜底是:
.floor-goods li img { background: #fff; mix-blend-mode: multiply; }mix-blend-mode: multiply可以去掉白底——白色在正片叠底模式下会消失。这个技巧在页面素材不统一时特别有用。但注意,multiply 会让整张图变暗,颜色的还原度和原图有差距,治标不治本。真正负责任的做法是把商品图重新切一遍,统一成 png 或统一 jpg 白底,并在页面里约定好每张图的显示尺寸,避免 CSS 把图片硬拉变形。
4. JavaScript 交互:轮播、Tab 与倒计时的实现思路
4.1 轮播图与 Tab 切换:用数据驱动渲染,而不是写死 DOM
这套页面里的动态效果集中在几个交互点:banner 轮播、楼层 Tab 切换、返回顶部。我先说轮播——静态页里最容易写成“定死”的地方。很多初学者会直接写三张<img>然后用display:none切来切去,这能用,但如果你想后面换成从接口拿数据,就要重写。我建议从一开始就把轮播内容放进数组,用 JavaScript 生成 DOM。
const slides = [ { img: "images/banner.jpg", link: "234.html" }, { img: "images/con_ima_r1_c1.jpg", link: "#" }, { img: "images/con_ima_r1_c5.jpg", link: "#" } ]; let currentIndex = 0; const bannerBox = document.querySelector(".banner-box"); function renderSlide(index) { const item = slides[index]; bannerBox.innerHTML = `<a href="${item.link}"><img src="${item.img}" alt="banner"></a>`; } function nextSlide() { currentIndex = (currentIndex + 1) % slides.length; renderSlide(currentIndex); } renderSlide(0); setInterval(nextSlide, 3000);currentIndex = (currentIndex + 1) % slides.length是循环轮播的核心:索引到末尾时取模回到 0,实现无缝循环。setInterval(nextSlide, 3000)表示每 3 秒换一张。注意这个写法的前提是渲染函数足够轻量——每次替换innerHTML会销毁重建 DOM,如果轮播图里还有子组件,就会连带销毁重建,产生闪烁。更平滑的做法是让所有图片常驻 DOM,只切换透明度和位置,这个就看你的页面复杂度决定了。
4.2 倒计时与数字加载动画:Date 时间差计算与补零处理
电商页面离不开倒计时——限时秒杀、双十一预热、优惠券失效时间。这套页面如果加倒计时,我不建议用服务器时间,因为静态页没有后端,直接用本地时间Date.now()就好,误差几秒内可以接受。常见做法是定一个目标时间戳,然后每秒计算差值。
function countdown(targetTime) { const timer = setInterval(() => { const now = Date.now(); const diff = targetTime - now; if (diff <= 0) { clearInterval(timer); document.querySelector(".countdown").innerHTML = "活动已结束"; return; } const hours = Math.floor(diff / (1000 * 60 * 60)); const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((diff % (1000 * 60)) / 1000); document.querySelector(".countdown").innerHTML = `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`; }, 1000); } countdown(new Date("2025-12-31 23:59:59").getTime());padStart(2, "0")是 ES2017 的字符串补零方法,9会变成09。如果目标浏览器太老,可以用("0" + hours).slice(-2)替代。有个细节很容易翻车:setInterval的 1000ms 并不是严格一秒,浏览器标签页切到后台会暂停定时器,回来时倒计时会短暂错位。解决粒度要求不高的场景足够了——如果要求高,就把“每秒渲染一次”改成“每次渲染时重新取当前时间”,而不是依赖自增计数。
4.3 一键返回顶部:不用 jQuery 也能写得干净
返回顶部按钮在京东这类长页面里是标配。你打开页面按 F12 看这个模板,大概率能在某张图片或<div>里找到三角形的箭头素材,就是前面说的arr.jpg。把它固定到右下角,点击后让窗口平滑滚动到顶部。
const backTop = document.querySelector(".back-top"); window.addEventListener("scroll", () => { const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; backTop.style.display = scrollTop > 200 ? "block" : "none"; }); backTop.addEventListener("click", () => { window.scrollTo({ top: 0, behavior: "smooth" }); });document.documentElement.scrollTop拿的是<html>的滚动距离,document.body.scrollTop是<body>的滚动距离,两个都写是为了兼容不同浏览器解析标准。按钮在滚动距离超过 200px 时才显示,避免页面一开始就露出一个无意义的按钮。behavior: "smooth"是平滑滚动,老浏览器不支持,可以退化用window.scrollTo(0, 0)。如果你希望滚动速度更可控(比如先快后慢),就得自己写 requestAnimationFrame 动画,那段逻辑比较长,这个模板用不上。
另外注意,返回按钮把arr.jpg作为背景图或<img>展示时,如果它原本是白色箭头的 jpg,在深色按钮上可能看不清楚——这就是第 3 章说的素材格式问题,建议换成 png 版本再实际看一下效果。
5. 避坑:仿京东页面最容易翻车的五个位置
5.1 图片路径大小写与缺失兜底
现象:页面在本地双击打开一切正常,传到服务器后大量图片裂开,控制台全是 404。
原因:Windows 文件系统不区分大小写,logo.jpg写成Logo.jpg也能打开;但 Linux 服务器严格区分,路径对不上就直接 404。此外,文件名里的中文(比如微信图片_20190423221402.jpg)在服务器编码不一致时也可能加载失败。
解决:拿到资源后第一件事是把所有文件名统一成小写英文加下划线,然后全局搜索 HTML 里的src和 CSS 里的url(),逐一核对。写一个小脚本批量替换比手改可靠。
# 把所有 jpg/png 文件重命名为小写 for f in images/*.jpg images/*.png; do mv "$f" "$(echo "$f" | tr '[:upper:]' '[:lower:]')" 2>/dev/null donetr '[:upper:]' '[:lower:]'会把大写字母转小写,2>/dev/null把已经小写的文件报错信息丢掉,无伤大雅。执行完再用 VS Code 全局搜索替换 HTML 里的路径引用。
5.2 hover 闪烁与第一帧空白
现象:鼠标移上按钮或菜单项时,背景图先闪一下白,然后图案才出来;快速来回移动时图片反复闪烁。
原因:hover 样式里的背景图是鼠标移上后才开始加载,第一次触发时图片还没下载完,所以出现空白帧。反复闪烁是因为每次 hover 都在重新设置背景位置,浏览器频繁重绘。
解决:把 hover 用到的图片提前加载。最土的办法是在 CSS 里加一个看不见的body::after把图片引用一次;更规范的做法是把多张状态图合并成雪碧图,hover 只改变background-position。
body::after { content: ""; position: absolute; background-image: url("images/nav_ba.jpg"), url("images/li_bg.jpg"); visibility: hidden; }visibility: hidden会让图片占位但不可见,浏览器依然会把它下载到缓存里。这样实际鼠标移上时,图片已经在本地缓存中了,不会闪。雪碧图能减少 HTTP 请求数,对性能也有帮助。
5.3 轮播定时器叠加导致越切越快
现象:手动点击轮播的左右按钮后,页面切换速度明显变快,甚至每张图停留不到一秒就跳走。
原因:手动切换的点击事件里又启动了一个setInterval,旧的定时器没有被清除。两个定时器同时在跑,nextSlide()被重复调用,所以速度翻倍。每点一次就多一个定时器,页面越来越快到肉眼跟不上的地步。
解决:每次创建新定时器之前,先清除旧定时器。用clearInterval把定时器 ID 清掉再重新设置。
let timer = setInterval(nextSlide, 3000); function resetTimer() { clearInterval(timer); timer = setInterval(nextSlide, 3000); } document.querySelector(".banner-prev").addEventListener("click", () => { currentIndex = (currentIndex - 1 + slides.length) % slides.length; renderSlide(currentIndex); resetTimer(); });注意currentIndex = (currentIndex - 1 + slides.length) % slides.length,这里加slides.length是为了防止索引变成 -1——取模在 JavaScript 里对负数返回负值,直接写(currentIndex - 1) % slides.length在 index 为 0 时会得到 -1,图片显示就错了。
5.4 固定宽度页面在手机端直接垮掉
现象:页面设计宽度是 1440px,用手机访问时整体横向溢出,右侧一大块内容看不到,缩放后字体小得没法读。
原因:这套静态稿没写响应式适配,所有容器都是固定像素宽度。手机浏览器实际视口宽度只有 375px 左右,1440px 的页面当然装不下。
解决:加一个基础的视口 meta 和两组媒体查询,至少让页面在手机上能看。
<meta name="viewport" content="width=device-width, initial-scale=1.0">@media (max-width: 768px) { .header { flex-direction: column; align-items: center; } .floor-goods li { flex: 0 0 calc((100% - 10px) / 2); } }max-width: 768px覆盖平板竖屏和手机横屏,flex-direction: column让头部竖排,商品卡从一行 3 个变成一行 2 个。如果还想更细,再加一组max-width: 480px把商品卡变成一行 1 个。响应式不是这套模板的强项,但你的上线环境如果是移动端,这步省不掉。
5.5 素材版权与上线合规
现象:用这套资源里的京东 logo 和商品图做了个网站,发出去后被品牌方投诉。
原因:仿京东只是学习演练,但 logo 属于京东的商标,商品图也有版权。直接部署上线商用,会构成侵权风险。这类问题在私活里尤其容易踩中——客户看到效果满意,顺手就要求直接上线。
解决:写代码阶段可以原样使用素材做还原测试,但交付前必须替换。把logo.jpg换成自己的占位图,商品图换成无版权图库(比如 Unsplash、Pexels)的图,或者用占位图服务via.placeholder.com过渡。不要心存侥幸,品牌方发律师函的速度远比你改图快。这点是纯粹的合规习惯,不是技术问题,但接私活的人最容易在这里翻车。
6. 进阶:把静态楼层改成带懒加载与滚动进场动画
6.1 用 IntersectionObserver 实现图片懒加载
楼层页面图片多,一次性全部加载会拖慢首屏速度。懒加载的核心逻辑是:图片先不设真实src,等它滚动到视口附近再赋予src。现代浏览器直接用 IntersectionObserver 做,比滚动事件监听更高效,也不会卡顿。
<img>const lazyImages = document.querySelectorAll(".lazy-img"); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; observer.unobserve(img); } }); }, { rootMargin: "100px" }); lazyImages.forEach(img => observer.observe(img));rootMargin: "100px"表示图片进入视口下方 100px 范围内就开始加载,比真正滚到再加载提前了一步,体感上几乎没有闪烁。unobserve(img)是加载完成后取消监听,避免重复触发。需要注意:没有 JS 降级时,爬虫和禁用 JS 的用户看不到图片,所以生产环境建议配合<noscript>标签给一个兜底<img>。
6.2 给楼层入场加一个轻量滚动动画
纯 CSS 的transition加上 JS 的 class 切换,就能做出楼层从透明到浮现的入场效果。关键是初始状态和最终状态要分别定义清楚。
.floor { opacity: 0; transform: translateY(30px); transition: opacity 0.6s ease, transform 0.6s ease; } .floor.visible { opacity: 1; transform: translateY(0); }const floors = document.querySelectorAll(".floor"); const floorObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add("visible"); floorObserver.unobserve(entry.target); } }); }, { threshold: 0.2 }); floors.forEach(floor => floorObserver.observe(floor));translateY(30px)让楼层原本往下偏移 30px,进入视口后再回到原位,配合透明度变化形成“浮上来”的效果。threshold: 0.2表示楼层有 20% 面积进入视口才触发动画。注意 animation 和 transition 的时长不能太夸张,电商页面追求信息效率,动画超过 0.8 秒就会让用户觉得卡。
这套从静态稿到可部署页面的大致改造流程,我每次都会走一遍:先统一图片路径,再拆 DOM 结构,然后加交互和动效,最后检查手机端表现。资源本身是完整可跑的,但真正变成你自己的作品,靠的是把素材换成自己的、把功能按场景做取舍。从那以后我每次拿到这类静态稿,都强制先替换一张图片跑通全流程再扩展功能,省得改到一半发现素材不符合预期。希望帮到你。
本文还有配套的精品资源,点击获取