大家好~ 今天带大家手把手实现一个「本地美食清单」应用,支持添加美食、展示清单,还会穿插前端基础知识点和实用技巧,新手也能轻松跟上!话不多说,开干!💪
🌟 项目预览与目标
先看看我们要做什么:
一个简洁美观的美食清单工具,用户可以输入美食名称并添加到列表中,页面有优雅的布局和交互效果。最终效果如下(示意图):
-
居中的半透明卡片布局
-
背景图自适应显示
-
表单提交添加美食项
-
支持后续扩展本地存储(localStorage)
涉及技术栈:HTML5 结构 + CSS3 样式 + JavaScript 交互,适合巩固前端基础的小伙伴~ 😊
📂 项目文件结构
先理清我们的文件组织,清晰的结构是高效开发的开始:
美食清单应用/
├─ index.html # 页面主结构
├─ common.css # 样式表
└─ common.js # 交互逻辑
🔨 step 1:HTML 结构搭建(语义化先行)
HTML 是页面的骨架,我们用语义化标签搭建清晰的结构,方便后续维护和 SEO。
核心代码解析(index.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 移动端适配关键配置 -->
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">
<title>Local TAPAS</title>
<link rel="stylesheet" href="./common.css"> <!-- 引入样式 -->
</head>
<body>
<div class="wrapper"> <!-- 主容器 -->
<h2>Local TAPAS</h2> <!-- 标题 -->
<p>请添加您的TAPAS</p> <!-- 提示文本 -->
<ul class="plates"> <!-- 美食列表容器 -->
<li>Loading Tapas...</li> <!-- 初始占位文本 -->
</ul>
<form class="add-items"> <!-- 添加表单 -->
<input
type="text"
placeholder="Item Name"
name="item"
required <!-- 表单验证:不能为空 -->
>
<input type="submit" value="+ Add Item"> <!-- 提交按钮 -->
</form>
</div>
<script src="./common.js"></script> <!-- 引入交互逻辑 -->
</body>
</html>
关键知识点:
- viewport 配置 📱
width=device-width:让页面宽度等于设备宽度
user-scalable=no:禁止用户缩放,保证移动端布局一致性(来自 readme 知识点)
viewport-fit=cover:适配全面屏(避免刘海遮挡) - 语义化标签 📝
使用<ul>+<li>展示列表(符合语义),<form>处理用户输入,结构清晰且利于浏览器解析。
🎨 step 2:CSS 样式设计(视觉美化)
CSS 负责让页面「好看」,我们从布局到细节逐步优化,让界面既美观又易用。
1. 全局样式与布局
/* 基础重置与全局配置 */
html {
box-sizing: border-box; /* 盒模型:border和padding不影响宽高 */
min-height: 100vh; /* 最小高度为视口高度,保证全屏显示 */
/* Flex布局:让内容居中 */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
/* 背景图设置 */
background: url("http://wes.io/hx9M/on-la-la.jpg") center no-repeat;
background-size: cover; /* 背景图等比例缩放,填满容器(裁剪多余部分) */
}
* {
box-sizing: inherit; /* 继承html的box-sizing,统一盒模型 */
}
知识点:box-sizing: border-box 的妙用 🧩
默认情况下,元素的 width 不包含 border 和 padding,导致布局计算麻烦。设置 border-box 后,width 直接等于「内容 + border+padding」,布局更可控!
2. 容器与卡片样式
.wrapper {
padding: 20px;
min-width: 350px; /* 最小宽度,避免移动端过窄 */
background-color: rgba(123,255,255,0.95); /* 半透明青色背景 */
box-shadow: 0 0 0 10px rgba(0,0,0,0.1); /* 外边框阴影,营造立体效果 */
}
.wrapper h2 {
text-align: center;
margin: 0;
font-weight: 200; /* 轻量级字体,更优雅 */
}
细节亮点 ✨
rgba(123,255,255,0.95):半透明背景(alpha 值 0.95),既遮挡背景图又保留通透感box-shadow:用「0 0 0 10px」模拟边框,比直接设置border更有层次感
3. 列表与表单样式
/* 美食列表样式 */
.plates {
margin: 0;
padding: 0;
text-align: left;
list-style: none; /* 去掉默认列表圆点 */
}
.plates li {
border-bottom: 1px solid rgba(0,0,0,0.2); /* 底部边框分隔项 */
padding: 10px 0;
display: flex; /* 弹性布局,方便内容对齐 */
}
.plates label {
flex: 1; /* 占满剩余空间 */
cursor: pointer; /* 鼠标悬停显示手型,提示可点击 */
}
/* 表单样式 */
.add-items {
margin-top: 20px; /* 与列表保持距离 */
}
.add-items input {
padding: 10px; /* 输入框内边距,方便输入 */
outline: 0; /* 去掉默认聚焦边框 */
border: 1px solid rgba(0,0,0,0.1); /* 浅色边框,不突兀 */
}
交互优化 🖱️
- 列表项用
border-bottom分隔,视觉上更清晰 - 标签
cursor: pointer提示用户「可点击」,提升交互体验
💻 step 3:JavaScript 交互逻辑(功能实现)
JS 让页面「动起来」,我们实现「添加美食到列表」的核心功能,逐步完善逻辑。
1. 基础逻辑:获取元素与监听事件
// 获取DOM元素
const addItems = document.querySelector('.add-items'); // 表单元素
const itemsList = document.querySelector('.plates'); // 列表容器
// 存储美食列表数据(后续可持久化到localStorage)
let items = [];
// 表单提交事件处理函数
function addItem(e) {
e.preventDefault(); // 阻止表单默认提交(避免页面刷新)
// 获取用户输入的美食名称
const input = this.querySelector('[name="item"]');
const text = input.value.trim(); // 去除首尾空格
// 若输入不为空,则添加到列表
if (text) {
const item = {
text: text,
done: false // 标记是否完成(后续可扩展勾选功能)
};
items.push(item); // 存入数据数组
input.value = ''; // 清空输入框
renderItems(); // 重新渲染列表
}
}
// 监听表单提交事件
addItems.addEventListener('submit', addItem);
2. 渲染列表:将数据展示到页面
// 渲染列表函数
function renderItems() {
// 拼接HTML字符串(将数组数据转为DOM结构)
itemsList.innerHTML = items.map((item, index) => `
<li>
<label for="item${index}">${item.text}</label>
<input
type="checkbox"
id="item${index}"
${item.done ? 'checked' : ''}
>
</li>
`).join(''); // 数组转字符串
}
逻辑解析 🧠
items.map(...):遍历数据数组,生成每个列表项的 HTML${item.done ? 'checked' : ''}:动态添加勾选状态(为后续「完成」功能预留)join(''):将数组转为字符串,避免渲染时出现逗号
3. 扩展:本地存储(localStorage)
为了让数据在页面刷新后不丢失,我们用 localStorage 持久化保存数据:
// 从localStorage读取数据(页面加载时)
function loadItems() {
const storedItems = localStorage.getItem('tapasItems');
if (storedItems) {
items = JSON.parse(storedItems); // 字符串转对象
renderItems();
}
}
// 保存数据到localStorage
function saveItems() {
localStorage.setItem('tapasItems', JSON.stringify(items)); // 对象转字符串
}
// 优化addItem函数:添加后保存数据
function addItem(e) {
// ... 之前的逻辑 ...
if (text) {
// ... 添加item到items ...
saveItems(); // 新增:保存到本地存储
renderItems();
}
}
// 页面加载时读取数据
loadItems();
知识点:localStorage 用法 🗄️
localStorage是浏览器提供的本地存储方案,数据持久化(除非手动清除)- 只能存储字符串,因此需要
JSON.stringify()和JSON.parse()转换格式
📚 进阶知识点拓展
除了核心功能,这些知识点能让你对前端开发理解更深~
1. CSS 背景图适配:cover vs contain
background-size: cover:背景图等比例缩放,填满容器(可能裁剪部分图像),适合作为全屏背景background-size: contain:背景图等比例缩放,完整显示(可能留空白),适合需要展示完整图像的场景
2. Stylus 预处理器(高效写 CSS)
Stylus 是 CSS 超集,支持缩进语法、变量、模块化,让写 CSS 更高效:
# 安装Stylus
npm install -g stylus
# 编译.styl文件为.css(实时监听)
stylus --watch style.styl -o style.css
示例(Stylus 语法):
// 变量定义
bgColor = rgba(123,255,255,0.95)
.wrapper
padding: 20px
background: bgColor // 使用变量
box-shadow: 0 0 0 10px rgba(0,0,0,0.1)
3. 移动端适配:viewport 配置
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
width=device-width:页面宽度等于设备宽度user-scalable=no:禁止用户缩放,保证布局一致性- 移动端开发必加,否则页面可能被默认缩放导致错乱!
🎯 总结与扩展方向
到这里,一个基础的美食清单应用就完成了!功能包括:
✅ 添加美食到列表
✅ 数据本地存储(刷新不丢失)
✅ 响应式布局(适配移动端)
效果图
你还可以继续扩展:
-
添加「删除美食」按钮 🗑️
-
实现「批量勾选完成」功能 ✅
-
美化勾选样式(如文字划线效果)
希望这篇教程能帮你巩固 HTML、CSS、JS 基础,动手试试吧!有问题欢迎在评论区交流~ 😊