Vanson's Eternal Blog

CSS盒模型

Css basic.png
Published on
/11 mins read/---

CSS盒模型

标准盒模型:W3C的默认选择

标准盒模型(content-box)是CSS规范定义的默认模型,它由内到外分为四层:

  1. 内容区域(Content) - 显示文本、图片等实际内容
  2. 内边距(Padding) - 内容与边框之间的缓冲带
  3. 边框(Border) - 元素的边界线
  4. 外边距(Margin) - 元素与其他元素的安全距离

关键特点

  • width/height只决定内容区域大小
  • 元素的总宽度 = width + padding-left + padding-right + border-left + border-right
  • 元素的总高度 = height + padding-top + padding-bottom + border-top + border-bottom
.standard-box {
  width300px;        /* 内容区域宽度 */
  height200px;       /* 内容区域高度 */
  padding20px;       /* 所有方向的内边距 */
  border10px solid #333/* 边框 */
  margin30px;        /* 所有方向的外边距 */
  background-color#f0f0f0;
  box-sizing: content-box/* 明确指定使用标准盒模型(默认值) */
}
 
  • 内容区域: 300px × 200px
  • 总宽度: 300px + 20px × 2 + 10px × 2 = 360px
  • 总高度: 200px + 20px × 2 + 10px × 2 = 260px
  • 外边距不计入元素实际尺寸,但会影响元素在页面中占据的空间二、怪异盒模型:更直观的布局方案

怪异盒模型:更直观的布局方案

与标准盒模型的区别

在怪异盒模型中:

  • width和height属性设置的是元素的总宽度和总高度,包括内容、内边距和边框
  • 内容区域的实际尺寸会根据内边距和边框自动调整

尺寸计算

  • 内容区域宽度 = width - padding-left - padding-right - border-left - border-right
  • 内容区域高度 = height - padding-top - padding-bottom - border-top - border-bottom
.border-box {
  width300px;        /* 总宽度,包括内容、内边距和边框 */
  height200px;       /* 总高度,包括内容、内边距和边框 */
  padding20px;       /* 所有方向的内边距 */
  border10px solid #333/* 边框 */
  margin30px;        /* 所有方向的外边距 */
  background-color#e0f0ff;
  box-sizing: border-box/* 使用怪异盒模型 */
}
  • 总宽度: 固定为300px
  • 总高度: 固定为200px
  • 内容区域宽度: 300px - 20px × 2 - 10px × 2 = 240px
  • 内容区域高度: 200px - 20px × 2 - 10px × 2 = 140px

两种盒模型的比较

<div class="box standard-box">标准盒模型 (content-box)</div>
<div class="box border-box">怪异盒模型 (border-box)</div>
.box {
  width300px;
  height100px;
  padding20px;
  border10px solid #333;
  margin20px;
  background-color#f0f0f0;
}
 
.standard-box {
  box-sizing: content-box;
}
 
.border-box {
  box-sizing: border-box;
}
 

实际应用中的选择

* {
  box-sizing: border-box;
}

JavaScript中的"三剑客"属性

这三组属性是JavaScript中用于获取元素尺寸和位置信息的重要工具,它们与CSS盒模型密切相关。

client 系列

client系列属性用于获取元素的可视区域信息,不包括边框、外边距和滚动条。

  • clientWidth: 内容宽度 + 内边距宽度(不包括边框和滚动条)
  • clientHeight: 内容高度 + 内边距高度(不包括边框和滚动条)
  • clientLeft: 元素左边框的宽度
  • clientTop: 元素上边框的宽度
// 获取元素的可视区域宽度(内容+内边距)
const visibleWidth = element.clientWidth;
 
// 获取元素的可视区域高度(内容+内边距)
const visibleHeight = element.clientHeight;

offset 系列

offset系列属性用于获取元素的尺寸和相对于父元素的位置信息,包括边框和内边距。

  • offsetWidth: 内容宽度 + 内边距宽度 + 边框宽度
  • offsetHeight: 内容高度 + 内边距高度 + 边框高度
  • offsetLeft: 元素左边框外侧到最近定位父元素左边框内侧的距离
  • offsetTop: 元素上边框外侧到最近定位父元素上边框内侧的距离
  • offsetParent: 最近的定位父元素(position不为static)
// 获取元素的总宽度(内容+内边距+边框)
const totalWidth = element.offsetWidth;
 
// 获取元素相对于定位父元素的左偏移
const leftPosition = element.offsetLeft;
 
// 获取元素相对于页面的绝对位置
functiongetElementPosition(el) {
let left = 0, top = 0;
while (el) {
    left += el.offsetLeft;
    top += el.offsetTop;
    el = el.offsetParent;
  }
return { left, top };
}

scroll 系列

scroll系列属性用于获取元素滚动相关的信息。

  • scrollWidth: 元素内容的总宽度,包括因溢出而不可见的部分
  • scrollHeight: 元素内容的总高度,包括因溢出而不可见的部分
  • scrollLeft: 元素水平滚动条已滚动的距离
  • scrollTop: 元素垂直滚动条已滚动的距离
// 获取元素内容的总高度(包括不可见部分)
const totalContentHeight = element.scrollHeight;
 
// 获取元素已滚动的垂直距离
const scrolledDistance = element.scrollTop;
 
// 检测元素是否已滚动到底部
functionisScrolledToBottom(el) {
  return el.scrollHeight - el.scrollTop <= el.clientHeight + 1; // 添加1像素容差
}
 
