跳到主要内容

基础概念✅

什么是CSS盒模型?

答案

CSS盒模型用于描述文档树中元素生成的矩形框,并根据视觉格式化模型进行布局。它定义了每个元素的内容区、内边距、边框和外边距,决定了页面元素的实际显示和排列方式。 盒模型的结构如下

  1. 内容盒(Content Box)
    • 包含元素的实际内容,如文本或图像。
    • 通过 widthheight 等属性设置。
  2. 内边距(Padding Box)
    • 包围内容盒的空间,用于增加内容与边框之间的距离。
    • 通过 padding 等属性设置。
  3. 边框(Border Box)
    • 包围内边距盒的线条,用于定义元素的边界。
    • 通过 border 等属性设置。
  4. 外边距(Margin Box)
    • 包围边框盒的空间,用于增加元素与其他元素之间的距离。
    • 通过 margin 等属性设置。
提示

参考 google css 盒模型讲解 初学者可以把盒模型理解为相框。

  1. 内容盒就是照片本身
  2. 内边距盒 照片+相框之间的空间
  3. 边框盒就是整个相框
  4. 外边距盒就是相框+其他物体之间的空间

这种关联方式就能更好地理解为什么边距可以合并,outline 不会影响盒模型的大小等概念。

可以通过 box-sizing 属性来控制盒模型的计算方式。

  1. content-box(默认):widthheight 只包含内容区域。

  2. border-boxwidthheight 包含内容、内边距和边框。一般推荐使用这种方式,行为更可控,在代理样式重置中,会采用如下方式重置

       *,
    *::before,
    *::after {
    box-sizing: border-box;
    }

延伸阅读

什么是视觉格式化模型(View formatting model) ?

答案

视觉格式化模型(View formatting model) 定义了用户代理(一般为浏览器)如何在可视化设备(电脑、手机、打印机等)上渲染文档树。简单理解可以认为,盒模型定义单个元素的渲染行为,视觉格式化模型则定义了整个文档树如何铺排和渲染的机制。核心概念包括

  1. 通过 display 属性控制盒模型的生成常见的盒类型包括
    1. display:block 块级盒,垂直堆叠铺排
    2. display:inline 内联盒,水平堆叠铺排
    3. display:inline-block 内容区按照块级盒处理,自身按照内联盒铺排
    4. display:flex flex 铺排
    5. display: grid 网格铺排
    6. display:table
    7. 匿名盒(anonymous block),用户代理在铺排的时候会生成匿名盒包含块级、内联两种,比如没有标签包裹的文字会生成内联匿名盒等,具体规则详见 匿名块级盒匿名行内盒
  2. 格式化模型存在三种定位策略(position schemes) 控制元素如何铺排
    1. 正常流(Normal Flow)
      1. 块级格式化上下文(Block formatting context BFC) 内容垂直堆叠,同一个块级上下文内部的垂直 margin 会出现合并
      2. 内联格式化上下文(Inline formatting context IFC) 内容水平堆叠, 注意铺排方式还和 writing-mode 属性相关
      3. 相对定位(Relative positioning) 内容会在正常流布局的基础上发生偏移,但是原始盒模型不会改动,所以可能出现显示重叠现象
    2. 浮动布局(Float) 元素会相对正常流对应的包含块边缘对齐
    3. 绝对定位(Absolute position) 元素会脱离正常流,相对关联的包含块对齐
提示

此外视觉格式化模型会受书写方式的影响,来决定内联盒和块级盒的铺排方式,具体可以参考 Writing Modes

延伸阅读

什么是 BFC 有哪些作用?

答案

块级格式化上下文(Block formatting context BFC) 是属于格式化模型(View formatting model) 中,正常流布局(Normal flow)的一种。

BFC 的核心特性

  1. 包含浮动元素:BFC 会包含其内部的所有浮动元素,解决高度塌陷问题
  2. 阻止外边距合并:BFC 内的外边距不会与外部元素的外边距合并
  3. 阻止文字环绕:BFC 会阻止浮动元素与 BFC 内的文字重叠
  4. 独立布局:BFC 内的元素布局不受外部影响

