美文网首页
:has()使用案例

:has()使用案例

作者: 涅槃快乐是金 | 来源:发表于2024-03-31 20:07 被阅读0次

:has()主要是用来检查一个元素是否包含特定的其他元素。它就像是简化了的条件样式。

但它不仅仅是在父子关系上的查找。:has()很灵活。你可以用它做出创意,可以基于不同元素关系应用样式的不同方法。

:has()是什么?兼容性怎么样?

:has()被归类为第4级CSS选择器,并已在Chrome 105及以上版本中实现(Firefox是最后一个支持的,但在2023年底的版本121中也加入了)。

它的引入是重要的,因为它允许在CSS中进行关系检查,这是一个长期以来一直被需求的特性。

全球90%的使用率,且在所有现代浏览器中都可用,没有理由不在今天就开始在你的未来项目中使用它!

基础知识

示例:



这是我们生成的HTML:

<div class="main-container">
 <header class="header">
  <img loading="lazy" srcset="..." class="image" />
  <h1 class="title">Blog Post Title</h1>
  <div class="meta-data">Written by John Doe on October 10, 2022</div>
 </header>
 <p class="description">Lorem ipsum dolor sit amet...</p>
 <a href="#" class="read-more">Read More</a>
</div>

CSS:

.main-container {
 background-color: #f1f1f1;
 display: flex;
 flex-direction: column;
 justify-content: center;
 padding: 8px;
}

.header {
 display: flex;
 flex-direction: column;
 padding: 40px 49px;
}

@media (max-width: 991px) {
 .header {
  max-width: 100%;
  padding: 0 20px;
 }
}

.image {
 aspect-ratio: 1;
 object-fit: contain;
 object-position: center;
 width: 800px;
 overflow: hidden;
 align-self: center;
 max-width: 100%;
}

.title {
 color: #000;
 text-align: center;
 align-self: center;
 margin-top: 65px;
 white-space: nowrap;
 font: 400 24px Arial, sans-serif;
}

@media (max-width: 991px) {
 .title {
  margin-top: 40px;
  white-space: initial;
 }
}

.meta-data {
 color: #666;
 text-align: center;
 align-self: center;
 margin-top: 40px;
 white-space: nowrap;
 font: 400 15px Arial, sans-serif;
}

@media (max-width: 991px) {
 .meta-data {
  white-space: initial;
 }
}

.description {
 color: #000;
 text-align: center;
 align-self: stretch;
 margin-top: 67px;
 font: 400 16px/26px Arial, sans-serif;
}

@media (max-width: 991px) {
 .description {
  max-width: 100%;
  margin-top: 40px;
 }
}

.read-more {
 color: #fff;
 text-align: center;
 white-space: nowrap;
 border-radius: 4px;
 background-color: #333;
 align-self: center;
 margin-top: 40px;
 justify-content: center;
 padding: 20px 54px;
 font: 400 15px Arial, sans-serif;
}

@media (max-width: 991px) {
 .read-more {
  white-space: initial;
  padding: 0 20px;
 }
}
父元素有一个特定的元素

最简单的用例是当你想样式父元素时,它有一个特定的子元素。假设我们想用不同的方式来设计我们的博客文章,当我们有一个副标题。在这种情况下,我们将改变背景颜色。这是我们需要添加的所有CSS代码:

.header:has(h2) {
 background-color: darkgrey;
}

/* added style just to center the subtitle */
.subtitle {
 text-align: center;
}

当我们在HTML中添加了一个带有subtitle类的h2元素:

<div class="main-container">
 <header class="header">
  <img loading="lazy" srcset="..." class="image" />
  <h1 class="title">Blog Post Title</h1>
  <h2 class="subtitle">Subtitle</h2>
  <div class="meta-data">Written by John Doe on October 10, 2022</div>
 </header>
 <p class="description">Lorem ipsum dolor sit amet...</p>
 <a href="#" class="read-more">Read More</a>
</div>

我们将得到一个不同颜色的头部:


父元素同时包含两个元素