// 平滑滚动到元素顶部
element.scrollTo({
  top: 0,
  behavior: 'smooth'
});

实战应用场景

内容区:宽度陷阱与流体写法

<style>
  .fluid {
    width: 90%;                     /* 相对于父元素 */
    max-width: 1200px;              /* 不要无限拉伸 */
    min-width: 320px;               /* 移动端最小体验 */
    margin: 0 auto;                 /* 水平居中 */
  }
</style>
<div class="fluid">响应式宽度</div>

内边距:逻辑写法 + 响应式

/* 逻辑属性:自动适应 RTL/LTR */
.logical {
  padding-block: 1rem;    /* 上下 */
  padding-inline: 2rem;   /* 左右 */
}
 
/* clamp() 让内边距随视口连续变化 */
.clamp-pad {
 
/* 
一条 clamp() 即可让间距、字号、圆角等无缝缩放,告别大量断点媒体查询。
8px → 最小安全间距(手机小屏不会更窄)
2vw → 理想值,随视口宽度线性变化
24px → 最大上限(大屏或横屏时不再膨胀)
*/
  padding: clamp(8px, 2vw, 24px);
}

边框:圆角 + 阴影 + 图像

<style>
  .round { border-radius: 12px; }           /* 统一圆角 */
  .circle { border-radius: 50%; }           /* 正圆 */
  .shadow { box-shadow: 0 4px 12px #0003; } /* 柔和阴影 */
  .gradient-border {
    border: 4px solid;
    border-image: linear-gradient(45deg, #e91e63, #2196f3) 1;
  }
</style>
<div class="round shadow gradient-border">炫酷边框</div>

外边距:折叠、负值、auto

<style>
  /* 1. 上下 margin 折叠演示 */
  .a { margin-bottom: 20px; }
  .b { margin-top: 30px; }   /* 实际间距 = 30px(取大) */
 
  /* 2. 负值重叠 */
  .overlap { margin-top: -10px; }  /* 向上提 10px,实现贴合 */
 
  /* 3. 水平居中经典写法 */
  .center { width: 300px; margin: 0 auto; }
</style>

margin 塌陷与解决方案

现象:上下相邻兄弟或父子 margin 合并为最大值。

解决:

  • 兄弟层:只写单边 margin
  • 父子层:给父级加
    • padding-top: 1px 或
    • border-top: 1px solid transparent 或
    • display: flow-root / overflow: hidden
<style>
  .dad { background: #ffc; }
  .son { margin-top: 40px; }   /* 无塌陷:dad 加 overflow:hidden */
  .dad.fix { overflow: hidden; }
</style>
 
<div class="dad fix">
  <div class="son">不再塌陷</div>
</div>  
 

检测元素是否在视口中

  1. 布局计算:使用offset属性获取元素的实际尺寸和位置
  2. 滚动检测:使用scroll属性检测滚动位置,实现无限滚动、懒加载等功能
  3. 可视区域检测:使用client属性确定元素的可视区域大小
  4. 元素位置调整:根据这些属性动态调整元素位置

检测元素是否在视口中

function isElementInViewport(el) {
  const rect = el.getBoundingClientRect();
 
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
    rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  );
}
 
// 使用方法
const element = document.querySelector('.my-element');
if (isElementInViewport(element)) {
  console.log('元素在视口中可见');
} else {
  console.log('元素不在视口中');
}

生产级卡片组件

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>生产级卡片组件</title>
<style>
  /* 1. 全局统一 */
  html { box-sizing: border-box; }
  *, *::before, *::after { box-sizing: inherit; }
 
  /* 2. 卡片本体 */
  .card {
    /* 尺寸 */
    inline-size: clamp(300px, 90%, 600px);  /* 逻辑属性:行内方向宽度 */
    block-size: auto;                       /* 块方向高度由内容决定 */
 
    /* 内边距:随视口连续变化 */
    padding-block: clamp(12px, 3vw, 24px);  /* 上下 */
    padding-inline: clamp(16px, 4vw, 32px); /* 左右 */
 
    /* 边框与圆角 */
    border: 1px solid #e0e0e0;
    border-radius: 12px;
 
    /* 阴影 */
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
 
    /* 居中 */
    margin-inline: auto; /* 逻辑写法:LTR=左右,RTL=右左 */
 
    /* 内容溢出 */
    overflow: hidden;
  }
 
  /* 3. 内部元素 */
  .card__title {
    margin-block: 0 0.5rem;   /* 逻辑上下边距 */
    font-size: clamp(1.125rem, 2.5vw, 1.5rem);
  }
  .card__body {
    line-height: 1.6;
  }
</style>
</head>
<body>
  <article class="card">
    <h2 class="card__title">响应式卡片</h2>
    <p class="card__body">宽度在 300-600px 之间连续变化,内边距随视口缩放,始终贴合。</p>
  </article>
</body>
</html>

总结回顾

  1. 标准盒模型(content-box):width/height只包括内容区域,不包括内边距和边框
  2. 怪异盒模型(border-box):width/height包括内容区域、内边距和边框
  3. client系列:获取元素可视区域信息(内容+内边距)
  4. offset系列:获取元素完整尺寸(内容+内边距+边框)和位置信息
  5. scroll系列:获取元素内容完整尺寸和滚动信息
← Previous postCSS布局
Next post →CSS动画