对于块级格式上下文内布局规则如下

  • 盒子从块级格式化上下文的顶层包含块从上到下垂直堆叠
  • 对于相邻的盒子,垂直 margin 会出现折叠
  • 块级格式化上下文的盒子会相对包含块左对齐(注意对于 direction: rtl 则右对齐)

如下方式会产生块级格式化上下文:

  1. 文档的根元素(<html>
  2. display: flow-root 的元素(推荐使用)
  3. 浮动元素(float 属性不为 none 的元素)
  4. 绝对定位元素(position 属性为 absolutefixed 的元素)
  5. 行内块元素(display: inline-block 的元素)
  6. 表格单元格(display: table-cell 的元素,HTML 表格单元格默认如此)
  7. 表格标题(display: table-caption 的元素,HTML 表格标题默认如此)
  8. display: tabletable-rowtable-row-grouptable-header-grouptable-footer-groupinline-table 的元素隐式创建的匿名表格单元格
  9. overflow 属性值不是 visibleclip 的块级元素
  10. <button> 元素和默认 display: flow-root 的按钮类型 <input> 元素
  11. contain 属性为 layoutcontentpaint 的元素
  12. 弹性盒子项目(display: flexinline-flex 的直接子元素)
  13. 网格项目(display: gridinline-grid 的直接子元素)
  14. 多列容器(column-countcolumn-width 不为 auto 的元素)
  15. column-span: all 的元素

示例说明

<style>
  .box {
    background-color: #f0f0f0;
    padding: 10px;
  }

  .float-box {
    float: left;
    border: 1px solid #000;
  }

  input[type="checkbox"]:checked ~ .box {
    display: flow-root;
  }
</style>
<div class="container">
  清除浮动
  <input type="checkbox" />
  <div class="box">
    <div class="float-box">浮动元素</div>
  </div>
</div>

延伸阅读

什么是 IFC 和 BFC 有哪些区别?

答案

内联格式化上下文(Inline Formatting Context IFC) 是视觉格式化模型中的一种布局模式,用于处理内联元素的排列。与 BFC 不同,IFC 主要处理水平方向的布局。

IFC 的核心特性:

  1. 水平排列:IFC 内的元素按照水平方向从左到右排列(在 LTR 语言环境下)
  2. 基线对齐:内联元素默认按照基线对齐,可以通过 vertical-align 属性调整
  3. 行框(Line Box):IFC 会创建行框来包含每一行的内联元素
  4. 自动换行:当内容超出容器宽度时,会自动换行创建新的行框

IFC 的布局规则:

  • 内联元素在 IFC 中水平排列,从包含块的顶部开始
  • 行框的高度由该行内最高元素的高度决定
  • 内联元素的垂直对齐通过 vertical-align 属性控制
  • 当内联元素包含块级元素时,会创建匿名块级盒

IFC 与 BFC 的主要是 IFC 主要用于文本内容的排版,如段落、标题等。 BFC 用于块级布局。

示例说明

<style>
  .container {
    border: 1px solid #ccc;
    padding: 10px;
    margin: 10px 0;
    width: 300px;
  }
  
  .normal-text {
    font-size: 16px;
    background: lightblue;
  }
  
  .tall-text {
    font-size: 32px;
    background: lightgreen;
    line-height: 1.2;
  }
  
  .image {
    width: 50px;
    height: 50px;
    background: lightcoral;
    display: inline-block;
    vertical-align: middle;
  }
  
  .highlight {
    background: yellow;
    padding: 2px;
  }
</style>

行框高度由最高元素决定:
<div class="container">
  <span class="normal-text">普通文本</span>
  <span class="tall-text">高文本</span>
  <span class="normal-text">普通文本</span>
</div>

包含图片的行框:
<div class="container">
  <span class="normal-text">文本内容</span>
  <div class="image"></div>
  <span class="normal-text">更多文本</span>
</div>

多行文本的行框:
<div class="container">
  <span class="normal-text">第一行文本</span>
  <span class="tall-text">第二行高文本</span>
  <span class="normal-text">第三行文本</span>
  <span class="highlight">高亮文本</span>
</div>

延伸阅读

什么是 Cascade?

答案

层叠(cascade) 用来处理一个元素的 css 属性上匹配多条规则时,如何计算最终的属性值。这最终通过层叠计算出来的属性值叫做层叠值(cascaded value)

核心规则如下

  1. 默认顺序,从低到高
    1. 用户代理设置的默认样式
    2. 用户设置的默认样式,通过插件或者配置实现
    3. @layer 规则
    4. 开发者设置的样式
      1. 内联样式 > ID 选择器 > 类属性选择器 > 标签选择器 > 通配符
      2. 选择器优先级相同,则后面的样式覆盖前面的
  2. 如果设置了 !important 会导致优先级权重提升,但是和默认顺序相反即从低到高依次为
    1. !important 开发者设置样式
    2. !important @layer 规则
    3. !important 用户设置样式
    4. !important 代理设置样式

延伸阅读

什么是层叠上下文(Stacking Context)?

答案

层叠上下文(Stacking Contexts) 决定了文档树中的元素在可视化设备上的叠放规则,可以类比为 PhotoShop 中图层堆叠的概念。默认层叠上下文的核心包括

  1. 正常流布局中,同级元素层叠优先级,靠后的元素高于靠前的元素,不同级的子元素层叠规则由最终同级的父元素决定。这也是为什么单纯设置某个子元素的 z-index 不生效的原因,因为同级的父元素可能层叠靠前导致无法覆盖后面的元素
  2. 对于 position 不等于 static 值的定位元素, z-index 的值越大层叠越靠上。注意对于 flex、grid 这些容器的子元素不依赖 position 限制,可以直接设置 z-index 控制层叠
  3. 如下方式会创建层叠上下文
    1. 文档根元素 <html>
    2. position 值为 absolute(绝对定位)或 relative(相对定位)且 z-index 值不为 auto 的元素;
    3. position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素(沾滞定位适配所有移动设备上的浏览器,但老的桌面浏览器不支持);
    4. flex (flex) 容器的子元素,且 z-index 值不为 auto;
    5. grid (grid) 容器的子元素,且 z-index 值不为 auto;
    6. opacity 属性值小于 1 的元素(参见 the specification for opacity);
    7. mix-blend-mode 属性值不为 normal 的元素;
    8. 以下任意属性值不为 none 的元素:
      • transform
      • filter
      • backdrop-filter
      • perspective
      • clip-path
      • mask / mask-image / mask-border
      • isolation 属性值为 isolate 的元素;
    9. will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素(参考这篇文章);
    10. contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。

延伸阅读

什么是逻辑属性和逻辑值?

答案

CSS 利用 逻辑属性 解决由于书写方向和模式改变,导致的非预期的渲染错误。

浏览器在实际布局(Layout)和绘制(Painting) 阶段依赖坐标系确认布局位置和绘制顺序,参考 Coordinate Spaces chromium 对坐标体系的说明

  • 物理坐标(Physical coordinates):对应于输出设备的物理方向(如屏幕、打印页面)。通常用于绘制,因此布局逻辑在传递给绘制阶段时可能会产生这种空间的值。CSS 属性如 toprightbottomleft 就属于物理坐标空间。
  • 逻辑坐标(Logical coordinates):用于布局,支持根据 writing-modedirection CSS 属性的不同值进行通用定位。以 beforeafterstartend 命名的属性属于逻辑坐标空间。
  • 不考虑内联方向翻转的逻辑坐标:即“逻辑块坐标(logical block coordinates)”,不考虑文本方向。比如 “LogicalLeft” 和 “LogicalRight” 就是这类坐标的例子。

常规属性均采用物理坐标系,这会导致在在通过 writing-mode 等改变书写模式后影响常规元素的铺排示例如下

<style>
  .container {
    display: flex;
    gap: 10px;
  }
  .parent {
    width: 200px;
    height: 200px;
    display: flex;
    background: cadetblue;
    justify-content: center;
    align-items: center;
    position: relative;
  }
  .child {
    height: 30px;
    position: absolute;
    top: 30px;
    left: 30px;
    background: coral;
  }

  .block-size {
    /* 采用 block-size 设置逻辑高度,不受物理坐标系影响 */
    block-size: 30px;
    /* 注意必须重置 height, 不然最终还是覆盖 block-size */
    height: unset;
  }
  
  #toggle:checked ~ .container .parent {
    writing-mode: vertical-rl;
  }
