:has() 的灵机一动

767 阅读3分钟

CSS 函数式伪类  :has()  表示一个元素,如果作为参数传递的任何相对选择器在锚定到该元素时,至少匹配一个元素。这个伪类通过把可容错相对选择器列表作为参数,提供了一种针对引用元素选择父元素或者先前的兄弟元素的方法。

现在,随着CSS的:has()选择器得到广泛支持,我们终于可以大展身手。在这篇文章中,我将分享一些有趣的CSS:has()选择器用法

相邻兄弟元素的选择

之前我们只能通过 +(选择元素的下一个同级元素), ~(选择一个元素的所有下一个同级元素) 选择器进行下一个同级元素的选择,那么如何进行向前的元素选择呢?没错,就是它:has()

<div class="shelf">
  <div class="book"></div>
  <div class="book"></div>
  <div class="book"></div>
  <p class="frame"></p>
  <div class="book"></div>
  <div class="book"></div>
  <div class="book"></div>
</div>
// 前一个兄弟元素
.book:has(+ .frame) {
  opacity: 1;
}
// 前面所有兄弟元素
.book:has(~ .frame) {
  opacity: 1;
}

为了更清晰地解释,我们可以这样理解:.book + .frame选择器用于匹配.book元素的下一个同级元素.frame,而.book:has(+ .frame)选择器则表示匹配.book元素拥有一个为.frame的下一个兄弟元素,也就是匹配.frame元素的前一个兄弟元素.book

单选

我们在常见的问卷调查中常用单选和多选功能,我们可以使用:has()来实现更多复杂的交互。

<div class="question">
  <div class="question-header">当前文章的主题是什么?</div>
  <div class="list">
    <div class="answer">
      <input type="radio" name="option" id="option-0" data-correct="false" />
      <label for="option-0">响应式设计</label>
    </div>
    <div class="answer">
     <input type="radio" name="option" id="option-1" data-correct="true" />
     <label for="option-1">CSS :has 选择器</label>
    </div>
    <!-- 其他选项 -->
  </div>
</div>

我们用 data-correct属性表示是否正确,如果答案错误,那么我们添加红色,正确答案会呈绿色闪烁。

// 错误答案被选择时
.question:has(input[data-correct='false']:checked) {
  // 错误时标题变为红色 
  .question-header {
    box-shadow: inset 0 7px 0 0 var(--wrong);
    background-color: var(--wrong-bg);
  }
  // 正确答案闪烁
  input[data-correct="true"] + label {
    animation: flash 1s infinite alternate;
  }
}

如果答案正确,那么我们添加为绿色

.question:has(input[data-correct='true']:checked) {
  .question-header {
    box-shadow: inset 0 7px 0 0 var(--correct);
    background-color: var(--correct-bg);
  }
}

代码演示

多选

多选功能的代码结构相似,这里就不再重复展示了。您可以参考下面的演示代码查看相关内容,设计的重点是:只有同时选择了所有正确答案才会显示绿色,否则标记为红色。其次,在选择了正确答案后,将错误答案标识为不可选。我们将根据这两点进行设计和分析。

由于答案可能有多个,因此我们通过属性 data-correct="true1"data-correct="true2" 来标识它们。如何表示“并且”关系?其实很简单,可以参考以下代码示例。

.question:has(input[data-correct='true1']:checked).question:has(
    input[data-correct='true2']:checked
  ).question:not(:has(input[data-correct='false']:checked)) {
  .question-header {
    box-shadow: inset 0 7px 0 0 var(--correct);
    background-color: var(--correct-bg);
  }
  
  // 禁用错误答案选项
  .answer:has(input[data-correct='false']) {
    opacity: 0.5;
    cursor: not-allowed;
    label {
      pointer-events: none; // 禁用
    }
  }
}

代码演示

以上就是本文所展示的内容,旨在抛砖引玉。如有错漏还请大家指正。

参考

  1. CSS :has() Interactive Guide