纯CSS实现点击切换与选项卡高亮的两个方案(附踩坑)

有段时间在折腾能不能尽量少写 JS,把一些常见的交互交给 CSS 去处理。

有两个效果还算典型:一个是点导航切换内容区域,另一个是选项卡选中高亮。两个都没用一行脚本,实现思路不复杂,但第一个方案的实际表现其实跟直觉有点出入,这里一并记下来。

点击切换内容——:target 的方案

这种效果经常会想到用 JS 给元素加类名,纯 CSS 的路子主要靠 :target 伪类。

原理简单:当 URL 的 hash 指向某个元素的 id 时,这个元素就会匹配上 :target,我们就可以拿它做显示/隐藏。

HTML 结构大致是这样:

<nav>
    <a href="#section1">Section 1</a>
    <a href="#section2">Section 2</a>
    <a href="#section3">Section 3</a>
</nav>
<div id="section1" class="content">
    <h2>Section 1 Content</h2>
    <p>This is the content for section 1.</p>
</div>
<div id="section2" class="content hidden">
    <h2>Section 2 Content</h2>
    <p>This is the content for section 2.</p>
</div>
<div id="section3" class="content hidden">
    <h2>Section 3 Content</h2>
    <p>This is the content for section 3.</p>
</div>

CSS 这边把需要默认隐藏的板块加上 .hidden,里面就一行 display: none。然后用 :target 把它们拉回来:

.content {
    border: 1px solid #ccc;
    padding: 20px;
    margin-bottom: 10px;
}
.hidden {
    display: none;
}
#section1:target,
#section2:target,
#section3:target {
    display: block;
}

导航链接的 href 和对应板块的 id 对上,点一下 hash 变了,对应板块就显示。

这里其实容易踩坑。

上面这种写法,如果点了 Section 2,Section 2 确实会显示,但 Section 1 因为没有隐藏机制,仍然摆在那里,结果就是两个板块同时出现。严格来说这不算“切换”,只是“点哪个多显示哪个”。demo 阶段可能无所谓,但真要上生产这个行为肯定不行。:target 本身只负责匹配当前 hash 的那个元素,不会主动帮你把兄弟元素藏起来。想让之前的消失,要么把所有板块默认都隐藏,让页面初次加载时空白,要么配合兄弟选择器做互斥,但那就不是这个简单例子能覆盖的了。这个限制心里有数就好,这里主要还是演示 :target 能做什么。

选项卡选中高亮——:checked 和兄弟选择器

选项卡的场景更接近实际组件需求:选中的标签要高亮,同时只展示对应的内容面板。纯 CSS 走的是 radio 类型的 input + :checked + 后续兄弟选择器。

HTML 结构放一组 radio,共享同一个 name,这样互斥是天然的。label 负责视觉呈现,内容面板跟在它们后面:

<div class="tabs">
    <input type="radio" id="tab1" name="tab-group" checked>
    <label for="tab1">Tab 1</label>
    <input type="radio" id="tab2" name="tab-group">
    <label for="tab2">Tab 2</label>
    <input type="radio" id="tab3" name="tab-group">
    <label for="tab3">Tab 3</label>
    
    <div class="tab-content" id="content1">
        <p>Content for Tab 1</p>
    </div>
    <div class="tab-content hidden" id="content2">
        <p>Content for Tab 2</p>
    </div>
    <div class="tab-content hidden" id="content3">
        <p>Content for Tab 3</p>
    </div>
</div>

所有 input 隐藏,用 label 模拟选项卡外观。

内容默认全是 display: none,哪个 radio 被选中,就通过 ~ 把对应的 content 显示出来。高亮也是同理,选中状态下让对应的 label 变色:

来此加密采用主流的ACME协议,兼容性极佳。对接Let’s Encrypt、Google、ZeroSSL三大渠道,确保证书在不同浏览器、操作系统和设备上均获得高度信任,为你的网站保驾护航。

.tabs input[type="radio"] {
    display: none;
}
.tabs label {
    padding: 10px 20px;
    border: 1px solid #ccc;
    border-bottom: none;
    background-color: #f9f9f9;
    cursor: pointer;
}
.tabs label:hover {
    background-color: #eee;
}
.tabs .tab-content {
    display: none;
    padding: 20px;
    border: 1px solid #ccc;
    border-top: none;
}
/* 内容显示 */
#tab1:checked ~ #content1,
#tab2:checked ~ #content2,
#tab3:checked ~ #content3 {
    display: block;
}
/* 高亮 */
#tab1:checked ~ label[for="tab1"],
#tab2:checked ~ label[for="tab2"],
#tab3:checked ~ label[for="tab3"] {
    background-color: #4CAF50;
    color: white;
}

这个方案跑起来确实做到了真正的切换,因为 radio 组只有一个能处于 :checked,切换时原来的匹配失效,内容自动回退到 display: none,高亮也跟着走。结构上要求内容面板和 label 都在同一个父级里,且内容必须排在 radio 和 label 之后,否则 ~ 选不到。

这算是一个布局上的约束,一般用 flex 或 grid 调一下顺序还能接受。

这两个方案都偏实验性质,真实项目里要是交互复杂了还是得请 JS 出山,不过偶尔碰到简单的演示或者对脚本有限制的环境,知道 CSS 能顶一顶还是挺省事的。

posted @ 2026-07-29 19:56  枫唐  阅读(15)  评论(0)    收藏  举报