</style>
开启 write-mode: vertical-rtl
<input type="checkbox" id="toggle" />
<div class="container">
  <div class="parent">
    parent
    <div class="child">children</div>
  </div>
  <div class="parent">
    parent
    <div class="child block-size">children</div>
  </div>
</div>

延伸阅读

什么是书写模式(writing-mode),有什么作用?

答案

默认浏览器采用由上到下、从左到右的书写模式(writing-mode),但是对于一些语言如阿拉伯语、日语等,书写模式会有所不同。所以浏览器定义了 Writing Modes Level 规范来处理不同书写模式的问题。核心的属性包括

  • writing-mode:定义书写模式,决定了块级元素的排列方式,常用值,支持 horizontal-tb(水平从上到下)、vertical-rl(垂直从右到左)
  • direction:定义文本方向,决定了内联块的排列顺序,常用值,支持 ltr(从左到右)、rtl(从右到左)

例如如下配置

  • writing-mode: vertical-rl 决定了块的铺排是垂直方向从右到左
  • direction: ltr 决定了内联盒的排列是从左到右
writing-mode: vertical-rl; direction: ltr:

'top' / 'start' side

block-flow direction
<------------------------------------ |
------------------------------------- |
| c | s | |
'left' | o | o | | inline 'right'
/ | n | m | | direction /
'after' | t | e | | 'before'
side | e | | | side
| n | | |
| t | | |
------------------------------------- v

