CSS容器查询和悬浮间隙问题

容器查询和悬浮间隙问题

1. 容器查询

在开发一个现代网页时,例如一个卡片组件。当它摆在页面正中央时,横向排版。然而,当把同一个组件复用到窄的侧边栏时,整个画面会挤压变形。

过去,为了解决这个问题,我们只能配合全局的媒体查询去套不同的父级类名:

css 复制代码
@media (min-width: 1024px) {
  /* 放到主内容区,横着排 */
  .main-content .my-card { flex-direction: row; }
  
  /* 放到侧边栏,竖着排 */
  .sidebar .my-card { flex-direction: column; }
}

这种做法不仅打破了组件的封装性(必须关注屏幕视口大小),还让代码变得极难维护。

媒体查询 和 容器查询:

  • 媒体查询: 组件根据浏览器视口有多宽,来决定自己的形态。
  • 容器查询: 组件根据父级容器有多大,来决定自己的形态。

这意味着,组件不再关心它被放在了哪里,它只关心分给自己的空间有多少。

如何开启容器查询?

以下面的代码为例

第一步:HTML 结构

首先,我们在页面里创造两个容器:一个窄(模拟侧边栏),一个宽(模拟主内容区),里面放的组件 HTML 代码完全一模一样。

html 复制代码
<div class="page-layout">
  <div class="sidebar-container">
    <div class="weather-widget">
      <div class="weather-status">☀️</div>
      <div class="weather-info">
        <h3>北京</h3>
        <p>晴间多云 · 26°C</p>
        <span class="tips">适合户外运动</span>
      </div>
    </div>
  </div>

  <div class="main-container">
    <div class="weather-widget">
      <div class="weather-status">☀️</div>
      <div class="weather-info">
        <h3>北京</h3>
        <p>晴间多云 · 26°C</p>
        <span class="tips">适合户外运动</span>
      </div>
    </div>
  </div>
</div>
第二步:在 CSS 中声明谁是容器

要想让天气组件能够识别父级大小,我们必须先让浏览器看这两个包裹层的宽度变化

css 复制代码
/* 让两个容器一个窄,一个宽 */
.sidebar-container { width: 300px; }
.main-container { width: 800px; }

/* 将其定义为一个"宽度监听容器" */
.sidebar-container, .main-container {
  container-type: inline-size;
}

注: container-type: inline-size; 意思是让浏览器只监听该容器的水平宽度变化(只有宽度变化才能触发容器查询)。因为网页布局绝大多数都在纵向滚动,宽度才是决定组件是该横排还是竖排的关键。

第三步:用 @container 编写自适应样式

现在,开始编写天气组件自己的样式。我们先写出它在窄空间下的默认状态(垂直排列),然后用 @container 写它在大空间下的横向排列。

css 复制代码
/* 1. 默认状态(窄容器/侧边栏):垂直布局,像个普通的列表项 */
.weather-widget {
  display: flex;
  flex-direction: column; /*主轴设为垂直方向*/
  align-items: center;
  padding: 15px;
  background: #f0f8ff;
  border-radius: 12px;
  text-align: center;
}
.weather-status { font-size: 40px; }
.tips { display: none; } /* 窄的时候空间不够,把温馨提示隐藏掉 */


/* 2. 当这个组件所在的"父容器"宽度大于 500px 时,横向排列 */
@container (min-width: 500px) {
  .weather-widget {
    flex-direction: row;      
    justify-content: space-around;
    padding: 30px;
    background: linear-gradient(135deg, #e0f2fe, #bae6fd); 
    text-align: left;
  }
  .weather-status {
    font-size: 80px;         
  }
  .tips {
    display: inline-block;   
    margin-top: 8px;
    color: #0369a1;
  }
}
动态改变的一个样例
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }

    html,
    body {
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      align-items: center;
      background-color: #ffc048;
      color: #1e272e;
    }

    img {
      display: block;
      border: 0 none;
      width: 100%;
      max-width: 100%;
      height: auto;
      margin: 0;
    }

    main {
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
      width: 100vw;
      max-width: 1400px;
      margin: 0 auto;
      padding: 1rem;
    }

    .main-content {
      width: 100%;
      margin: 1rem 0 0 0;
    }

    .card {
      background: #fff;
      padding: 1rem;
      border: 1px solid #1e272e;
      box-shadow: 5px 5px 0px #1e272e;

      /* 容器查询 */
      container-type: inline-size;
      container-name: card;
      resize: both;
      overflow: scroll;
    }

    .card .content {
      display: flex;
      flex-direction: column;
      justify-content: flex-start;
    }

    p {
      font-size: 1rem;
      line-height: 1.5;
      margin: 1rem 0 0 0;
    }

    .card .copy p:first-child {
      font-size: 1.25rem;
    }

    @media (min-width: 600px) {
      main {
        flex-direction: row;
      }

      .main-content {
        width: calc(100% - 400px);
        margin: 0 0 0 0.5rem;
      }
    }

    @container card (min-width: 600px) {
      .card .content {
        flex-direction: row;
      }

      .card img {
        align-self: flex-start;
        width: 50%;
        max-width: 400px;
      }

      .card .copy {
        margin: 0 0 0 1rem;
      }

      .card .copy p {
        font-size: 1.125rem;
      }

      .card .copy p:first-child {
        font-size: 1.75rem;
        margin: 0;
      }
    }
  </style>