当我们想要在父元素同时包含副标题和引用时进行样式设置时,该怎么办?非常简单:

.header:has(h2):has(blockquote) {
 background-color: hotpink;
}

只有当同时存在h2blockquote时,背景颜色才会改变。

父元素包含任一或两个元素

如果你想要在存在一个或两个元素时保持样式:

.header:has(h2, blockquote) {
 background-color: lightsalmon;
}
父元素不包含某个元素

使用:not()伪类,我们可以做相反的操作,并在父元素中不存在子元素时设置样式:

.header:not(:has(h2)) {
 background-color: lightpink;
}

任何位置选择器

无JS的条件样式

根据条件,可以使用:has()选择器在几乎任何位置选择任何东西。在下面的例子中,我们甚至可以在没有JS的情况下触发一种设置!

<body>
 <p>What is the meaning of life?</p>
 <p class="answer">42</p>
 <label>
  <input type="checkbox" class="blur-answer" />
  Hide answer
 </label>
</body>
body:has(input.blur-answer:checked) .answer {
 filter: blur(5px);
}

当我们检查输入时,answer类将被模糊处理。

选择器的意思是,当body元素中有一个具有blur-answer类的输入并且它被选中时,样式如下(可以使用任何选择器)。

防止样式
“任何位置选择器”的另一个用例可能是在导航栏中删除logo,以防logo已经出现在您的主英雄部分中。

在这种情况下,重复出现的logo是一个眼中钉,你的设计师会大发雷霆。

我们只需要编写一个CSS选择器,检查是否有一个主英雄部分(我们的.hero-with-logo类),如果是这样,隐藏导航栏的logo:

/* ... other styles */

body:has(.hero-with-logo) .nav-bar .logo {
/* This can be "display: none;", however in this case that would shift the links to the left */
 opacity: 0;
}

现在我们的logo只出现了一次:

元素选择器向前遍历

在有了:has()之前,这是不可能的事情。

让我们看一下:

<p>element 1</p>
<p>element 2</p>
<p class="select-before">element 3</p>
<p>element 4</p>
<p>element 5</p>
*:has(+ .select-before) {
 background-color: palegoldenrod;
}

我们有5个元素,第3个元素有.select-before类。在我们的CSS中,我们告诉浏览器给任何具有具有select-before类的下一个兄弟元素的元素添加背景颜色。

这样,元素2将会有一个背景颜色:


我们可以用这种方法做很多古怪的事情,不过,其中一个可能有用的是突出显示无效输入的标签:

<label for="url-input">请输入网址:</label>
<!-- 当设置为“url”类型时,您的浏览器将处理验证和用户无效状态 -->
<input type="url" id="url-input" />

<style>
label:has(+ #url-input:user-invalid) {
 border: 1px solid red;
}
</style>

通过这4行代码,我们可以得到原生HTML验证、有条件的样式,而无需JS!


布局定位

假设我们使用HTML和CSS来创建幻灯片。根据幻灯片上的内容,我们可以切换幻灯片的布局。例如:

<article>
 <h1>Hello slide with h1!</h1>
</article>

<article>
 <h2>Hello slide with h2 and img!</h2>
 <img class="img1" src="https://picsum.photos/600" />
</article>

<article>
 <h2>Hello slide with h2 and 2 imgs!</h2>
 <img class="img2" src="https://picsum.photos/200" />
 <img class="img3" src="https://picsum.photos/200" />
</article>
/* ... 通用样式  */

article:has(> is:(h1, h2):only-child) {
 grid-template-areas:
  "heading heading"
  "heading heading";
}

article:has(h2 + img) {
 grid-template-areas:
  "heading heading"
  "image1 image1";
}

article:has(h2 + img + img) {
 grid-template-areas:
  "heading heading"
  "image2 image3";
 place-content: center;
}

我们根据内容改变模板区域。

表单验证样式

例如,我们可以在用户输入无效时为表单内的标签设置样式:

<form>
 <div>
  <label for="pass">密码:
   <input id="pass" type="password" minlength="8" required/>
  </label>
 </div>
 <div>
  <label for="website">网址:
   <input id="website" type="url" required/>
  </label>
 </div>
 <button>提交</button>
