尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

HTML列表标签全解析:语义化、属性实战与电商按钮组实现

HTML列表标签全解析:语义化、属性实战与电商按钮组实现 刚接触HTML那会儿我也和别人一样觉得列表标签大概是整门语言里最没什么可讲的一个ul套几个li顶多再换换里面的文字还能玩出什么花样等到真正开始做页面才发现自己低估它了。导航栏是它们商品列表是它们面包屑是它们连电商网站上那两个扎眼的“加入购物车”“立即购买”按钮底层也经常是无序列表。这篇文章就来把有序列表、无序列表、定义列表这三种结构从头讲透再从一个实际需求出发——用无序列表做一个网购按钮组把HTML列表的使用场景、属性和细节一次聊清楚。适合刚学完HTML基础标签、正愁不知道该怎么用的人也适合写了两三个页面但从来没细想过列表语义的开发者。1. 三种列表的语义差别先搞清楚什么时候选哪个1.1 语义化解决的是“谁来看”的问题很多初学者会问一个问题ul和div做出来的东西浏览器渲染出来好像没什么区别为什么非要用列表标签这个疑问很常见但方向错了。列表标签的价值不在视觉而在语义——它告诉浏览器、搜索引擎蜘蛛和屏幕阅读器这里的内容是一组条目不是一个普普通通的容器。打个比方div就像超市里扔在地上的纸箱里面装的东西只有你一个人知道而ul、ol就像贴着分类标签的货架顾客一眼就知道这排是饮料、那排是零食不需要把每个箱子都打开翻一遍。搜索引擎的爬虫和读屏软件就是超市里的“顾客”。它们对div里是什么完全不敏感但对ul结构非常敏感因为它明确表达了“下面的li是一组并列内容”。这也就是为什么做SEO的人总强调列表标签要用对——不是玄学是让机器更容易理解你的页面结构。1.2 一份可以直接照抄的选择判断表与其死记标签定义不如记住下面这张判断表。遇到一个内容区块先问自己三个问题答案自然就会告诉你该用哪个标签。场景推荐标签判断理由操作步骤、排行榜、Top 10、时间线ol顺序变了内容含义就变了导航菜单、商品列表、标签列表、按钮组ul各条目地位平等顺序调换不影响理解名词术语、商品参数、键值对、FAQdl每一条都是“名词—描述”成对出现我有一个用了很多年的判断口诀如果你删掉其中一条剩下的内容还能不能讲得通能通用ul不能通用ol。比如“使用步骤”删掉第2步直接到第3步步骤就不完整了必须用ol。而导航菜单删掉“关于我们”剩下“首页、产品、联系”依然成立那就用ul。至于dl更特殊它根本不关心顺序关心的是“这个词条后面跟着的解释”是否成对。2. 有序列表ol自动编号背后的门道2.1 为什么不能自己手动加序号先看一个很典型的反面教材有人为了省事直接在li文字里写“1. 注册账号”“2. 完善资料”“3. 提交审核”。如果这个列表永远不变倒也看不出什么问题。但实际情况是需求永远在变——今天在中间插入一步“绑定手机”明天删掉一步“提交审核”后面所有编号都要手动改一遍。改错一个数字用户看到的就是“1、2、4、5”的诡异顺序。这时候用ol就省心得多。它最大的价值是编号由浏览器自动生成增删条目时其他编号自动重排。你只管保证li的先后顺序正确剩下的交给浏览器。这就是语义化标签带来的第一个实实在在的效率提升——把“维护编号”这件事从手动劳动里解放出来。2.2 type、start、reversed三个属性比想象中实用ol有三个属性初学者经常只见过type但start和reversed在实际项目中也很常用。先看type它决定编号的样式type值编号效果常见场景1默认1、2、3通用步骤aa、b、c附录、法律条款AA、B、C英文大纲ii、ii、iii前言、目录II、II、III正式文书start属性则用来指定起始编号。这里有个很容易踩的坑start的值永远是数字哪怕你设置了typeAstart也不能写字母。比如你想让编号从第C章开始代码是ol start3而不是ol startC。浏览器会根据type把数字3渲染成对应的字母C。很多人在这个细节上栽过跟头语法也没报错但编号就是不对劲原因就在这里。reversed属性用于倒序编号写法是ol reversed。比如“倒计时10、9、8、7”这种场景就很合适。不过要提醒一句reversed在旧版浏览器里偶尔会出现样式不一致的情况如果项目对老浏览器兼容性要求高我更建议用start手动算好起始值而不是盲目依赖reversed。2.3 用value属性强制指定某个条目的序号ol的另一个冷门用法是li上的value属性。它可以让某一条li的编号不按顺序走而是从指定的数字重新开始。举个例子一个待办清单里有5项你只想展示其中第3项和第5项但又希望编号保留原来的3和5就可以这样写ol li value3完成需求评审/li li value5发布上线/li /ol这个属性在我处理“从一堆数据里挑几条展示但保留原顺序号”的需求时特别好用。它不会影响其他li的编号连续性后面如果有第6项不写value的话会从6继续编号。这种细节如果不知道硬改HTML文本模拟编号不仅麻烦还会在前面的小节里引入今天讲到的“手动编号”这个坑。3. 无序列表ul为什么导航菜单都长这样3.1 ul li a一个标准导航结构打开任何一家成熟网站的源码导航栏的结构大概率是下面这一套ul classnav lia href/首页/a/li lia href/products商品/a/li lia href/about关于我们/a/li /ul外层ul是导航的“容器”中间每个li代表一个导航条目最里层的a才是用户真正点击的链接。这个三层结构不是随便定的而是HTML设计之初就为“一组并列内容”准备的。屏幕阅读器读到ul时会告诉用户“下面是一组链接共3项”这种无障碍提示对依赖读屏软件的人非常重要div嵌套的链接做不到这一点。更关键的是ul是后面CSS布局的绝佳载体。默认情况下li是块级元素一个占一行所以ul天然是纵向排列的。但只要你给ul加上display: flex或grid所有li立刻变成横向排列再配合justify-content调节间距一个导航栏的骨架就出来了。这也是为什么前端开发几乎没人用div堆导航——不是不能做而是ul做起来更顺手、语义也更清晰。3.2 常见的反面写法我见过不少新手直接把导航写成这样div classnav a href/首页/a a href/products商品/a a href/about关于我们/a /div乍一看浏览器里渲染效果差不多问题体现在三个地方。第一这堆a之间没有任何结构关系搜索引擎无法判断它们是不是一组导航权重理解上会有损失第二如果哪天要加一条分隔线或者给每个导航项加背景色你得给每个a单独写类名代码冗余第三在无障碍测试里读屏软件不会把这三段链接识别成“导航”只会当成三个无关链接依次念出来用户体验极差。还有一种常见写法是用span或者i做点击区域再绑JS事件。这种连a都不用的情况一般出现在纯交互按钮上比如“收起”“展开”。但如果它本质上是跳转链接还是应该用a如果它不是链接而是动作按钮则应该用button而不是ul下的li。这涉及另一个语义边界下面实战部分会详细展开。3.3 一个简洁可复用的导航菜单样板给一个最基础的横向导航菜单方便你直接试验header nav ul classnav lia href/首页/a/li lia href/products商品/a/li lia href/about关于我们/a/li /ul /nav /header.nav { list-style: none; margin: 0; padding: 0; display: flex; gap: 24px; } .nav a { text-decoration: none; color: #333; padding: 8px 12px; border-radius: 6px; } .nav a:hover { background: #f5f5f5; }这里的list-style: none和padding: 0是操作ul时几乎必写的两行。list-style去掉默认的项目符号padding去掉浏览器默认的左侧缩进。如果漏了padding导航栏会整体向右偏移看起来非常奇怪这是一个基础但高频的小问题。4. 定义列表dl字典式结构用对地方很加分4.1 dl/dt/dd到底在表达什么定义列表是三种列表里最容易被忽略的一个很多人学到ul和ol之后就把它跳过了。它实际上承担着一个非常独特的工作表达“名词—描述”的配对关系。dl是包裹整个列表的容器dt是术语名dd是术语的描述或解释。一个dt可以对应多个dd这在语义上是支持的。你可以把它理解成字典里一个词条下面有好几条释义比如dl dtHTML/dt dd超文本标记语言/dd dd用于构建网页结构的标记语言/dd /dl注意dt和dd是一组一组成对出现的但这个“成对”不像表格那样严格一个dt配多个dd是完全合理的。浏览器默认渲染时会适当缩进dd内容视觉上自然形成“小标题 正文”的层叠感。4.2 实用场景商品参数、FAQ、术语表定义列表最典型的应用场景是产品详情页的参数说明。比如dl classparams dt屏幕尺寸/dt dd6.1英寸/dd dt电池容量/dt dd4500mAh/dd dt运行内存/dt dd8GB/dd dt机身存储/dt dd256GB/dd /dl为什么推荐用dl而不是table来写参数因为参数本质上是一组“属性名—属性值”的键值对语义上和表格的单元格数据不同。而且dl在响应式布局上占便宜你可以用CSS很轻松地让dt和dd在同一行显示也可以让它们在小屏幕下变成两行堆叠结构不用改。如果用table布局调整要动DOM结构麻烦得多。另一个常见用法是FAQ问答列表dt放问题dd放答案。读屏软件可以明确区分问题与答案的层级关系对可访问性提升很明显。4.3 一个常见误解别拿dl当div用我会经常看到有人把dl当作万能容器页面里所有块级内容都往里面塞然后靠CSS调节布局。这是对语义标签的滥用。dl只在“名词—描述”成对出现时有意义。如果你写的dl里面找不到dt名词那就说明这个结构不该用dl。一个简单的自查方法把dl里的内容念出来问一句“这里有没有一个名词在被解释或描述”如果答案是“没有”请换回ul或div。语义标签不是越用越好而是用得越准确越好。乱用dl比不用dl更糟因为机器会被误导爬虫和读屏软件按定义列表的逻辑去解释内容最终反而影响SEO和无障碍体验。5. 实战用ul/li把电商购买按钮组从零做出来5.1 先拆解需求网购按钮组里到底有些什么现在来到标题里的重头戏——利用无序列表制作网购按钮。先描述一下需求场景一个商品详情页底部通常会有一排操作按钮最常见的是“加入购物车”和“立即购买”两个。有些页面还会多一个“收藏”或者旁边带一个数量选择器。这些按钮在语义上是什么它们是一组并列的操作项用户从左到右按区域点击。这个“并列操作项集合”的结构恰恰是ul最擅长表达的。这里要插一个容易纠缠的问题按钮里到底放a还是button我的经验是这样——如果点击后会跳转到购物车页或结算页用a因为它是链接如果点击后只是产生一个前端交互比如弹出提示、发送AJAX请求但页面不跳转用button因为它是动作。本文的例子是基于跳转场景所以用a标签通过class把它样式化成“按钮的视觉”。这个选择不是随意的而是对比了两种元素的语义后得到的结论。5.2 第一步搭出HTML结构先给一个最基础的购买按钮组HTMLul classbuy-actions lia href/cart.html classbtn add-cart加入购物车/a/li lia href/checkout.html classbtn buy-now立即购买/a/li /ul这段结构表达的语义是这里有一个操作列表包含两个动作第一个是加入购物车并跳转到购物车页第二个是立即购买并跳转到结算页。为什么用ul包着两个li而不是直接放两个a因为这两个a在功能上是“一组并列操作”用ul包裹后浏览器和爬虫能理解它们的关系。而且从布局角度看ul li的组合给了我们两个天然的“格子”谁该占多宽用flex配合flex属性就能精确控制。5.3 第二步重置默认样式并用flex横向排列给ul加上CSS让它变成我们熟悉的电商按钮形态.buy-actions { list-style: none; margin: 0; padding: 0; display: flex; gap: 12px; } .buy-actions li { flex: 1; } .btn { display: block; text-align: center; padding: 14px 0; border-radius: 24px; text-decoration: none; font-size: 16px; font-weight: 600; transition: background-color 0.2s ease; } .add-cart { background: #ff5000; color: #fff; } .buy-now { background: #fff; color: #ff5000; border: 2px solid #ff5000; }这段CSS有几个关键点值得展开。list-style: none和padding: 0是清理ul默认样式的固定操作display: flex让两个li从纵向变成横向gap: 12px替代了传统的margin方案好处是“最后一个元素右边不会多出间距”这个恼人的问题和gap说再见li上的flex: 1让两个按钮均分宽度不管容器多宽都能保证两个按钮大小一致。按钮内部的a标签display: block是必须的否则a作为行内元素padding得不到完整的高度撑开点击区域会变得很小尤其在移动端非常难操作。设置padding: 14px 0是为了保证按钮纵向高度足够14px是一个比较适中的触控值配合字体16px整个按钮高度大约在50px左右符合主流移动端的点击规范。5.4 第三步加上hover与按下反馈光有静态样式还不够电商按钮没有交互反馈就显得很“死”。加两段很轻量的CSS.add-cart:hover { background: #ff7333; } .buy-now:hover { background: #fff5f2; } .add-cart:active, .buy-now:active { transform: scale(0.98); }hover变色是最基本的反馈这里不再多说。我想重点提的是:active配transform: scale(0.98)这个小技巧。用户点击的瞬间按钮会缩小到原来的98%松手后恢复这种微小的物理按压感能明显提升操作的“确认感”。很多大厂的按钮都有这个细节不需要任何JS纯CSS就能实现。transition加在.btn上是为了让颜色变化和scale变化平滑过渡0.2s是一个经过验证的舒适区间太快会闪眼太慢会让人觉得拖沓。5.5 进阶把整个商品卡片也做成ul结构按钮组能用了但实际的电商页面里按钮组往往是嵌在商品卡片里的。这时候就牵扯出列表嵌套的能力外层ul做商品列表内层ul做按钮组层次清晰语义分明。ul classproduct-list li classproduct img srcproduct-1.jpg alt商品图 h3商品名称/h3 p classprice¥299/p ul classbuy-actions lia href/cart.html classbtn add-cart加入购物车/a/li lia href/checkout.html classbtn buy-now立即购买/a/li /ul /li li classproduct img srcproduct-2.jpg alt商品图 h3商品名称/h3 p classprice¥399/p ul classbuy-actions lia href/cart.html classbtn add-cart加入购物车/a/li lia href/checkout.html classbtn buy-now立即购买/a/li /ul /li /ul.product-list { list-style: none; margin: 0; padding: 0; display: flex; gap: 16px; } .product { flex: 1; border: 1px solid #eee; border-radius: 12px; padding: 16px; } .product img { max-width: 100%; border-radius: 8px; }这个例子把前面所有知识串起来了外层商品列表是ul因为商品之间是并列关系顺序调换不影响页面含义内层按钮组也是ul因为两个操作按钮同样是并列关系。这种“列表套列表”的结构在HTML层面完全合法也完全合理。它让整块内容的语义层次变得非常清楚外层是若干个商品每个商品下面有一组操作。5.6 一个真实场景里的数量选择变体有时候购买区不只是两个按钮还会带一个数量选择器。数量选择器本身也是一组操作减号、数字、加号。这个结构同样可以用ul组织ul classpurchase-area li classqty-item button typebutton classqty-btn minus-/button span classqty-num1/span button typebutton classqty-btn plus/button /li li classaction-item a href/cart.html classbtn add-cart加入购物车/a /li li classaction-item a href/checkout.html classbtn buy-now立即购买/a /li /ul这里我用了button来写加减号因为它们的功能是触发数值变化不是跳转链接。页面区域整体还是ul容器里面的每个li各司其职。如果你愿意可以把.purchase-area设成flex再让.qty-item不参与均分而是固定宽度右侧两个action-item用flex: 1占满剩余空间。这是典型的电商购买区布局。6. 列表实战中的三个高频坑6.1 坑一清理默认样式时下手太狠很多前端新手的CSS开头都会有一句* { margin: 0; padding: 0; }这句通配清理确实能抹平浏览器默认差异但副作用是所有ol、ul的缩进和项目符号也一起没了。等你想做一个真正的“步骤列表”时还得手动给ol加padding-left和list-style等于绕了一圈弯路。我自己的习惯是不依赖通配清理而是针对需要清理的元素单独写。比如在写首页头部时只对导航栏的ul加list-style: none正文里的ul保持默认样式。这样列表的默认外观“该在时还在”不会因为全局清理把症状统一抹掉。如果你已经写了通配清理记得在需要恢复默认列表样式的地方手动补上padding-left: 2em; list-style: disc;之类的代码。6.2 坑二li里直接塞块级结构li标签允许直接放文字比如li首页/li这种简单内容完全没问题。但当你开始往li里面塞图片、标题、段落、内嵌列表时要记住li可以直接包裹其他块级标签但最好先有一个直接的块级内容再继续嵌套。关键是子列表必须放在li的“内部”而不是放在li的“后面”。错误示范ul li一级菜单/li ul li二级菜单/li /ul /ul正确示范ul li 一级菜单 ul li二级菜单/li /ul /li /ul第一段代码里二级ul直接出现在一级ul的下方属于“兄弟”关系不是“父子”关系。浏览器解释时会把二级ul强行提升到和一级ul平级DOM结构就不是你想要的了。很多多级菜单导航样式错乱根因就是这个。6.3 坑三忽略超小屏幕下的按钮挤压使用flex: 1均分按钮时有个容易被忽视的问题当容器宽度不足比如手机屏幕320px以下“加入购物车”和“立即购买”两个词都有五个字左右按钮宽度会被压缩到很窄文字就会折行两个词叠成两排丑到不行。我的处理方式有两个方案。第一个是在按钮字号上用clamp(14px, 2vw, 16px)让字号在小屏上主动缩小第二个是把按钮padding左右调小只留上下padding文字不换行利用内边距变化保证点击区域依然够大。如果你实在不想缩字号也可以用flex-direction: column在超小屏上让两个按钮竖排牺牲视觉效果保住可读性。这个取舍没有标准答案取决于产品设计稿的底线宽度但提前想到总比上线后手忙脚乱要好。6.4 我的个人习惯先语义后样式做HTML列表这件事这些年给我最大的体会是结构的选择比样式的实现更影响后续的维护成本。写页面时先问自己“这组内容是什么关系”再动手写标签就会少走很多弯路。一个项目里导航是ul步骤是ol参数是dl按钮组是ul这些明明可以用div强行模拟但把语义摆对之后不仅是给机器看的更是给三个月后的自己看的——看到ul就知道这是一组并列内容看到ol就知道顺序可能有讲究看到dl就明白是词条与解释的关系。我写列表时还有个习惯class命名尽量和语义挂钩。比如.buy-actions比.orange-buttons强得多前者表达“这是购买操作区”后者只描述颜色。颜色明天就能换成蓝色但语义不会变。命名影响的是代码可读性列表影响的是页面语义两者都值得多花十秒钟想清楚。
返回列表