</head>

<body>
  <main>
    <div class="main-content">
      <div class="card">
        <div class="content">
          <img src="./IMG_8945.JPG" alt="">
          <div class="copy">
            <p><strong>容器查询</strong></p>
            <p>CSS 容器查询允许开发者根据容器元素自身的尺寸来调整子元素的样式,而不再仅仅依赖视口宽度。这使得组件在不同布局上下文中都能保持优雅的响应式表现。</p>
            <p>调整卡片大小,当容器宽度超过 600px 时,图片和文字会自动切换为水平排列,字号也会相应增大。这就是容器查询带来的组件级响应能力。</p>
          </div>
        </div>
      </div>
    </div>
  </main>
</body>

</html>
  1. 使用resize: both;配合overflow: scroll; 实现调整容器大小。
  2. container-name: card;给开启容器查询的盒子命名,精准匹配,提高可读性也更易于维护,不加名字的话是自动检测上一个开启了容器查询的父盒子。@container card (min-width: 600px) 后面用容器查询的时候加上名字。

2. 悬浮间隙问题

在写百度页面时需要实现鼠标悬停在某个按钮上,然后出现下拉菜单或是说明,但是在鼠标从按钮移动到悬浮窗的过程中,鼠标会经过一个空白区域,导致悬浮窗消失。

解决方法

这个功能有多个html结构都可以实现

  1. 按钮元素作为父亲,包含下拉菜单元素。
html 复制代码
<div class="button">
  <div class="float">菜单</div>
</div>
  1. 按钮和下拉菜单为兄弟元素。
html 复制代码
<div class="button"></div>
<div class="float">菜单</div>
  1. 有一个包裹盒。
html 复制代码
<div class="container">
  <div class="button"></div>
	<div class="float">菜单</div>	
</div>
css 复制代码
		.container {
      position: relative;
    }
    
    .float {
      visibility: hidden;
      position: absolute;
      left: 55px;
      top: 55px;
    }

    .container:hover .float {
      visibility: visible;
    }

以第三种结构为例:

1. 缩小间隙

向上移动float盒子,让其紧贴着包裹盒 也就是container

css 复制代码
    .float {
      margin-top: -10px;
      padding-top: 10px;
    }
2. 伪元素补位

在菜单和按钮的空隙处定位一个菜单的伪元素,再加opacity: 0; / background color: transparent;将其隐藏。

但是在这里不能加display: none; / visibility: hidden;这两个属性无法被 hover。

属性 是否占位 能否被 hover
display: none 不存在 不能
visibility: hidden 占位 不能
opacity: 0 占位
background color: transparent; 占位
css 复制代码
		.float::before {
      position: absolute;
      top: -10px;
      height: 10px;
      width: 100%;
      opacity: 0;
    }
3. 延迟消失

给菜单的消失添加延迟,注意这里显示和隐藏的延迟要分开写,显示是0s,隐藏才加延迟。

css 复制代码
 		.float {
      visibility: hidden;
      transition: visibility 0s .2s;
    }
    
    .container:hover .float {
      transition: visibility 0s 0s;
      visibility: visible;
    }
4. clip-path

用 clip-path + pointer-events 让包裹盒的实际 hover 区域比视觉更大。

比较少用。

相关推荐
云水一下5 小时前
Vue.js从零到精通系列(一):初识Vue——背景、环境与第一个应用
前端·javascript·vue.js
云水一下5 小时前
Vue.js从零到精通系列(二):响应式核心——ref、reactive、computed与watch
前端·javascript·vue.js
放下华子我只抽RuiKe55 小时前
FastAPI 全栈后端(二):路由与数据模型
前端·人工智能·react.js·前端框架·html·fastapi
lichenyang4535 小时前
ArkTS 严格类型系统:我答错 2 道题后才真正搞懂的几条规则
前端
小小小小宇5 小时前
定高、不定高、瀑布流虚拟列表
前端
天启HTTP5 小时前
开启全局代理后网络变慢,问题出在哪
开发语言·前端·网络·tcp/ip·php
卡布鲁5 小时前
Webpack 核心原理与自定义 Loader/Plugin 实战
前端·javascript
智码看视界5 小时前
Web Storage 的无障碍实践与工程化应用
前端·javascript·web
孟陬6 小时前
国外技术周刊 #140:在 Jeff Bezos 的私密 Campfire 峰会上,我学到了关于亿万富翁的事
前端·后端