</form>
label:has(input:user-invalid) {
 background-color: red;
}

请注意,两个输入都是必填项,且嵌套在标签内。这就是告诉我们选择器的标志,即标签中有一个输入是无效的。

现在,如果我们不输入符合8个字符要求的密码,或者我们不在网址输入中输入有效的URL,我们将得到一个验证信息:



再次强调,所有的用户体验交互,无需JavaScript!

我们还可以使用这种方法来为我们的输入中的一个无效时设置样式:

<form>
  <fieldset>
    <legend>添加您的信息</legend>
    <div>
  <label for="pass">密码:
   <input id="pass" type="password" minlength="8" required />
  </label>

 </div>
 <div>
  <label for="website">网址:
   <input id="website" type="url" required />
  </label>
 </div>
 <button>提交</button>
  </fieldset>
</form>
fieldset:has(input:user-invalid) {
 border: 1px solid red;
}

与标签示例类似,关键部分是字段集中包含了无效的输入,以实现这一目标。

指定元素添加样式

如果你想要为我们没有悬停的其他元素添加样式,该怎么办?

看看这个:

<ul class="card-list">
 <li class="card">Card1</li>
 <li class="card">Card2</li>
 <li class="card">Card3</li>
 <li class="card">Card4</li>
 <li class="card">Card5</li>
</ul>
.card-list:has(.card:hover) .card:not(:hover) {
 opacity: 0.4;
 transform: scale(0.9);
}

我们针对.card-list类,检查卡片是否被悬停,然后选择我们没有悬停的卡片。

这个方法非常酷,并且在没有:has()之前是无法实现的。

数量查询

如果父元素有X个或更多子元素,则可以对其进行不同的样式设置。例如:

<ul class="card-list">
 <li class="card">Card1</li>
 <li class="card">Card2</li>
 <li class="card">Card3</li>
</ul>
.card-list:has(> *:nth-child(3n)) {
 display: grid;
 grid-template-columns: 1fr 1fr;
 place-items: center;
 gap: 1rem;
 background-color: lightgrey;
 & :last-child {
  width: 50vw;
  background-color: royalblue;
  grid-column: 1 / -1
 }
}

空的子元素

您是否曾经遇到过这样的情况:您的React组件可能会渲染一个空的子元素,但是您很难注意到吗?

试试这个:

<article>
 <p>我是一篇文章。如果我的子元素为空,我的父元素将有边框。</p>
 <p></p>
</article>
article:has(> *:empty) {
 height: auto;
 border: 1px dotted crimson;
}

在调试时,这可能会很方便。

有下拉菜单

如果您有一个带有嵌套元素的下拉菜单,您可以找到它并标记它。

.menu-dropdown li:has(ul) b:after {
  content: " ⬇️";
}

现在,我们知道哪个部分可以展开:


匹配选择器中的属性

假设您有一个带有透明背景的.png图像,您可以找到它并添加背景颜色,如下所示:

<main class="display">
 <img src="image.png" />
</main>
.display:has(img[src$=".png"]) img {
 background: linear-gradient(90deg, rgba(232,231,232,1) 0%, rgba(201,204,208,1) 100%);
}

您可以使用这种方法来获取基本上您能想到的任何属性,并为父元素或该父元素的任何子元素设置样式。请随意提出您的用例。

结论

在网页开发的大背景中,引入:has()伪类就像在CSS地图上发现了一个新大陆一样,为我们以前只能梦想的动态样式提供了无限可能。

从根据其子元素的属性为父元素添加样式,到在没有一行JavaScript的情况下实现复杂的布局更改,:has()带来了一种力量和灵活性,使设计师和开发人员都感到欣喜若狂。

正如我们所探讨的,无论是根据数量样式化元素,选择前一个元素,还是甚至以一种新发现的优雅方式处理表单验证.

相关文章

网友评论

      本文标题::has()使用案例

      本文链接:https://www.haomeiwen.com/subject/eljbtjtx.html