相邻兄弟选择器
概述
相邻兄弟选择器(Adjacent Sibling Combinator)用于选择紧接在某个元素之后的第一个同级兄弟元素。
语法
元素1 + 元素2 {
属性: 值;
}注意: 两个元素之间用加号 + 分隔,并且要有空格。
基本示例
1. 基本用法
/* 选择紧跟在 h2 之后的第一个段落 */
h2 + p {
color: red;
font-weight: bold;
}<h2>标题</h2>
<p>这个段落会被选中(红色粗体)</p>
<p>这个段落不会被选中</p>
<h2>另一个标题</h2>
<p>这个段落会被选中(红色粗体)</p>2. 选择器类型不限
/* 选择紧跟在 p 之后的第一个 div */
p + div {
background-color: yellow;
}
/* 选择紧跟在 li 之后的第一个 li */
li + li {
border-top: 1px solid #ccc;
}工作原理
相邻兄弟选择器的要求:
- 同级关系: 两个元素必须有相同的父元素
- 相邻位置: 元素2必须紧接在元素1之后
- 只选择一个: 只选择第一个符合条件的元素
图示
父元素
├── 元素1 (p)
├── 元素2 (p) ✓ 会被选中
├── 元素3 (div) ✗ 不会被选中(不是 p)
├── 元素4 (p) ✗ 不会被选中(不是第一个)
└── 元素5 (p) ✗ 不会被选中(不是第一个)与通用兄弟选择器的区别
相邻兄弟选择器(+)
h2 + p {
/* 只选择紧跟在 h2 之后的第一个 p */
color: red;
}效果: 只选择紧邻的第一个元素
通用兄弟选择器(~)
h2 ~ p {
/* 选择 h2 之后的所有同级 p 元素 */
color: red;
}效果: 选择后面所有的同级元素
对比示例
/* 相邻兄弟选择器 */
.title + p {
color: red; /* 只有紧邻的第一个 p 是红色 */
}
/* 通用兄弟选择器 */
.title ~ p {
color: blue; /* title 后面所有的 p 都是蓝色 */
}<div class="title">标题</div>
<p>第一个段落 - 红色(+) / 蓝色(~)</p>
<p>第二个段落 - 不变色(+) / 蓝色(~)</p>
<div>其他内容</div>
<p>第三个段落 - 不变色(+) / 蓝色(~)</p>权重计算
相邻兄弟选择器的权重是各组成部分权重之和:
h2 + p {
/* 权重 = 1(h2) + 1(p) = 2 */
color: red;
}
.title + .box {
/* 权重 = 10(.title) + 10(.box) = 20 */
color: blue;
}
#header + p {
/* 权重 = 100(#header) + 1(p) = 101 */
color: green;
}常见应用场景
1. 列表项分隔线
/* 给列表项添加上边框 */
li + li {
border-top: 1px solid #eee;
}<ul>
<li>列表项 1 (无上边框)</li>
<li>列表项 2 (有上边框)</li>
<li>列表项 3 (有上边框)</li>
<li>列表项 4 (有上边框)</li>
</ul>2. 表单控件
/* 输入框与标签的间距 */
label + input {
margin-left: 10px;
}
/* 表单组之间的间距 */
.form-group + .form-group {
margin-top: 15px;
}<form>
<div class="form-group">
<label>用户名:</label>
<input type="text">
</div>
<div class="form-group">
<label>密码:</label>
<input type="password">
</div>
<div class="form-group">
<label>邮箱:</label>
<input type="email">
</div>
</form>3. 标题和段落
/* 标题后的第一个段落特殊样式 */
h2 + p {
font-size: 18px;
color: #666;
margin-top: 10px;
}<h2>文章标题</h2>
<p>这是摘要段落,会应用特殊样式</p>
<p>这是正文段落,不会应用特殊样式</p>4. 按钮组
/* 按钮之间的间距 */
.btn + .btn {
margin-left: 10px;
}<div>
<button class="btn">取消</button>
<button class="btn">确定</button>
<button class="btn">提交</button>
</div>5. 布局分隔
/* 段落与标题的间距 */
p + h3 {
margin-top: 30px;
}
/* 图片与段落的间距 */
img + p {
margin-top: 15px;
}6. 卡片布局
/* 卡片之间的间距 */
.card + .card {
margin-top: 20px;
}7. 表格行样式
/* 表格行的分隔 */
tr + tr td {
border-top: 1px solid #ddd;
}8. 导航菜单
/* 导航项的分隔符 */
.nav-item + .nav-item::before {
content: "|";
margin: 0 10px;
color: #ccc;
}<nav>
<a href="#" class="nav-item">首页</a>
<a href="#" class="nav-item">产品</a>
<a href="#" class="nav-item">关于</a>
<a href="#" class="nav-item">联系</a>
</nav>9. 图标和文字
/* 图标后的文字间距 */
.icon + span {
margin-left: 8px;
}
/* 图标与链接的间距 */
.icon + a {
margin-left: 10px;
}10. 图片库
/* 图片之间的间距 */
.gallery img + img {
margin-left: 10px;
}高级用法
1. 与伪类配合
/* 选中第一个元素之外的所有元素 */
li:first-child ~ li {
/* ... */
}
/* 等同于 */
li + li {
/* ... */
}2. 与伪元素配合
/* 在相邻元素前添加内容 */
h2 + p::before {
content: "→ ";
color: #3498db;
}3. 多个相邻选择器组合
/* 链式选择 */
h2 + p + p {
color: red; /* 选择第二个段落 */
}
/* 等价于 */
h2 + p + p,
h2 + p + p + p,
h2 + p + p + p + p {
/* 选择第三个及之后的段落 */
}4. 选择特定状态
/* 选中特定元素之后的元素 */
.active + li {
background-color: #f9f9f9;
}<ul>
<li class="active">当前项</li>
<li>下一项(会应用背景色)</li>
<li>再下一项(不会应用背景色)</li>
</ul>5. 响应式设计
/* 小屏幕下调整间距 */
@media (max-width: 768px) {
.btn + .btn {
margin-left: 5px;
margin-top: 5px;
}
}6. 交互动画
/* 悬停效果 */
.box:hover + .next {
opacity: 0.5;
transition: opacity 0.3s ease;
}实战案例
案例1: 评论列表
/* 评论分隔线 */
.comment + .comment {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
}
/* 评论和回复之间的间距 */
.comment > .replies + .reply {
margin-top: 10px;
}<div class="comment-list">
<div class="comment">
<p>第一条评论</p>
</div>
<div class="comment">
<p>第二条评论</p>
</div>
<div class="comment">
<p>第三条评论</p>
</div>
</div>案例2: 新闻列表
/* 新闻项 */
.news-item {
padding: 20px;
border-bottom: 1px solid #eee;
}
/* 第一个新闻项没有上边框 */
.news-item + .news-item {
border-top: none;
}
/* 标题和摘要的间距 */
.news-item h3 + p {
margin-top: 10px;
color: #666;
}<div class="news-list">
<div class="news-item">
<h3>新闻标题1</h3>
<p>新闻摘要...</p>
</div>
<div class="news-item">
<h3>新闻标题2</h3>
<p>新闻摘要...</p>
</div>
</div>案例3: 步骤指示器
/* 步骤指示器 */
.step {
display: inline-block;
width: 40px;
height: 40px;
line-height: 40px;
text-align: center;
border: 2px solid #ddd;
border-radius: 50%;
}
.step + .step::before {
content: "→";
margin: 0 10px;
color: #ddd;
}<div class="steps">
<div class="step">1</div>
<div class="step">2</div>
<div class="step">3</div>
<div class="step">4</div>
</div>案例4: 价格表
/* 价格表项 */
.price-item {
display: flex;
justify-content: space-between;
padding: 15px 20px;
}
.price-item + .price-item {
border-top: 1px dashed #eee;
}案例5: 对话框
/* 消息气泡 */
.message {
max-width: 70%;
padding: 10px 15px;
border-radius: 10px;
margin-bottom: 10px;
}
.message.sent {
background: #3498db;
color: white;
margin-left: auto;
}
.message.received + .message.received {
border-bottom-left-radius: 5px;
}
.message.sent + .message.sent {
border-bottom-right-radius: 5px;
}注意事项
1. 必须是同级元素
/* ❌ 不会选中 - 不是同级 */
.parent > .first + .second {
/* ... */
}<div class="parent">
<div class="first"></div>
<div class="wrapper">
<div class="second"></div>
</div>
</div>2. 必须相邻
div + span {
/* 只选择紧邻的 span */
}<div>元素1</div>
<span>会被选中</span>
<div>元素2</div>
<span>不会被选中</span>3. 只选择一个
p + p {
/* 只选择第一个相邻的 p */
}<p>段落1</p>
<p>会被选中</p>
<p>不会被选中</p>
<p>不会被选中</p>4. 空格很重要
/* ❌ 错误: 缺少空格 */
div+p {}
/* ✅ 正确: 有空格 */
div + p {}5. 与后代选择器的区别
/* 相邻兄弟选择器 - 同级 */
h2 + p {
/* ... */
}
/* 后代选择器 - 父子 */
div h2 + p {
/* ... */
}性能考虑
相邻兄弟选择器的性能通常较好,因为:
- 只检查相邻的元素
- 不需要遍历所有后代
但在大量元素的情况下,仍需谨慎使用。
最佳实践
- 列表分隔: 使用相邻兄弟选择器添加分隔线
- 表单布局: 控制表单元素之间的间距
- 按钮组: 管理按钮之间的间距
- 导航菜单: 添加分隔符
- 布局间距: 控制元素之间的间距
- 避免过度使用: 只在确实需要相邻关系时使用
- 考虑可读性: 复杂选择器要添加注释
总结
相邻兄弟选择器是一种精确的选择器,能够选择紧接在某个元素之后的第一个同级元素。理解其工作原理和应用场景,能够帮助我们在特定情况下编写更简洁、高效的 CSS 代码。