小白前端速通:用CSS3 target伪类实现无JS交互效果(附实战技巧)

小白前端速通:用CSS3 target伪类实现无JS交互效果(附实战技巧)
小白前端速通:用CSS3 target伪类实现无JS交互效果(附实战技巧)
“写啥JavaScript?地址栏里加个#号就能让页面跳探戈,这才是浪漫的前端。”——某位不想加班的切图仔
为什么还在写JavaScript做简单交互?
先别急着翻React文档,也别急着npm install某个弹窗组件。
很多时候,我们只是想点一下按钮,让某个区域高亮、让某个盒子出现、让某个提示滚到视野中央——这些“小动静”真用不着把webpack请出来。
CSS3的:target伪类就是官方白送的“零依赖交互库”。它躲在规范角落里,安静得像图书馆后排的学霸,但只要你喊它的名字(在URL里丢一个#),它立刻站起来给你表演一段街舞。
今天这篇长文,就把这位学霸拉到舞台中央,跳满五支舞:导航高亮、选项卡、模态框、手风琴、呼吸灯。
全程无JavaScript,代码管饱,注释管够,复制粘贴就能跑。
认识target伪类这位低调高手
:target的户口本很简单:
- 选择器语法:
:target - 监听对象:当前URL片段(hash)对应的元素
- 激活时机:hash与元素id匹配的瞬间
- 失效时机:hash被清空或换成别的id
一句话:谁的名字在地址栏#后面,谁就是:target的亲儿子,立刻继承你写的样式。
举个最直白的例子:
<!-- ① 导航 -->
<nav>
<a href="#cat">喵</a>
<a href="#dog">汪</a>
</nav>
<!-- ② 内容 -->
<article id="cat">我是一只猫</article>
<article id="dog">我是一只狗</article>
/* ③ 让被选中的动物变红 */
article:target {
color: #e91e63;
background: #fce4ec;
padding: 1rem;
border-radius: 8px;
}
点“喵”,地址栏变成...#cat,<article id="cat">瞬间变粉;点“汪”,换狗变粉。
没有任何JS,却完成了“点击切换高亮”的经典交互。
target怎么工作?原理讲透不绕弯
浏览器内部有个“当前目标”插槽。
当你点击<a href="#id">,浏览器做三件事:
- 把
#id塞进URL - 滚动页面让
id="id"的元素进入视口(默认行为) - 把“当前目标”指向该元素,同时触发
:target匹配
如果下一刻你用JS或者手动把hash改成#id2,老目标立刻失宠,新目标上位。
CSS是声明式语言,:target只是浏览器在样式计算阶段告诉它“现在谁是指定目标”,其余全靠开发者想象力。
动手试试:5个经典场景玩转target
下面每个场景都给“完整HTML+CSS”,直接扔文件里就能打开看效果。
为了阅读体验,代码折叠在注释里,复制时别漏行。
点击菜单项自动高亮当前页
需求:顶部导航三项,点谁谁高亮,且页面不跳转(纯单页感)。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>导航高亮</title>
<style>
/* reset */
*{margin:0;padding:0;box-sizing:border-box;}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif;}
/* 导航条 */
nav{
display:flex;
background:#673ab7;
padding:0 2rem;
}
nav a{
color:#fff;
text-decoration:none;
padding:1rem 1.5rem;
position:relative;
transition:background .3s;
}
/* 默认hover */
nav a:hover{background:#5e35b1;}
/* 关键:谁被target,谁就长条高亮 */
nav a:target{
background:#9575cd;
}
/* 为了让链接本身成为target,需要给a标签加id */
#nav-home:target{background:#9575cd;}
#nav-work:target{background:#9575cd;}
#nav-me:target{background:#9575cd;}
/* 内容区随便写点 */
section{padding:2rem;}
</style>
</head>
<body>
<nav>
<!-- 注意:href与id同名,点自己把自己变成target -->
<a id="nav-home" href="#nav-home">首页</a>
<a id="nav-work" href="#nav-work">作品</a>
<a id="nav-me" href="#nav-me">关于</a>
</nav>
<section>
<p>点上面导航,紫色条会跟随。全程无JS。</p>
</section>
</body>
</html>
小技巧:
如果嫌弃地址栏老出现#nav-home,可以用<a href="#/">回到顶部并清掉hash,高亮消失,实现“取消选中”。
无刷新切换选项卡内容
需求:经典“横向选项卡”,点标题换内容,不闪屏。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>纯CSS选项卡</title>
<style>
.tabs{width:600px;margin:2rem auto;}
/* 按钮排排坐 */
.tabs>label{
display:inline-block;
padding:.6rem 1.2rem;
background:#e0e0e0;
cursor:pointer;
margin-right:4px;
border-radius:6px 6px 0 0;
transition:background .25s;
}
/* 内容面板默认隐身 */
.tabs>.panel{
display:none;
padding:1rem;
background:#fafafa;
border:1px solid #e0e0e0;
border-radius:0 6px 6px 6px;
}
/* 关键:谁被target,谁显示 */
#tab1:target ~ #panel1,
#tab2:target ~ #panel2,
#tab3:target ~ #panel3{
display:block;
}
/* 同时把对应label高亮 */
#tab1:target ~ [for="tab1"],
#tab2:target ~ [for="tab2"],
#tab3:target ~ [for="tab3"]{
background:#fff;
border-bottom:1px solid #fafafa;
}
</style>
</head>
<body>
<div class="tabs">
<!-- 隐藏的三兄弟,专门负责被target -->
<a id="tab1"></a>
<a id="tab2"></a>
<a id="tab3"></a>
<label for="tab1"><a href="#tab1">东京</a></label>
<label for="tab2"><a href="#tab2">巴黎</a></label>
<label for="tab3"><a href="#tab3">纽约</a></label>
<div id="panel1" class="panel">东京的寿司真好吃</div>
<div id="panel2" class="panel">巴黎可颂香到飞起</div>
<div id="panel3" class="panel">纽约芝士蛋糕yyds</div>
</div>
</body>
</html>
解释:
- 用空
<a>做“靶子”,放在最前,负责被hash激活 - 后面的
<label>里再包一个真正的链接,用户点的是它,hash变化触发前面的空<a>成为:target - 利用兄弟选择器
~把对应的.panel显示出来
这样做的好处是:label文字样式随便玩,还能保持键盘无障碍。
纯CSS实现模态框弹出与关闭
需求:点击“登录”按钮,弹出遮罩+对话框,再点遮罩或关闭按钮消失。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>纯CSS模态框</title>
<style>
body{font-family:-apple-system;}
/* 触发按钮 */
.trigger{
display:inline-block;
padding:.8rem 1.4rem;
background:#2196f3;
color:#fff;
border-radius:4px;
text-decoration:none;
}
/* 模态框本体:默认透明且点不到 */
.modal{
position:fixed;
top:0;left:0;right:0;bottom:0;
background:rgba(0,0,0,.4);
display:flex;
align-items:center;
justify-content:center;
visibility:hidden;
opacity:0;
transition:opacity .3s, visibility .3s;
}
/* 关键:当modal被target,现身 */
.modal:target{
visibility:visible;
opacity:1;
}
/* 白底内容盒 */
.modal-content{
background:#fff;
padding:2rem;
border-radius:8px;
width:90%;
max-width:400px;
position:relative;
}
/* 关闭按钮:右上角小红叉 */
.close{
position:absolute;
right:1rem;top:1rem;
font-size:1.5rem;color:#999;text-decoration:none;
}
/* 点遮罩背景也能关:整个modal层再包一层a */
.modal>a.overlay{
position:absolute;
top:0;left:0;right:0;bottom:0;
cursor:default;
}
</style>
</head>
<body>
<!-- ① 触发 -->
<a class="trigger" href="#login-modal">登录</a>
<!-- ② 模态框 -->
<div id="login-modal" class="modal">
<!-- 背景遮罩链接:点它等于访问#,清空hash -->
<a href="#" class="overlay"></a>
<div class="modal-content">
<a class="close" href="#">×</a>
<h3>用户登录</h3>
<p>这里可以放真实表单</p>
</div>
</div>
</body>
</html>
使用技巧:
- 把遮罩层本身做成链接
href="#",点背景即清空hash,modal自动消失 - 如果想“只能点按钮关闭”,把遮罩层的
<a>去掉即可 - 支持ESC关闭?很遗憾,纯CSS做不到,得靠JS监听键盘;但移动端没有ESC,反而更稳
手风琴式FAQ折叠展开
需求:一排问题列表,点一个问题,答案滑出,其它问题自动收起。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>纯CSS手风琴</title>
<style>
.faq{width:640px;margin:2rem auto;}
/* 问题条 */
.faq summary{
display:block;
padding:1rem;
background:#f5f5f5;
border-bottom:1px solid #e0e0e0;
cursor:pointer;
font-weight:600;
position:relative;
}
/* 小三角 */
.faq summary::after{
content:'▶';
position:absolute;
right:1rem;
transition:transform .3s;
}
/* 答案默认收起 */
.faq .answer{
max-height:0;
overflow:hidden;
transition:max-height .4s ease;
padding:0 1rem;
}
/* 关键:当条目被target,答案展开 */
#q1:target ~ #a1,
#q2:target ~ #a2,
#q3:target ~ #a3{
max-height:200px;
padding:1rem;
}
/* 同时三角旋转 */
#q1:target ~ summary[for="q1"]::after,
#q2:target ~ summary[for="q2"]::after,
#q3:target ~ summary[for="q3"]::after{
transform:rotate(90deg);
}
</style>
</head>
<body>
<div class="faq">
<!-- 隐形靶子 -->
<a id="q1"></a><a id="q2"></a><a id="q3"></a>
<label for="q1"><summary><a href="#q1">CSS能取代JavaScript吗?</a></summary></label>
<div id="a1" class="answer">不能,但能省掉80%的“小交互”。</div>
<label for="q2"><summary><a href="#q2">target伪类兼容如何?</a></summary></label>
<div id="a2" class="answer">IE9+全绿,放心浪。</div>
<label for="q3"><summary><a href="#q3">如何关闭已展开项?</a></summary></label>
<div id="a3" class="answer">再点一次或给个“全部收起”链接到#。</div>
</div>
</body>
</html>
思路:
用max-height过渡制造“滑梯”效果,比display:block/hidden更顺滑。
反向控制“仅展开一个”需要额外技巧,后面会讲。
页面内锚点跳转时加个呼吸灯效果
需求:点目录跳到对应段落,段落闪两下淡黄背景,提示用户“看这里”。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>锚点呼吸灯</title>
<style>
/* 目录 */
.toc{position:fixed;right:2rem;top:2rem;background:#fff;box-shadow:0 2px 8px rgba(0,0,0,.1);padding:1rem;border-radius:6px;}
.toc a{display:block;margin:.5rem 0;color:#1976d2;}
/* 段落 */
article{max-width:600px;margin:4rem auto;line-height:1.8;}
h2{margin-top:3rem;}
/* 关键:被target时播放高亮动画 */
h2:target{
animation:highlight 1.2s ease 2; /* 播放两次 */
}
@keyframes highlight{
0%,100%{background:transparent;}
50%{background:#fff9c4;}
}
</style>
</head>
<body>
<nav class="toc">
<a href="#sec1">第一章</a>
<a href="#sec2">第二章</a>
<a href="#sec3">第三章</a>
</nav>
<article>
<h1>《如何不加班》</h1>
<h2 id="sec1">第一章:需求评审要勇敢说“不”</h2>
<p>……(此处省略两千字)</p>
<h2 id="sec2">第二章:能用CSS就别上JS</h2>
<p>……(继续省略)</p>
<h2 id="sec3">第三章:早下班是为了早上线</h2>
<p>……(再省略)</p>
</article>
</body>
</html>
动画细节:
animation填2表示播放两次,用户目光会被黄色背景闪两次吸引,然后自动消失,毫无负担。
target的闪光点和小脾气
优点:
- 零运行时成本,性能天花板
- 天然支持键盘与屏幕阅读器(锚点跳转本就无障碍)
- 样式与结构解耦,UI一改全改
- 不写事件监听,减少内存泄漏风险
缺点:
- hash变化会留历史记录,用户点返回键可能“误关”弹窗
- 无法传参、无法做条件判断(比如“登录后才可打开”)
- 依赖id,命名冲突会打架
- 默认锚点滚动可能打乱单页应用的路由逻辑
一句话:简单交互让它上,复杂业务让JS来。
真实项目里怎么用才不翻车?
-
单页应用先评估路由
如果项目用Vue Router/React Router,hash模式本身占用了#,再拿:target做弹窗容易互相覆盖。
解决方案:- 改用history模式,让出hash
- 或者弹窗用
input:checked方案,不抢hash
-
做降级 & 渐进增强
先写:target版本,再用JS监听click给不支持的老浏览器加class兜底。.modal.show{visibility:visible;opacity:1;}// 粗略示例 if(!CSS.supports('selector(:target)')){ /* 加class写逻辑 */ } -
语义化加分
导航高亮时,给当前链接加aria-current="page";弹窗打开时把焦点移到对话框内,关闭后焦点返回触发源。
这些都不影响:target工作,却能让读屏软件用户爱你三千遍。 -
命名空间隔离
大型项目里给id加前缀:id="ui-faq-q1"、id="login-modal-trigger",避免与后端渲染内容冲突。
踩坑实录:那些年被target整懵的瞬间
| 场景 | 症状 | 真相 | 解药 |
|---|---|---|---|
| 点击后页面滚到顶部 | 模态框一闪然后整个页面跳 | 浏览器默认行为:hash为#时滚动到0,0 | 给触发链接加event.preventDefault()? 不,我们说不写JS。直接让href="#modal-id"而不是href="#" |
| 多个target同时生效 | 打开弹窗后背景颜色也变了 | 两个元素id重名 | 命名加前缀,构建阶段用eslint-plugin-jsx-a11y/anchor-ambiguous-text扫描 |
| 手机端点系统返回键关不掉弹窗 | 用户狂点返回,history堆栈爆炸 | 每打开一次弹窗就push一个hash,返回只是清hash,不会关闭 | 打开弹窗时记录scrollTop,关闭时手动history.back();或者干脆用input:checked方案 |
| 刷新页面后弹窗还在 | 用户分享链接,别人一打开就弹登录 | hash被保存到URL | 在JS端加判断:若hash匹配弹窗id且用户未登录,则自动清hash;纯CSS无法解决 |
让target更聪明的小技巧
-
利用
:not(:target)反向控制兄弟元素
想让“仅展开一个”手风琴?给所有面板默认max-height:0,再用:not(:target)重置它们:.panel{max-height:0;} .faq:has(:target) .panel:not(:target){max-height:0!important;}当然
:has()兼容性还在路上,生产环境请先用JS加class。 -
配合
transition做丝滑动画
模态框别直接display:none/block,用visibility+opacity过渡;手风琴用max-height或grid-template-rows过渡,性能更丝滑。 -
与自定义属性联动做主题切换
html:target{ --theme-color:#ff4081; }然后整站用
var(--theme-color),实现“点按钮换主题”的魔法(当然仅限当前页)。 -
用
label + input[type="radio"]做多状态
当hash模式被路由占用时,可以用radio的:checked伪类接力,同样不写JS。思路与:target完全一致,只是把“hash”换成“radio选中值”。
写在最后(也没有结束)
:target不是银弹,却是前端工具箱里最轻便的那把瑞士军刀。
它教会我们:在合适的场景,用合适的技术,做刚刚好的事。
下次产品说“只要一个小小的弹窗”,你就可以自信地回:“不用打包依赖,不用写事件监听,给我十分钟,一行href搞定。”
然后合上电脑,去楼下买杯柠檬茶——早下班,从学会:target开始。
(代码都给你了,现在轮到你去让浏览器跳探戈。)
欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!
| 专栏系列(点击解锁) | 学习路线(点击解锁) | 知识定位 |
|---|---|---|
| 《微信小程序相关博客》 | 持续更新中~ | 结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等 |
| 《AIGC相关博客》 | 持续更新中~ | AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结 |
| 《HTML网站开发相关》 | 《前端基础入门三大核心之html相关博客》 | 前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识 |
| 《前端基础入门三大核心之JS相关博客》 | 前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。 通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心 | |
| 《前端基础入门三大核心之CSS相关博客》 | 介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页 | |
| 《canvas绘图相关博客》 | Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化 | |
| 《Vue实战相关博客》 | 持续更新中~ | 详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅 |
| 《python相关博客》 | 持续更新中~ | Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具 |
| 《sql数据库相关博客》 | 持续更新中~ | SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能 |
| 《算法系列相关博客》 | 持续更新中~ | 算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维 |
| 《IT信息技术相关博客》 | 持续更新中~ | 作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识 |
| 《信息化人员基础技能知识相关博客》 | 无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方 | |
| 《信息化技能面试宝典相关博客》 | 涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面 | |
| 《前端开发习惯与小技巧相关博客》 | 持续更新中~ | 罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等 |
| 《photoshop相关博客》 | 持续更新中~ | 基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结 |
| 日常开发&办公&生产【实用工具】分享相关博客》 | 持续更新中~ | 分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具 |
吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤
非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!

更多推荐
所有评论(0)