'bottom' / 'end' side

什么是响应式设计?如何实现?

答案

核心概念

响应式设计是一种网页设计方法,使网站能够自动适应不同设备和屏幕尺寸,提供最佳的用户体验。其核心思想是"一套代码,多端适配"。

实现方法

  1. 媒体查询(Media Queries)

    • 根据设备特性应用不同的样式
    • 常用的断点:手机(<768px)、平板(768px-1024px)、桌面(>1024px)
  2. 弹性布局(Flexbox/Grid)

    • 使用相对单位而非固定像素
    • 容器能够根据内容自动调整
  3. 相对单位

    • 使用 rem、em、%、vw/vh 等相对单位
    • 避免使用固定像素值
  4. 图片适配

    • 使用 max-width: 100% 确保图片不溢出容器
    • 使用 srcset 和 sizes 属性提供不同尺寸的图片

示例说明

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>响应式设计演示</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: Arial, sans-serif;
            line-height: 1.6;
            color: #333;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 0 20px;
        }
        
        /* 响应式导航栏 */
        .navbar {
            background: #007bff;
            color: white;
            padding: 1rem 0;
        }
        
        .nav-content {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .logo {
            font-size: 1.5rem;
            font-weight: bold;
        }
        
        .nav-menu {
            display: flex;
            list-style: none;
            gap: 2rem;
        }
        
        .nav-menu a {
            color: white;
            text-decoration: none;
            transition: opacity 0.3s;
        }
        
        .nav-menu a:hover {
            opacity: 0.8;
        }
        
        .hamburger {
            display: none;
            flex-direction: column;
            cursor: pointer;
        }
        
        .hamburger span {
            width: 25px;
            height: 3px;
            background: white;
            margin: 3px 0;
            transition: 0.3s;
        }
        
        /* 响应式网格布局 */
        .grid-demo {
            margin: 2rem 0;
        }
        
        .grid-container {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 2rem;
            margin: 2rem 0;
        }
        
        .grid-item {
            background: #f8f9fa;
            padding: 1.5rem;
            border-radius: 8px;
            border: 1px solid #dee2e6;
        }
        
        .grid-item h3 {
            color: #007bff;
            margin-bottom: 1rem;
        }
        
        /* 响应式图片 */
        .image-demo {
            margin: 2rem 0;
        }
        
        .responsive-image {
            width: 100%;
            height: auto;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        
        /* 响应式表格 */
        .table-demo {
            margin: 2rem 0;
            overflow-x: auto;
        }
        
        .responsive-table {
            width: 100%;
            border-collapse: collapse;
            margin: 1rem 0;
        }
        
        .responsive-table th,
        .responsive-table td {
            border: 1px solid #dee2e6;
            padding: 0.75rem;
            text-align: left;
        }
        
        .responsive-table th {
            background: #f8f9fa;
            font-weight: bold;
        }
        
        /* 响应式文本 */
        .text-demo {
            margin: 2rem 0;
        }
        
        .responsive-text {
            font-size: clamp(1rem, 2.5vw, 1.5rem);
            line-height: 1.6;
        }
        
        /* 响应式按钮 */
        .button-demo {
            margin: 2rem 0;
        }
        
        .responsive-button {
            display: inline-block;
            padding: clamp(0.5rem, 2vw, 1rem) clamp(1rem, 4vw, 2rem);
            background: #007bff;
            color: white;
            text-decoration: none;
            border-radius: 4px;
            transition: background 0.3s;
            font-size: clamp(0.875rem, 2vw, 1rem);
        }
        
        .responsive-button:hover {
            background: #0056b3;
        }
        
        /* 媒体查询断点 */
        @media (max-width: 768px) {
            .nav-menu {
                display: none;
            }
            
            .hamburger {
                display: flex;
            }
            
            .grid-container {
                grid-template-columns: 1fr;
                gap: 1rem;
            }
            
            .container {
                padding: 0 15px;
            }
        }
        
        @media (max-width: 480px) {
            .container {
                padding: 0 10px;
            }
            
            .grid-item {
                padding: 1rem;
            }
        }
        
        /* 设备信息显示 */
        .device-info {
            background: #e9ecef;
            padding: 1rem;
            border-radius: 4px;
            margin: 1rem 0;
            font-family: monospace;
        }
        
        .demo-section {
            margin: 3rem 0;
            padding: 2rem;
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        
        .demo-section h2 {
            color: #495057;
            margin-bottom: 1rem;
            border-bottom: 2px solid #007bff;
            padding-bottom: 0.5rem;
        }
        
        .code-example {
            background: #f8f9fa;
            border: 1px solid #e9ecef;
            border-radius: 4px;
            padding: 1rem;
            margin: 1rem 0;
            font-family: 'Courier New', monospace;
            font-size: 14px;
            overflow-x: auto;
        }
    </style>
</head>
<body>
    <!-- 响应式导航栏 -->
    <nav class="navbar">
        <div class="container">
            <div class="nav-content">
                <div class="logo">响应式设计演示</div>
                <ul class="nav-menu">
                    <li><a href="#grid">网格布局</a></li>
                    <li><a href="#images">响应式图片</a></li>
                    <li><a href="#table">响应式表格</a></li>
                    <li><a href="#text">响应式文本</a></li>
                </ul>
                <div class="hamburger">
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
            </div>
        </div>
    </nav>
    
    <div class="container">
        <!-- 设备信息 -->
        <div class="device-info">
            <strong>当前视口宽度:</strong><span id="viewport-width"></span>px<br>
            <strong>设备类型:</strong><span id="device-type"></span>
        </div>
        
        <!-- 网格布局演示 -->
        <section class="demo-section" id="grid">
            <h2>响应式网格布局</h2>
            <p>使用 CSS Grid 的 auto-fit 和 minmax 实现响应式网格:</p>
            
            <div class="grid-container">
                <div class="grid-item">
                    <h3>网格项目 1</h3>
                    <p>这是一个响应式网格项目,会根据屏幕宽度自动调整列数。</p>
                </div>
                <div class="grid-item">
                    <h3>网格项目 2</h3>
                    <p>在桌面端显示多列,在移动端自动变为单列布局。</p>
                </div>
                <div class="grid-item">
                    <h3>网格项目 3</h3>
                    <p>使用 CSS Grid 的 auto-fit 功能实现自适应列数。</p>
                </div>
            </div>
            
            <div class="code-example">
.grid-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 2rem;
}
            </div>
        </section>
        
        <!-- 响应式图片演示 -->
        <section class="demo-section" id="images">
            <h2>响应式图片</h2>
            <p>图片会根据容器宽度自动缩放,保持宽高比:</p>
            
            <img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAwMCIgaGVpZ2h0PSI2MDAiIHZpZXdCb3g9IjAgMCAxMDAwIDYwMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjEwMDAiIGhlaWdodD0iNjAwIiBmaWxsPSIjMDA3YmZmIi8+Cjx0ZXh0IHg9IjUwMCIgeT0iMzAwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iNDgiIGZpbGw9IndoaXRlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBkeT0iLjNlbSI+5Y2g5L2N5p2h5Lu2PC90ZXh0Pgo8L3N2Zz4K" 
                 alt="响应式图片演示" 
                 class="responsive-image">
            
            <div class="code-example">
.responsive-image {
  width: 100%;
  height: auto;
  max-width: 100%;
}
            </div>
        </section>
        
        <!-- 响应式表格演示 -->
        <section class="demo-section" id="table">
            <h2>响应式表格</h2>
            <p>表格在小屏幕上可以水平滚动:</p>
            
            <div class="table-demo">
                <table class="responsive-table">
                    <thead>
                        <tr>
                            <th>设备类型</th>
                            <th>屏幕宽度</th>
                            <th>布局方式</th>
                            <th>特点</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>手机</td>
                            <td>&lt; 768px</td>
                            <td>单列布局</td>
                            <td>垂直堆叠,触摸友好</td>
                        </tr>
                        <tr>
                            <td>平板</td>
                            <td>768px - 1024px</td>
                            <td>双列布局</td>
                            <td>中等屏幕优化</td>
                        </tr>
                        <tr>
                            <td>桌面</td>
                            <td>&gt; 1024px</td>
                            <td>多列布局</td>
                            <td>充分利用屏幕空间</td>
                        </tr>
                    </tbody>
                </table>
            </div>
            
            <div class="code-example">
.table-demo {
  overflow-x: auto;
}

.responsive-table {
  width: 100%;
  min-width: 600px;
}
            </div>
        </section>
        
        <!-- 响应式文本演示 -->
        <section class="demo-section" id="text">
            <h2>响应式文本</h2>
            <p>使用 clamp() 函数实现响应式字体大小:</p>
            
            <p class="responsive-text">
                这是一段响应式文本,字体大小会根据视口宽度自动调整。
                在桌面端显示较大字体,在移动端显示较小字体,确保良好的可读性。
            </p>
            
            <div class="code-example">
.responsive-text {
  font-size: clamp(1rem, 2.5vw, 1.5rem);
  line-height: 1.6;
}
            </div>
        </section>
        
        <!-- 响应式按钮演示 -->
        <section class="demo-section">
            <h2>响应式按钮</h2>
            <p>按钮的内边距会根据屏幕大小自动调整:</p>
            
            <div class="button-demo">
                <a href="#" class="responsive-button">响应式按钮</a>
                <a href="#" class="responsive-button">另一个按钮</a>
            </div>
            
            <div class="code-example">
.responsive-button {
  padding: clamp(0.5rem, 2vw, 1rem) clamp(1rem, 4vw, 2rem);
  font-size: clamp(0.875rem, 2vw, 1rem);
}
            </div>
        </section>
        
        <!-- 媒体查询演示 -->
        <section class="demo-section">
            <h2>媒体查询断点</h2>
            <p>常用的响应式断点设置:</p>
            
            <div class="code-example">
/* 手机端 */
@media (max-width: 768px) {
  .nav-menu { display: none; }
  .grid-container { grid-template-columns: 1fr; }
}

/* 小屏手机 */
@media (max-width: 480px) {
  .container { padding: 0 10px; }
}

/* 平板端 */
@media (min-width: 769px) and (max-width: 1024px) {
  .grid-container { grid-template-columns: repeat(2, 1fr); }
}

/* 桌面端 */
@media (min-width: 1025px) {
  .grid-container { grid-template-columns: repeat(3, 1fr); }
}
            </div>
        </section>
    </div>
    
    <script>
        // 更新设备信息
        function updateDeviceInfo() {
            const width = window.innerWidth;
            const height = window.innerHeight;
            
            document.getElementById('viewport-width').textContent = width;
            
            let deviceType = '桌面端';
            if (width <= 480) {
                deviceType = '小屏手机';
            } else if (width <= 768) {
                deviceType = '手机';
            } else if (width <= 1024) {
                deviceType = '平板';
            }
            
            document.getElementById('device-type').textContent = deviceType;
        }
        
        // 初始化
        updateDeviceInfo();
        
        // 监听窗口大小变化
        window.addEventListener('resize', updateDeviceInfo);
        
        // 汉堡菜单交互
        document.querySelector('.hamburger').addEventListener('click', function() {
            const navMenu = document.querySelector('.nav-menu');
            navMenu.style.display = navMenu.style.display === 'flex' ? 'none' : 'flex';
        });
        
        // 平滑滚动
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                const target = document.querySelector(this.getAttribute('href'));
                if (target) {
                    target.scrollIntoView({
                        behavior: 'smooth'
                    });
                }
            });
        });
    </script>
