美文网首页
: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