</body>
</html>

Open browser consoleTests

延伸阅读

什么是视口(Viewport)?如何设置?

答案

核心概念

视口是浏览器中用于显示网页的区域。在移动设备上,视口的概念尤为重要,因为移动设备的屏幕尺寸与桌面不同,需要特殊的处理来确保网页正确显示。

视口类型

  1. 布局视口(Layout Viewport)

    • 浏览器默认的视口大小
    • 通常是 980px 宽度(桌面浏览器标准)
  2. 视觉视口(Visual Viewport)

    • 用户实际看到的视口区域
    • 会随着用户缩放而改变
  3. 理想视口(Ideal Viewport)

    • 设备屏幕的物理像素宽度
    • 通过 meta viewport 标签设置

设置方法

<!-- 基本设置 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<!-- 详细设置 -->
<meta name="viewport" content="
width=device-width,
initial-scale=1.0,
minimum-scale=1.0,
maximum-scale=5.0,
user-scalable=yes
">

参数说明

参数说明示例
width视口宽度device-width(设备宽度)
initial-scale初始缩放比例1.0(不缩放)
minimum-scale最小缩放比例0.5
maximum-scale最大缩放比例5.0
user-scalable是否允许用户缩放yes/no

常见问题

  1. 移动端字体过小

    /* 设置最小字体大小 */
    body {
    font-size: 16px;
    }
  2. 触摸目标过小

    /* 确保触摸目标足够大 */
    .button {
    min-height: 44px;
    min-width: 44px;
    }
  3. 横屏适配

    @media (orientation: landscape) {
    .container {
    flex-direction: row;
    }
    }

延伸阅读