很全面的前端面试——CSS篇(上)

前言

CSS的面试题可谓是鱼龙混杂,在整理这一类的面试题时,也是觉得写之不尽,越写越多,原本是打算用一篇文章去写尽CSS的面试题,现在看来是我想多了,在文章字符数达到10w的时候被提示超出最大字符限制了,那么就将CSS篇暂时分为上下篇吧

文章里的一些实例也因为篇幅原因无法展示,也许以后会补上?不知道

算了,多说无益,让我们开始吧

一、介绍一下CSS选择器及其优先级

CSS(层叠样式表)是网页设计的核心语言之一,而选择器则是CSS的基石CSS 选择器用于定位 HTML 元素,从而为其应用样式规则。

CSS选择器基础:认识选择器

CSS选择器的作用是"选择"HTML元素并为其应用样式。就像在人群中找人一样,我们需要不同的"识别方式"来找到特定的元素。

  • CSS 定义:层叠样式表,用于选择 HTML DOM 元素并应用样式规则。

  • 引入方式

    • 内联标签(<style> :在 HTML 文件的 <head> 标签内使用 <style> 标签编写 CSS 代码。
    • 外联样式(<link> :通过 <link> 标签引入外部 CSS 文件。
    • 行内样式 :直接在 HTML 元素的 style 属性中编写 CSS 代码。
  • 渲染流程:先下载样式,再解析 DOM 并应用样式,DOM 与 CSS 结合形成渲染树(render tree),最后通过浏览器渲染引擎渲染得到页面。

渲染树示意图

选择器类型一览

选择器类型 格式示例 优先级权重
id 选择器 #id 100
类选择器 .classname 10
属性选择器 a[ref="eee"] 10
伪类选择器 li:last-child 10
标签选择器 div 1
伪元素选择器 li:after 1
相邻兄弟选择器 h1+p 0
子选择器 ul>li 0
后代选择器 li a 0
通配符选择器 * 0

1. 基本选择器类型

标签选择器 - 通过HTML标签名选择元素:

css 复制代码
p {
  color: blue;
}

这会选择页面中所有的<p>段落元素,并将文字颜色设为蓝色。

类选择器 - 通过class属性选择元素:

css 复制代码
.highlight {
  background-color: yellow;
}

在HTML中使用:<p class="highlight">这段文字会高亮</p>

ID选择器 - 通过id属性选择唯一元素:

css 复制代码
#header {
  font-size: 24px;
}

在HTML中使用:<div id="header">网站标题</div>

二、CSS选择器优先级:谁说了算?

当多个样式规则作用于同一个元素时,浏览器如何决定应用哪个样式呢?这就涉及到优先级的概念。

优先级权重计算规则:

  • 标签选择器:1
  • 类选择器:10
  • ID选择器:100
  • 行内样式:1000
  • !important:最高优先级

让我们看一个实际例子:

html 复制代码
<div class="container" id="main">
  <P style="color: pink;">我看看怎么个事!</P>
</div>
css 复制代码
p {
  color: blue !important;
}
.container p {
  color: red;
}
#main p {
  color: green;
}

虽然行内样式权重最高(1000),但!important具有最高优先级,所以最终文字显示蓝色。如果没有!important,则行内样式的粉色会生效。

实例与展示

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS</title>
    <style>
        /* 1(样式) */
        /* 如果一定要显示蓝色 */
        /* !important 很重要 */
        p {
            color: blue !important;
        }
        /* 10(类) + 1(样式) */
        .container p {
            color: red;
        }
        /* 100(id) + 1(样式) */
        #main p {
            color: green;
        }

    </style>
</head>
<body>
    <div class="container" id="main">
        <P style="color: pink;/* 1000 */">我看看怎么个事!</P>
    </div>
</body>
</html>

三、组合选择器:精准定位元素

1. 后代选择器(空格)

选择某个元素内部的所有特定后代元素:

css 复制代码
.container p {
  text-decoration: underline;
}

这会选择.container内部的所有<p>元素,无论嵌套多深。

2. 子元素选择器(>)

只选择直接子元素:

css 复制代码
.container > p {
  font-weight: bold;
}

这只会选择.container直接子元素中的<p>,不会选择嵌套在其他元素中的<p>

3. 相邻兄弟选择器(+)

选择紧接在某个元素后的第一个兄弟元素:

css 复制代码
h1 + p {
  color: red;
}

这会让紧跟在<h1>后的第一个<p>变为红色。

4. 通用兄弟选择器(~)

选择某个元素后面的所有同级元素:

css 复制代码
h1 ~ p {
  color: blue;
}

这会让<h1>后面的所有<p>兄弟元素变为蓝色。

实例与展示

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>选择器</title>
    <style>
        /* 相邻兄弟选择器 */
        /* 得分为2 */
        h1+p{
            color: red;
        }
        /* 通用兄弟选择器 */
        /* 得分为2 */
        h1~p{
            color: blue; 
        }
        /* 子元素选择器 直接子元素*/
        /* 用于选择.container元素内的段落文本 */
        .container >p{
                font-weight: bold;
        }
        .container p{       
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>标题</h1>
        <p>这是第一段文字</p>
        <p>这是第二段文字</p>
        <a href="">链接</a>
        <span>这是一个span元素</span>
        <div class="inner">
            <p>这是一个内部段落</p>
        </div>
    </div>
</body>
</html>

四、伪类选择器:元素的状态选择

伪类选择器允许我们根据元素的状态或位置来应用样式。

1. 交互状态伪类

css 复制代码
/* 鼠标悬停时 */
p:hover {
  background-color: yellow;
}

/* 按钮被点击时 */
button:active {
  background-color: red;
  color: white;
}

/* 输入框获得焦点时 */
input:focus {
  border: 2px solid blue;
}

2. 结构伪类

css 复制代码
/* 选择奇数位置的列表项 */
li:nth-child(odd) {
  background-color: lightgray;
}

/* 选择除最后一个外的所有子元素 */
li:not(:last-child) {
  margin-bottom: 10px;
}

3. 表单相关伪类

css 复制代码
/* 复选框被选中时改变相邻标签颜色 */
input:checked + label {
  color: blue;
}

实例与展示

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        /* 伪类选择器 */
        button:active{
            background-color: red; 
            color: white;
        }
        p:hover{
            background-color: yellow;
        }
        /* 鼠标选中文本的效果 */
        ::selection{
            background-color: blue;
            color: white;
        }
        /* 输入框的效果 */
        input:focus{
            border: 2px solid blue;
            outline: none; /* 移除浏览器默认outline */
            accent-color: blue;  /* 设置复选框选中标记颜色 */
        }
         /* 复选框的效果 */
        input:checked + label{
            color: blue;
        } 
        li:nth-child(odd){
            background-color: lightgray;
        }
        li:not(:last-child){
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>伪类选择器示例</h1>
        <button>点击我</button>
        <p>鼠标悬浮在这里</p>
        <input type="text" placeholder="输入框">
        <input type="checkbox" id="option1">
        <label for="option1">选项1</label>
        <input type="checkbox" id="option2" checked>
        <label for="option2">选项2</label>
        <ul>
            <li>列表项1</li>
            <li>列表项2</li>
            <li>列表项3</li>
            <li>列表项4</li>
        </ul>
    </div>
</body>
</html>

五、易错点一------组合选择器的优先级计算

下面我们来看一个题目

js 复制代码
.container ul li:nth-child(odd)

这个选择器的优先级是多少?

答案为10+1+1+10=22

因为:nth-child(odd)是伪类选择器,优先级是10

六、易错点二------nth-child vs nth-of-type:关键区别

这两个选择器经常让人困惑,让我们通过一个例子来理解它们的区别:

html 复制代码
<div class="container">
  <h1>nth-child vs nth-of-type 例子</h1>
  <p>这是第一个段落</p>
  <div>这是一个div</div>
  <p>这是第二个段落</p>
  <p>这是第三个段落</p>
  <div>这是第二个div</div>
</div>
css 复制代码
/* 选择.container下第3个子元素,且这个元素必须是<p>标签 */
.container p:nth-child(3) {
  background-color: yellow;
}
/* 实际不会生效,因为第3个子元素是div不是p */

/* 选择.container下第3个<p>类型的元素 */
.container p:nth-of-type(3) {
  background-color: lightblue;
}
/* 这会选择"这是第三个段落" */

关键区别

  • nth-child(n):选择父元素的第n个子元素,且必须是指定类型
  • nth-of-type(n):选择父元素下第n个指定类型的子元素(忽略其他类型元素)

二、谈谈display的属性值

我们的详解会基于下面的表进行

display 属性值速查表

属性值 作用描述 示例代码 典型场景
none 完全隐藏元素,不占空间 .hidden { display: none; } 动态显示 / 隐藏元素
block 块级元素,独占一行,支持宽高 .block { display: block; width: 200px; } 容器、段落、标题
inline 行内元素,不换行,宽高由内容决定 .inline { display: inline; } 文本、链接、图片
inline-block 不换行,但支持宽高 .btn { display: inline-block; width: 100px; } 水平按钮组、图标
table 模拟表格布局 .table { display: table; } 数据表格、等高布局
table-cell 模拟表格单元格,支持垂直居中 .cell { display: table-cell; vertical-align: middle; } 表单对齐、垂直居中
flex 弹性布局,一维排列 .flex { display: flex; justify-content: center; } 导航栏、自适应卡片
grid 网格布局,二维排列 .grid { display: grid; grid-template-columns: repeat(3, 1fr); } 图片画廊、响应式网格

一、基础布局属性值

1. display: none

  • 作用:完全隐藏元素,不占用文档空间,脱离文档流。

  • 示例

    xml 复制代码
    <style>
      .hidden { display: none; }
    </style>
    <div>可见内容</div>
    <div class="hidden">隐藏内容</div> <!-- 完全消失 -->
    <div>可见内容</div>

2. display: block

  • 作用:块级元素,独占一行,宽度默认为父元素的 100%,支持设置宽高。

  • 示例

    xml 复制代码
    <style>
      .block {
        display: block;
        width: 200px;
        height: 50px;
        background: lightblue;
      }
    </style>
    <div class="block">块级元素1</div>
    <div class="block">块级元素2</div> <!-- 自动换行 -->
  • 常见元素<div>, <p>, <h1>, <ul>等。

3. display: inline

  • 作用:行内元素,不换行,宽度由内容决定,无法设置宽高。

  • 示例

    xml 复制代码
    <style>
      .inline {
        display: inline;
        background: lightgreen;
        width: 200px; /* 无效 */
      }
    </style>
    <span class="inline">行内内容1</span>
    <span class="inline">行内内容2</span> <!-- 不换行 -->
  • 限制 :垂直方向的marginpadding无效,水平方向有效。

  • 常见元素<span>, <a>, <img>等。

4. display: inline-block

  • 作用 :行内块元素,不换行,但支持设置宽高,兼具inlineblock的特性。

  • 示例

    xml 复制代码
    <style>
      .inline-block {
        display: inline-block;
        width: 100px;
        height: 50px;
        background: lightcoral;
      }
    </style>
    <div class="inline-block">元素1</div>
    <div class="inline-block">元素2</div> <!-- 不换行 -->
  • 应用场景:水平排列的按钮、图片画廊等。

二、表格布局属性值

5. display: table

  • 作用 :将元素渲染为块级表格,需配合子元素的table-rowtable-cell

  • 示例

    xml 复制代码
    <style>
      .table { display: table; }
      .row { display: table-row; }
      .cell { display: table-cell; border: 1px solid #ccc; }
    </style>
    <div class="table">
      <div class="row">
        <div class="cell">单元格1</div>
        <div class="cell">单元格2</div>
      </div>
    </div>
  • 特性:自动应用表格的布局规则(如单元格等高)。

6. display: table-cell

  • 作用:模拟表格单元格,常用于垂直居中。

  • 示例

    xml 复制代码
    <style>
      .container {
        display: table-cell;
        width: 200px;
        height: 100px;
        background: lightblue;
        vertical-align: middle; /* 垂直居中 */
        text-align: center; /* 水平居中 */
      }
    </style>
    <div class="container">居中内容</div>

三、弹性布局(Flexbox)

7. display: flex

  • 作用:将元素转换为弹性容器,子元素成为弹性项目,支持灵活的对齐和分布。

  • 示例:水平均匀分布三个按钮

    xml 复制代码
    <style>
      .flex-container {
        display: flex;
        justify-content: space-between; /* 均匀分布 */
      }
      .btn { padding: 10px; background: lightgreen; }
    </style>
    <div class="flex-container">
      <div class="btn">按钮1</div>
      <div class="btn">按钮2</div>
      <div class="btn">按钮3</div>
    </div>
  • 常用属性flex-direction, justify-content, align-items, flex-wrap等。

8. display: inline-flex

  • 作用:弹性容器,但表现为内联元素,不独占一行。

  • 示例

    xml 复制代码
    <style>
      .inline-flex {
        display: inline-flex;
        background: lightyellow;
      }
      .item { width: 50px; height: 50px; background: lightcoral; }
    </style>
    <div class="inline-flex">
      <div class="item">1</div>
      <div class="item">2</div>
    </div>

四、网格布局(Grid)

9. display: grid

  • 作用:将元素转换为网格容器,子元素成为网格项目,支持二维布局。

  • 示例:创建 3×3 网格

    xml 复制代码
    <style>
      .grid-container {
        display: grid;
        grid-template-columns: repeat(3, 1fr); /* 3列,等宽 */
        gap: 10px;
      }
      .grid-item { background: lightblue; padding: 20px; }
    </style>
    <div class="grid-container">
      <div class="grid-item">1</div>
      <div class="grid-item">2</div>
      <div class="grid-item">3</div>
      <!-- 自动填充剩余网格 -->
    </div>
  • 常用属性grid-template-columns, grid-template-rows, gap, place-items等。

10. display: inline-grid

  • 作用:网格容器,但表现为内联元素。

  • 示例

    xml 复制代码
    <style>
      .inline-grid {
        display: inline-grid;
        grid-template-columns: 50px 50px;
        gap: 5px;
        background: lightyellow;
      }
    </style>
    <div class="inline-grid">
      <div>1</div>
      <div>2</div>
    </div>

三、block、inline和inline-block的区别

block(块级元素)

  • 独占一行,前后会换行
  • 可以设置宽度(width)、高度(height)、内外边距(margin/padding)
  • 默认宽度为父元素的100%
  • 常见块级元素:<div>, <p>, <h1>-<h6>, <ul>, <ol>, <li>
css 复制代码
<div style="border: 2px solid blue; padding: 10px;">
  这是一个块级元素
  <p style="background: lightyellow;">块级元素内的段落</p>
</div>

inline(行内元素)

  • 不会独占一行,与其他行内元素排列在同一行
  • 设置宽度和高度无效
  • 水平方向的内外边距有效,垂直方向的内外边距不会影响其他元素
  • 默认宽度为内容宽度
  • 常见行内元素:<span>, <a>, <strong>, <em>, <img>
css 复制代码
<p>
  这是一段包含<span style="background: pink; padding: 0 10px;">行内元素</span>的文本,
  <a href="#" style="color: red;">链接</a>也是行内元素。
</p>

inline-block(行内块元素)

  • 结合了inline和block的特性
  • 不会独占一行,与其他行内元素排列在同一行
  • 可以设置宽度、高度、内外边距
  • 默认宽度为内容宽度
css 复制代码
<div style="background: #f0f0f0; padding: 10px;">
  <span style="display: inline-block; width: 80px; height: 40px; background: lightgreen;">项目1</span>
  <span style="display: inline-block; width: 80px; height: 60px; background: lightblue;">项目2</span>
  <span style="display: inline-block; width: 80px; height: 50px; background: lightcoral;">项目3</span>
</div>

主要区别对比

特性 block inline inline-block
是否换行
设置width/height 有效 无效 有效
margin/padding 全部有效 水平有效,垂直特殊 全部有效
默认宽度 父元素100% 内容宽度 内容宽度
元素排列 垂直排列 水平排列 水平排列
包含关系 可包含其他块/行内 通常只含文本/行内 可包含其他块/行内

使用场景

  • block:用于构建页面主要结构,如头部、导航、内容区域、页脚等
  • inline:用于文本修饰或小图标等不需要设置宽高的元素
  • inline-block:需要水平排列但又要设置宽高的元素,如导航菜单项、按钮组等

下面分别举一些例子来佐证

block

css 复制代码
<!-- 页面头部 -->
<header style="display: block; background: #333; color: white; padding: 20px; text-align: center;">
  <h1>网站标题</h1>
</header>

<!-- 导航栏 -->
<nav style="display: block; background: #444; padding: 10px;">
  <ul style="margin: 0; padding: 0;">
    <li style="display: inline-block; margin-right: 15px;"><a href="#" style="color: white;">首页</a></li>
    <li style="display: inline-block; margin-right: 15px;"><a href="#" style="color: white;">产品</a></li>
    <li style="display: inline-block;"><a href="#" style="color: white;">关于</a></li>
  </ul>
</nav>

<!-- 内容区域 -->
<main style="display: block; padding: 20px; background: #f5f5f5;">
  <p>这里是页面主要内容...</p>
</main>

<!-- 页脚 -->
<footer style="display: block; background: #333; color: white; text-align: center; padding: 10px;">
  <p>© 2023 版权所有</p>
</footer>

inline

xml 复制代码
<p>
  这是一段包含 <strong style="display: inline; color: red;">强调文本</strong> 和 
  <a href="#" style="display: inline; text-decoration: underline;">超链接</a> 的段落。
</p>

<!-- 小图标(通过伪元素实现) -->
<style>
  .icon::before {
    content: "★";
    display: inline;
    color: gold;
    margin-right: 5px;
  }
</style>
<p><span class="icon">重要提示</span>:请阅读注意事项。</p>

inline-block

css 复制代码
<!-- 导航菜单项 -->
<div style="background: #f0f0f0; padding: 10px;">
  <a href="#" style="display: inline-block; width: 100px; height: 40px; line-height: 40px; text-align: center; background: #4CAF50; color: white; margin-right: 5px;">首页</a>
  <a href="#" style="display: inline-block; width: 100px; height: 40px; line-height: 40px; text-align: center; background: #2196F3; color: white; margin-right: 5px;">产品</a>
  <a href="#" style="display: inline-block; width: 100px; height: 40px; line-height: 40px; text-align: center; background: #FF9800; color: white;">关于</a>
</div>

<!-- 按钮组 -->
<div style="margin-top: 20px;">
  <button style="display: inline-block; padding: 8px 16px; margin-right: 10px; background: #e0e0e0; border: none;">取消</button>
  <button style="display: inline-block; padding: 8px 16px; background: #4CAF50; color: white; border: none;">确认</button>
</div>

四、说一说你知道的隐藏元素的方法

在前端开发中,隐藏元素是一个常见但需要谨慎处理的操作。不同的隐藏方法会导致不同的渲染表现、可访问性影响和性能开销。

一、完全移除型隐藏

1. display: none - 彻底移除元素

css 复制代码
.hide-element {
  display: none;
}

核心特性

  • 元素不会出现在渲染树中
  • 不占据任何文档流空间
  • 所有子元素都会被连带隐藏
  • 无法触发任何DOM事件
  • 屏幕阅读器完全忽略该元素
  • 触发浏览器重排(reflow)

性能影响:高(导致布局重新计算)

适用场景

  • 需要完全移除不需要的元素
  • 标签页切换内容区域
  • 响应式布局中在不同断点隐藏元素

示例

html 复制代码
<div class="tab-content" style="display: none;">
  这个内容将在切换标签时显示
</div>

二、视觉隐藏但保留空间

2. visibility: hidden - 隐形占位

css 复制代码
.invisible {
  visibility: hidden;
}

核心特性

  • 元素不可见但保留原有空间
  • 无法触发鼠标等交互事件
  • 只导致重绘(repaint),性能较好
  • 可通过visibility: visible显示子元素
  • 屏幕阅读器无法访问

性能影响:中等(仅重绘)

适用场景

  • 需要保留布局占位的隐藏
  • 实现自定义复选框/单选框样式
  • 需要保持布局稳定的场景

示例

html 复制代码
<div class="placeholder" style="visibility: hidden;">
  这里的内容隐藏但仍占位
</div>

三、透明化隐藏

3. opacity: 0 - 完全透明

css 复制代码
.transparent {
  opacity: 0;
}

核心特性

  • 元素完全透明但占据空间
  • 仍能触发所有DOM事件
  • 会创建新的复合层,适合动画
  • 屏幕阅读器可以访问
  • 子元素无法单独恢复可见性

性能影响:低(GPU加速)

适用场景

  • 需要淡入淡出动画
  • 需要隐藏但仍需交互的元素
  • 可访问性要求高的内容

示例

html 复制代码
<button class="fade-button" style="opacity: 0;">
  这个按钮透明但仍可点击
</button>

四、定位移出型隐藏

4. position: absolute - 移出视口

css 复制代码
.off-screen {
  position: absolute;
  left: -9999px;
  top: -9999px;
}

核心特性

  • 视觉上不可见且不占空间
  • 仍保留DOM位置和事件绑定
  • 屏幕阅读器可以访问
  • 不影响页面布局流

性能影响:高(导致重排)

适用场景

  • SEO优化需要隐藏但可抓取的内容
  • 可访问性要求高的隐藏内容
  • 需要隐藏但保留表单元素值

示例

html 复制代码
<label for="search" class="visually-hidden">搜索框</label>
<input type="text" id="search">

五、层叠隐藏

5. z-index: 负值 - 下层遮盖

css 复制代码
.under-layer {
  position: relative;
  z-index: -1;
}

核心特性

  • 元素被其他层叠元素遮盖
  • 仍占据原有文档流空间
  • 事件触发取决于遮盖元素
  • 屏幕阅读器可以访问

性能影响:低(复合层处理)

适用场景

  • 背景元素隐藏
  • 特殊视觉效果实现
  • 需要保留元素但置于底层的场景

示例

html 复制代码
<div class="background-element" style="z-index: -1;">
  这个内容会被其他元素遮盖
</div>

六、裁剪型隐藏

6. clip/clip-path - 元素裁剪

css 复制代码
.clipped {
  /* 传统clip方法(已废弃) */
  clip: rect(0 0 0 0);
  
  /* 现代clip-path方法 */
  clip-path: circle(0);
}

核心特性

  • 视觉隐藏但保留元素空间
  • 不响应交互事件
  • 屏幕阅读器行为不一致
  • 支持平滑动画过渡

性能影响:中等(GPU加速)

适用场景

  • 创意动画效果
  • 渐进式内容展示
  • 需要保留元素尺寸的隐藏

示例

html 复制代码
<div class="expandable" style="clip-path: inset(0 100% 0 0);">
  这个内容可以通过动画展开
</div>

七、变形隐藏

7. transform: scale(0) - 零尺寸缩放

css 复制代码
.scaled-zero {
  transform: scale(0);
}

核心特性

  • 元素视觉尺寸为零但保留布局空间
  • 不响应交互事件
  • 保持元素原本的盒模型特性
  • 屏幕阅读器可以访问
  • 支持平滑的缩放动画

性能影响:低(GPU加速)

适用场景

  • 需要缩放动画的元素
  • 需要保留布局空间的隐藏
  • 特殊交互效果的实现

示例

html 复制代码
<button class="zoom-button" style="transform: scale(0);">
  点击后会放大显示
</button>

方法对比表

方法 占据空间 可交互性 可访问性 动画支持 性能影响 SEO友好
display: none
visibility: hidden ✔️ 有限
opacity: 0 ✔️ ✔️ ✔️ ✔️ ✔️
position: absolute ✔️ ✔️ ✔️
z-index: 负值 ✔️ 可能 ✔️ ✔️
clip-path ✔️ 可能 ✔️ 可能
transform: scale(0) ✔️ ✔️ ✔️ ✔️

选型建议

  1. 需要彻底移除元素
  • 首选:display: none
  • 场景:标签页切换、响应式布局隐藏
  1. 需要保留布局空间
  • 首选:visibility: hidden
  • 替代:opacity: 0(如需交互)
  • 场景:占位隐藏、自定义表单控件
  1. 需要动画效果
  • 淡入淡出:opacity
  • 缩放动画:transform: scale()
  • 裁剪动画:clip-path
  1. 需要可访问性支持
  • 首选:position: absolute移出视口
  • 替代:opacity: 0
  • 场景:屏幕阅读器可读的隐藏内容
  1. 需要SEO优化
  • 首选:position: absolute
  • 替代:z-index负值
  • 场景:隐藏关键词但需要被搜索引擎抓取

在给面试官介绍这部分知识的时候,建议采用下面的步骤
首先分类概述8种方法(完全移除/视觉隐藏/特殊技巧三大类),然后逐类分析核心方法的特性、性能差异和应用场景,接着通过布局影响、交互性等维度系统对比,最后结合项目实战经验说明选型策略,并延伸提及现代CSS特性。这种回答既展现知识体系完整性,又体现技术决策的思考过程。

五、display:none与visibility:hidden有何区别

1. 两者差异

(1)渲染机制差异

  • display: none

    浏览器会从 渲染树(Render Tree) 中完全移除该元素,后续布局计算时忽略其存在。

    xml 复制代码
    <div style="display: none;">Hidden</div>
    <!-- 等同于DOM中删除此元素 -->
  • visibility: hidden

    元素仍保留在渲染树中,但会被标记为不可见(类似透明效果),浏览器仍会计算其布局。

    xml 复制代码
    <div style="visibility: hidden;">Invisible</div>
    <!-- 类似于设置透明度为0,但保留占位 -->

(2)性能影响

  • display: none :触发 重排(Reflow) ,影响较大(尤其是频繁切换时)。
  • visibility: hidden :仅触发 重绘(Repaint) ,性能开销更小。

(3)子元素行为

visibility: hidden 会隐藏元素但保留占位,其子元素可通过 visibility: visible 重新显示;
display: none 会完全移除元素及其子元素,子元素无法通过 display: block 恢复显示。

css 复制代码
<div style="visibility: hidden;">
  <span style="visibility: visible;">我仍会显示!</span>
</div>

<div style="display: none;">
  <span style="display: block;">我永远不会显示!</span>
</div>

2. 应用场景差异

何时用 display: none

  • 需要 彻底移除元素(如标签页切换、响应式布局隐藏侧边栏)。
  • 需要 减少DOM渲染压力(如长列表的懒加载)。

何时用 visibility: hidden

  • 需要 保持布局稳定(如占位隐藏即将加载的内容)。
  • 实现 自定义复选框/单选框 的视觉替换。
  • 需要 保留元素状态(如表单隐藏字段仍需提交数据)。

3. 可以给面试官整点"夜宵"

(1)延伸问题(前文讲过)

  • "如果希望隐藏元素但仍能被屏幕阅读器读取,你会怎么做?"
    答:使用 绝对定位移出视口.visually-hidden 工具类(如Bootstrap的屏幕阅读器专用样式)。

(2)框架中的表现

  • Reactv-if 对应 display: nonev-show 对应 visibility: hidden
  • CSS动画visibility 可配合 transition 实现延迟隐藏(避免元素突然消失)

4. 核心区别总结

特性 display: none visibility: hidden
是否占据布局空间 ❌ 完全移除,不占空间 ✔️ 隐藏但保留原有空间
是否触发重排/重绘 触发重排 (Reflow) 仅触发重绘 (Repaint)
子元素是否可显示 ❌ 子元素必然隐藏 ✔️ 子元素可设 visibility: visible 单独显示
是否响应事件 ❌ 无法触发任何事件 ❌ 无法触发事件(但DOM仍存在)
屏幕阅读器可访问性 ❌ 完全忽略 ❌ 无法访问

六、对盒模型的理解

盒模型是 CSS 布局的基础,描述了元素在页面中所占空间的计算方式。 每个元素都被视为一个矩形盒子,由四个主要部分组成:

一、盒模型的四个组成部分

1. 内容区(Content)

  • 包含元素的实际内容(文本、图片等)。
  • widthheight 属性控制。

2. 内边距(Padding)

  • 内容区与边框之间的距离。
  • padding-toppadding-rightpadding-bottompadding-left 控制,也可简写为 padding

3. 边框(Border)

  • 围绕内边距和内容区的线条。
  • border-widthborder-styleborder-color 控制,也可简写为 border

4. 外边距(Margin)

  • 元素与其他元素之间的距离。
  • margin-topmargin-rightmargin-bottommargin-left 控制,也可简写为 margin

二、盒模型的宽度和高度计算

1. 标准盒模型(默认)

总宽度 = width + padding-left + padding-right + border-left-width + border-right-width

总高度 = height + padding-top + padding-bottom + border-top-width + border-bottom-width

示例

css 复制代码
div {
  width: 200px;       /* 内容区宽度 */
  padding: 10px;      /* 内边距:上下左右各10px */
  border: 2px solid;  /* 边框:2px宽 */
  margin: 15px;       /* 外边距:上下左右各15px */
}

总宽度 = 200 + 10×2 + 2×2 = 224px

总高度同理。

2. 怪异盒模型(IE 盒模型)

通过 box-sizing: border-box 设置。

总宽度 = width(已包含 paddingborder

总高度 = height(已包含 paddingborder

示例

css 复制代码
div {
  width: 200px;       /* 总宽度(包含padding和border) */
  padding: 10px;      /* 内边距:上下左右各10px */
  border: 2px solid;  /* 边框:2px宽 */
  box-sizing: border-box; /* 使用怪异盒模型 */
}

总宽度 = 200px(内容区宽度 = 200 - 10×2 - 2×2 = 176px)

下面我们横向介绍一下标准盒模型(content-box)和IE盒模型(border-box)的不同

标准盒模型的宽度 / 高度仅包含内容区,而 IE 盒模型的宽度 / 高度包含内容区、内边距和边框。

三、盒模型的关键特性

1. 外边距合并(Margin Collapsing)

  • 相邻元素的垂直外边距会合并为较大的一个。

  • 示例

    css 复制代码
    <div style="margin-bottom: 20px;">元素1</div>
    <div style="margin-top: 10px;">元素2</div>

    元素 1 和元素 2 之间的实际间距为 20px(取较大值)。

2. 内边距和边框不影响元素的位置

  • 增加内边距和边框会撑大元素,但不会改变其他元素的位置(除非超出父元素)。

3. 负外边距

  • 可用于将元素拉向其他元素,例如:margin-top: -10px 会使元素向上移动 10px。

四、最佳实践

1. 全局设置盒模型

css 复制代码
* {
  box-sizing: border-box;
}

这样可以避免因默认盒模型导致的宽度计算问题。

2. 合理使用内边距和外边距

  • 内边距用于控制元素内部的空间。
  • 外边距用于控制元素与其他元素之间的空间。

3. 避免外边距合并问题

  • 使用 flexgrid 布局,它们的子元素不会发生外边距合并。

五、盒模型可视化示例

xml 复制代码
<style>
  .box {
    width: 200px;
    height: 100px;
    padding: 20px;
    border: 5px solid #333;
    margin: 30px;
    background: lightblue;
  }
</style>
<div class="box">内容区</div>


这个盒子的总尺寸计算:

  • 宽度:200(内容) + 20×2(内边距) + 5×2(边框) = 250px
  • 高度:100(内容) + 20×2(内边距) + 5×2(边框) = 150px
  • 外边距(30px)会影响与其他元素的距离,但不包含在盒子自身尺寸内。

★★★ 建议在项目中统一使用 box-sizing: border-box,以减少宽度计算的复杂性。

七、谈一谈CSS3的新特性

一、选择器增强

1. 属性选择器

  • 作用:基于元素属性或属性值来选择元素。

  • 示例

    css 复制代码
    /* 选择所有title属性以"image"开头的元素 */
    [title^="image"] { border: 1px solid red; }
    
    /* 选择所有href属性包含"example"的链接 */
    a[href*="example"] { color: blue; }
    
    /* 选择所有data-type属性以"pdf"结尾的元素 */
    [data-type$="pdf"] { background: #f0f0f0; }

2. 伪类选择器

  • 作用:选择处于特定状态或位置的元素。

  • 示例

    css 复制代码
    /* 选择每个ul的第二个li */
    li:nth-child(2) { font-weight: bold; }
    
    /* 选择每个section的第一个p */
    p:first-of-type { color: #666; }
    
    /* 选择未被访问的链接 */
    a:link { color: blue; }
    
    /* 选择鼠标悬停的按钮 */
    button:hover { background: #e63946; }

3. 伪元素

  • 作用:选择元素的特定部分或创建虚拟元素。

  • 示例

    css 复制代码
    /* 在每个blockquote前添加引号 */
    blockquote::before { content: """; font-size: 2em; }
    
    /* 选中的文本变为红色背景 */
    ::selection { background: red; color: white; }
    
    /* 首字母大写并设置特殊样式 */
    p::first-letter { font-size: 200%; color: #e63946; }

二、盒模型相关

1. box-sizing 属性

  • 作用:控制宽度和高度的计算方式。

  • 示例

    css 复制代码
    .standard-box {
      box-sizing: content-box; /* 默认值,宽度=内容区 */
      width: 200px;
      padding: 20px; /* 总宽度=200+20*2=240px */
    }
    
    .border-box {
      box-sizing: border-box; /* 宽度=内容区+padding+border */
      width: 200px;
      padding: 20px; /* 内容区宽度=200-20*2=160px */
    }

2. 多列布局

  • 作用:将内容分成多列,类似报纸排版。

  • 示例

    css 复制代码
    .news-container {
      column-count: 3; /* 分成3列 */
      column-gap: 20px; /* 列间距20px */
      column-rule: 1px solid #ccc; /* 列分隔线 */
    }

三、背景与边框

1. 背景增强

  • 作用:提供更灵活的背景控制。

  • 示例

    css 复制代码
    .hero {
      background-image: url(bg.jpg);
      background-size: cover; /* 覆盖整个容器 */
      background-position: center; /* 居中显示 */
      background-repeat: no-repeat;
      background-attachment: fixed; /* 固定背景(滚动时不移动) */
    }
    
    .gradient {
      background: linear-gradient(to right, #ff512f, #f09819); /* 线性渐变 */
    }

2. 边框增强

  • 作用:创建圆角、阴影和图片边框。

  • 示例

    css 复制代码
    .card {
      border-radius: 10px; /* 圆角 */
      box-shadow: 0 4px 8px rgba(0,0,0,0.2); /* 阴影 */
    }
    
    .fancy-border {
      border-image: url(border.png) 30 round; /* 图片边框 */
    }

四、文本效果

1. 文本阴影

  • 作用:为文本添加阴影效果。

  • 示例

    css 复制代码
    h1 {
      text-shadow: 2px 2px 4px rgba(0,0,0,0.5); /* 水平偏移、垂直偏移、模糊半径、颜色 */
    }

2. 文字溢出处理

  • 作用:控制文本溢出容器时的显示方式。

  • 示例

    css 复制代码
    .truncate {
      white-space: nowrap; /* 不换行 */
      overflow: hidden; /* 溢出隐藏 */
      text-overflow: ellipsis; /* 显示省略号 */
    }

3. 自定义字体

  • 作用:引入非系统字体。

  • 示例

    css 复制代码
    @font-face {
      font-family: 'MyFont';
      src: url('fonts/myfont.woff2') format('woff2');
      font-weight: normal;
      font-style: normal;
    }
    
    body {
      font-family: 'MyFont', sans-serif;
    }

五、2D/3D 转换

1. 2D 转换

  • 作用:对元素进行平移、旋转、缩放和倾斜。

  • 示例

    css 复制代码
    .box {
      transform: translate(50px, 20px); /* 平移 */
      transform: rotate(45deg); /* 旋转 */
      transform: scale(1.5); /* 缩放 */
      transform: skew(20deg, 10deg); /* 倾斜 */
      
      /* 组合转换 */
      transform: translate(50px) rotate(30deg) scale(1.2);
    }

2. 3D 转换

  • 作用:创建 3D 空间中的变换效果。

  • 示例

    css 复制代码
    .cube {
      transform: perspective(1000px) rotateY(45deg); /* 3D透视和旋转 */
      transform-style: preserve-3d; /* 保留3D空间 */
    }
    
    .card {
      transition: transform 0.5s;
    }
    
    .card:hover {
      transform: rotateY(180deg); /* 翻转动画 */
      backface-visibility: hidden; /* 背面不可见 */
    }

六、动画与过渡

1. 过渡(Transition)

  • 作用:在两个状态之间平滑过渡。

  • 示例

    css 复制代码
    .button {
      background: blue;
      color: white;
      transition: background 0.3s ease, transform 0.3s ease;
    }
    
    .button:hover {
      background: red;
      transform: scale(1.1);
    }

2. 动画(Animation)

  • 作用:创建复杂的多阶段动画。

  • 示例

    css 复制代码
    @keyframes fadeIn {
      from { opacity: 0; }
      to { opacity: 1; }
    }
    
    .element {
      animation: fadeIn 2s infinite alternate; /* 无限播放,交替反向 */
    }
    
    @keyframes bounce {
      0%, 100% { transform: translateY(0); }
      50% { transform: translateY(-20px); }
    }
    
    .ball {
      animation: bounce 1s ease infinite;
    }

七、弹性布局(Flexbox)

  • 作用:高效地布局、对齐和分配容器内的空间。

  • 示例

    css 复制代码
    .flex-container {
      display: flex;
      justify-content: space-between; /* 水平分布 */
      align-items: center; /* 垂直居中 */
      flex-wrap: wrap; /* 自动换行 */
    }
    
    .flex-item {
      flex: 1; /* 平均分配空间 */
    }

八、网格布局(Grid)

  • 作用:创建二维网格布局。

  • 示例

    css 复制代码
    .grid-container {
      display: grid;
      grid-template-columns: repeat(3, 1fr); /* 3列等宽 */
      grid-template-rows: auto 100px; /* 2行,第一行自动高度 */
      gap: 20px; /* 行列间距 */
    }
    
    .item1 {
      grid-column: 1 / 3; /* 跨越第1-2列 */
      grid-row: 1; /* 位于第1行 */
    }

九、媒体查询(Responsive Design)

  • 作用:根据设备屏幕尺寸应用不同的样式。

  • 示例

    css 复制代码
    /* 大屏幕 */
    .container {
      width: 80%;
      margin: 0 auto;
    }
    
    /* 小屏幕 */
    @media (max-width: 768px) {
      .container {
        width: 100%;
        padding: 0 15px;
      }
    }
    
    /* 打印样式 */
    @media print {
      body {
        font-size: 12pt;
        color: black;
      }
    }

十、其他特性

1. 滤镜(Filters)

  • 作用:对元素应用视觉效果(模糊、亮度等)。

  • 示例

    css 复制代码
    img {
      filter: grayscale(100%); /* 灰度 */
      filter: blur(5px); /* 模糊 */
      filter: brightness(0.5); /* 亮度降低 */
    }
    
    .hover-effect:hover {
      filter: contrast(150%); /* 增加对比度 */
    }

2. 计算(calc ())

  • 作用:动态计算 CSS 值。

  • 示例

    css 复制代码
    .sidebar {
      width: calc(25% - 20px); /* 宽度为父容器的25%减去20px */
    }
    
    .full-height {
      height: calc(100vh - 80px); /* 高度为视口高度减去80px */
    }

3. 多背景

  • 作用:为元素应用多层背景。

  • 示例

    css 复制代码
    .element {
      background: 
        url(top.png) top no-repeat,
        url(bottom.png) bottom no-repeat,
        linear-gradient(to bottom, #f0f0f0, #ccc);
    }

浏览器兼容性

大多数现代浏览器已全面支持上述特性,但部分旧版浏览器(如 IE)可能需要添加前缀或降级方案。建议使用工具如 Autoprefixer 自动处理前缀问题。

这些新特性让 CSS 从单纯的样式描述语言转变为强大的布局和动画工具,极大提升了前端开发的效率和网页的用户体验。

八、单行、多行内容的隐藏

一、单行文本溢出隐藏

核心属性
white-space: nowrap + overflow: hidden + text-overflow: ellipsis

示例代码

css 复制代码
.single-line {
  white-space: nowrap;    /* 强制不换行 */
  overflow: hidden;       /* 溢出内容隐藏 */
  text-overflow: ellipsis; /* 显示省略号 */
  width: 200px;           /* 必须设置宽度 */
}

关键点

  1. 容器需有固定宽度 (如 widthmax-width
  2. 仅支持单行,无法处理多行文本
  3. 兼容性好(IE6+ 支持)

下面给大家一个小demo方便大家理解

xml 复制代码
<!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>
        .container {
            max-width: 600px;
            margin: 20px auto;
            padding: 20px;
            font-family: Arial, sans-serif;
            background-color: #f9f9f9;
            border-radius: 8px;
        }
        
        .demo-box {
            margin: 20px 0;
        }
        
        .single-line {
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            width: 200px;
            border: 1px solid #ccc;
            padding: 10px;
            margin: 10px 0;
            background-color: white;
        }
        
        .controls {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-top: 15px;
        }
        
        input {
            padding: 8px;
            width: 80px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        
        .code-block {
            background-color: #333;
            color: white;
            padding: 15px;
            border-radius: 4px;
            font-family: monospace;
            overflow-x: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>单行文本溢出隐藏示例</h2>
        
        <div class="demo-box">
            <div class="single-line" id="demoText">
                这是一段很长的文本内容,当宽度不足以显示全部内容时,会自动显示省略号...
            </div>
            
            <div class="controls">
                <label for="widthInput">容器宽度 (px):</label>
                <input type="number" id="widthInput" value="200" min="50" max="500">
                <button onclick="updateWidth()">应用</button>
            </div>
        </div>
        
        <div class="code-block">
            <pre>
.single-line {
  white-space: nowrap;    /* 强制不换行 */
  overflow: hidden;       /* 溢出内容隐藏 */
  text-overflow: ellipsis; /* 显示省略号 */
  width: 200px;           /* 必须设置宽度 */
}</pre>
        </div>
    </div>
    
    <script>
        function updateWidth() {
            const width = document.getElementById('widthInput').value;
            document.getElementById('demoText').style.width = `${width}px`;
        }
    </script>
</body>
</html>
    

二、多行文本溢出隐藏

方法 1:使用 -webkit-line-clamp(推荐现代浏览器)

核心属性
display: -webkit-box + -webkit-line-clamp: 2 + overflow: hidden

示例代码

css 复制代码
.multi-line {
  display: -webkit-box;      /* 必须 */
  -webkit-box-orient: vertical; /* 必须 */
  -webkit-line-clamp: 2;     /* 显示的行数 */
  overflow: hidden;          /* 溢出隐藏 */
  text-overflow: ellipsis;   /* 省略号(可选) */
  width: 200px;              /* 容器宽度 */
}

关键点

  1. 仅支持 webkit 内核浏览器(Chrome、Safari 等)
  2. 简单高效,推荐用于移动端
  3. 行数固定,无法根据内容动态调整

下面给大家一个小demo方便大家理解

方法 2:使用绝对定位 + 渐变遮罩(兼容所有浏览器)

原理

通过绝对定位覆盖一个带渐变的遮罩层,模拟省略号效果。

示例代码

css 复制代码
.multi-line-fallback {
  position: relative;
  line-height: 1.5em;
  max-height: 3em;         /* 显示的最大高度(行数×行高) */
  overflow: hidden;
  width: 200px;
}

.multi-line-fallback::after {
  content: "...";
  position: absolute;
  bottom: 0;
  right: 0;
  padding-left: 40px;      /* 渐变区域宽度 */
  background: linear-gradient(to right, transparent, white 70%);
}

关键点

  1. 兼容性好(所有浏览器支持)
  2. 效果近似,但省略号位置可能不精确
  3. 需要手动计算高度(行数 × 行高)

下面给大家一个小demo方便大家理解

xml 复制代码
<!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>
        .container {
            max-width: 600px;
            margin: 20px auto;
            padding: 20px;
            font-family: Arial, sans-serif;
            background-color: #f9f9f9;
            border-radius: 8px;
        }
        
        .demo-box {
            margin: 20px 0;
        }
        
        .multi-line-mask {
            position: relative;
            line-height: 1.5em;
            max-height: 4.5em; /* 默认3行 */
            overflow: hidden;
            width: 200px;
            border: 1px solid #ccc;
            padding: 10px;
            margin: 10px 0;
            background-color: white;
        }
        
        .multi-line-mask::after {
            content: "";
            position: absolute;
            bottom: 0;
            right: 0;
            width: 60px;
            height: 1.5em;
            background: linear-gradient(to right, transparent, white 80%);
        }
        
        .controls {
            display: flex;
            align-items: center;
            gap: 10px;
            margin-top: 15px;
        }
        
        input {
            padding: 8px;
            width: 50px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        
        .code-block {
            background-color: #333;
            color: white;
            padding: 15px;
            border-radius: 4px;
            font-family: monospace;
            overflow-x: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>多行文本溢出 - 渐变遮罩法</h2>
        
![GIF.gif](https://p3-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/6f6a70af6f43432fa61c0421f5dc9ce3~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg5aSp5aSp5omt56CB:q75.awebp?rk3s=f64ab15b&x-expires=1752841093&x-signature=6I%2BR74wk99R2Iolmqf2OZzne9qc%3D)
        <div class="demo-box">
            <div class="multi-line-mask" id="demoText">
                这是一段多行文本内容,当超过容器高度时会通过渐变遮罩隐藏多余内容。这种方法兼容性好,但省略号位置可能不够精确。这是一段多行文本内容,当超过容器高度时会通过渐变遮罩隐藏多余内容。
            </div>
            
            <div class="controls">
                <label for="linesInput">行数:</label>
                <input type="number" id="linesInput" value="3" min="1" max="10">
                <button onclick="updateLines()">应用</button>
            </div>
        </div>
        
        <div class="code-block">
            <pre>
.multi-line-mask {
  position: relative;
  line-height: 1.5em;
  max-height: 4.5em; /* 3行 */
  overflow: hidden;
}

.multi-line-mask::after {
  content: "";
  position: absolute;
  bottom: 0;
  right: 0;
  width: 60px;
  height: 1.5em;
  background: linear-gradient(to right, transparent, white 80%);
}</pre>
        </div>
    </div>
    
    <script>
        function updateLines() {
            const lines = document.getElementById('linesInput').value;
            document.getElementById('demoText').style.maxHeight = `${lines * 1.5}em`;
        }
    </script>
</body>
</html>
    

方法 3:JavaScript 动态处理(最灵活)

原理

通过 JS 计算文本高度,超出时截断并添加省略号。

示例代码

ini 复制代码
function truncateText(element, maxLines) {
  const lineHeight = parseInt(getComputedStyle(element).lineHeight);
  const maxHeight = lineHeight * maxLines;
  
  if (element.scrollHeight > maxHeight) {
    element.style.overflow = 'hidden';
    element.style.height = maxHeight + 'px';
    
    // 添加省略号
    const text = element.textContent;
    while (element.scrollHeight > maxHeight) {
      element.textContent = text.slice(0, -1);
    }
    element.textContent += '...';
  }
}

// 使用方法
document.querySelectorAll('.truncate').forEach(el => {
  truncateText(el, 3); // 限制3行
});

关键点

  1. 完全自定义,可动态调整行数
  2. 性能开销较大,需遍历所有元素
  3. 适用于复杂场景(如响应式布局)
ini 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多行文本溢出 - JavaScript动态处理</title>
    <style>
        .container {
            max-width: 600px;
            margin: 20px auto;
            padding: 20px;
            font-family: Arial, sans-serif;
            background-color: #f9f9f9;
            border-radius: 8px;
        }
        
        .demo-box {
            margin: 20px 0;
        }
        
        .multi-line-js {
            line-height: 1.5em;
            border: 1px solid #ccc;
            padding: 10px;
            margin: 10px 0;
            background-color: white;
        }
        
        .controls {
            display: grid;
            grid-template-columns: auto 1fr;
            gap: 10px;
            margin-top: 15px;
        }
        
        input {
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        
        .code-block {
            background-color: #333;
            color: white;
            padding: 15px;
            border-radius: 4px;
            font-family: monospace;
            overflow-x: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>多行文本溢出 - JavaScript动态处理</h2>
        
        <div class="demo-box">
            <div class="multi-line-js" id="demoText">
                这是一段多行文本内容,使用JavaScript可以根据容器高度和行数限制动态截断文本并添加省略号。这种方法灵活性最高,适用于各种复杂场景,但需要注意性能开销。这是一段多行文本内容,使用JavaScript可以根据容器高度和行数限制动态截断文本并添加省略号。这种方法灵活性最高,适用于各种复杂场景,但需要注意性能开销。
            </div>
            
            <div class="controls">
                <label for="linesInput">行数限制:</label>
                <input type="number" id="linesInput" value="3" min="1" max="10">
                
                <label for="widthInput">容器宽度 (px):</label>
                <input type="number" id="widthInput" value="300" min="100" max="500">
                
                <label for="customEllipsis">自定义省略号:</label>
                <input type="text" id="customEllipsis" value="...">
            </div>
            
            <button onclick="applyTruncation()">应用设置</button>
        </div>
        
        <div class="code-block">
            <pre>
function truncateText(element, maxLines, ellipsis = '...') {
  const lineHeight = parseInt(getComputedStyle(element).lineHeight);
  const maxHeight = lineHeight * maxLines;
  
  // 保存原始内容
  if (!element.dataset.originalText) {
    element.dataset.originalText = element.textContent;
  }
  
  // 恢复原始内容再处理
  element.textContent = element.dataset.originalText;
  
  if (element.scrollHeight > maxHeight) {
    element.style.overflow = 'hidden';
    element.style.height = maxHeight + 'px';
    
    // 截断文本
    let text = element.textContent;
    while (element.scrollHeight > maxHeight && text.length > 0) {
      text = text.slice(0, -1);
      element.textContent = text + ellipsis;
    }
  } else {
    // 内容未超出,恢复完整显示
    element.style.height = 'auto';
    element.textContent = element.dataset.originalText;
  }
}</pre>
        </div>
    </div>
    
    <script>
        function truncateText(element, maxLines, ellipsis = '...') {
            const lineHeight = parseInt(getComputedStyle(element).lineHeight);
            const maxHeight = lineHeight * maxLines;
            
            // 保存原始内容
            if (!element.dataset.originalText) {
                element.dataset.originalText = element.textContent;
            }
            
            // 恢复原始内容再处理
            element.textContent = element.dataset.originalText;
            
            if (element.scrollHeight > maxHeight) {
                element.style.overflow = 'hidden';
                element.style.height = maxHeight + 'px';
                
                // 截断文本
                let text = element.textContent;
                while (element.scrollHeight > maxHeight && text.length > 0) {
                    text = text.slice(0, -1);
                    element.textContent = text + ellipsis;
                }
            } else {
                // 内容未超出,恢复完整显示
                element.style.height = 'auto';
                element.textContent = element.dataset.originalText;
            }
        }
        
        function applyTruncation() {
            const element = document.getElementById('demoText');
            const lines = parseInt(document.getElementById('linesInput').value);
            const width = document.getElementById('widthInput').value;
            const ellipsis = document.getElementById('customEllipsis').value;
            
            element.style.width = `${width}px`;
            truncateText(element, lines, ellipsis);
        }
        
        // 初始化
        applyTruncation();
    </script>
</body>
</html>
    

三、面试回答总结

单行文本溢出

" 对于单行文本溢出,我会使用 white-space: nowrap 防止换行,配合 overflow: hiddentext-overflow: ellipsis 显示省略号。这种方法简单直接,兼容性好,但需要确保容器有固定宽度。"

多行文本溢出

" 对于多行文本溢出,有几种解决方案:

  1. 优先使用 -webkit-line-clamp,它简单高效,但仅支持 webkit 内核浏览器。
  2. 对于兼容性要求高的场景,我会使用绝对定位 + 渐变遮罩的方式,通过 CSS 模拟省略号效果。
  3. 如果需要更灵活的控制(如动态调整行数),我会结合 JavaScript 计算文本高度并截断。"

补充说明

" 在实际项目中,我会根据业务需求选择方案。例如移动端可以优先使用 -webkit-line-clamp,而 PC 端则考虑兼容性更好的方案。同时,我也会考虑性能因素,避免在大型列表中使用 JS 动态处理。"

九、手写两栏布局的实现

两栏布局是一种网页设计模式,将页面横向划分为两个主要区域,通常左侧为固定宽度(如导航、侧边栏),右侧为自适应宽度(如主内容区),通过浮动、Flexbox、Grid 等技术实现。

正如下面的效果

一、浮动(Float)布局

考虑到对Float布局不是很熟悉的各位,给各位推荐一个b站的视频,五分钟即可入门浮动 b23.tv/ucCTDAi

核心原理 :通过 float 属性使一侧元素浮动,另一侧自适应宽度。

这里注意,要给浮动元素预留空间,不要让主内容区的内容被浮动内容覆盖

示例代码

xml 复制代码
<style>
  .container {
    overflow: auto; /* 清除浮动 */
  }
  .sidebar {
    float: left;
    width: 30%;
  }
  .main {
    margin-left: 30%; /* 为浮动元素留出空间 */
  }
</style>
<div class="container">
  <div class="sidebar">侧边栏</div>
  <div class="main">主内容区</div>
</div>

仔细观察上述的代码,可以发现,浮动元素的宽度为(width: 30%;),而主内容区给浮动元素留的宽度也是(margin-left: 30%;),这也是前文提到的'要给浮动元素预留空间,不要让主内容区的内容被浮动内容覆盖'

优点 :兼容性好(IE6+)。
缺点 :需要清除浮动,容易出现高度塌陷问题。
适用场景:需要兼容旧浏览器的项目。

二、Flexbox 布局

核心原理 :使用 display: flex 实现弹性布局。

flex布局在这里的运用,主要是'弹性的'占据剩余区域,即flex: 1; 这样就可以实现两栏布局

示例代码

xml 复制代码
<style>
  .container {
    display: flex;
  }
  .sidebar {
    width: 30%;
  }
  .main {
    flex: 1; /* 占据剩余空间 */
  }
</style>
<div class="container">
  <div class="sidebar">侧边栏</div>
  <div class="main">主内容区</div>
</div>

优点

  • 代码简洁,无需清除浮动
  • 支持响应式调整
  • 垂直居中简单(align-items: center

缺点 :IE10+ 支持,旧浏览器不兼容。
适用场景:现代 Web 应用、移动端。

三、Grid 布局

核心原理 :使用 display: grid 创建二维网格。

示例代码

xml 复制代码
<style>
  .container {
    display: grid;
    grid-template-columns: 30% 1fr; /* 两列布局 */
  }
</style>
<div class="container">
  <div class="sidebar">侧边栏</div>
  <div class="main">主内容区</div>
</div>

优点

  • 语法最简洁

  • 支持高级布局(如间距、自动填充)

  • 响应式调整更灵活(grid-template-columns

缺点 :IE 不支持,Chrome/Firefox/Safari 完全支持。
适用场景:现代 Web 应用、管理后台。

四、绝对定位(Absolute)布局

核心原理 :通过 position: absolute 固定一侧宽度,另一侧自适应。

示例代码

xml 复制代码
<style>
  .container {
    position: relative;
    height: 100vh; /* 需要指定高度 */
  }
  .sidebar {
    position: absolute;
    left: 0;
    width: 30%;
    height: 100%;
  }
  .main {
    margin-left: 30%;
    height: 100%;
  }
</style>
<div class="container">
  <div class="sidebar">侧边栏</div>
  <div class="main">主内容区</div>
</div>

优点 :布局稳定,不受内容影响。
缺点

  • 脱离文档流,可能影响其他元素
  • 需要明确高度(如 height: 100%

适用场景:固定侧边栏的页面(如邮件客户端)。

五、表格布局(Table)

核心原理 :使用 display: tabletable-cell 模拟表格。

示例代码

xml 复制代码
<style>
  .container {
    display: table;
    width: 100%;
  }
  .sidebar, .main {
    display: table-cell;
  }
  .sidebar {
    width: 30%;
  }
</style>
<div class="container">
  <div class="sidebar">侧边栏</div>
  <div class="main">主内容区</div>
</div>

优点

  • 单元格高度自动匹配
  • 兼容性好(IE8+)

缺点

  • 语义不明确
  • 灵活性差,难以实现复杂布局

适用场景:简单的两栏布局,需要等高效果。

六、响应式实现

★★★ 这里是和其他面试者拉开差距的关键

方法:结合媒体查询(Media Query)实现不同屏幕下的布局变化。

示例代码

css 复制代码
.container {
  display: flex;
  flex-direction: column; /* 移动端垂直排列 */
}

@media (min-width: 768px) {
  .container {
    flex-direction: row; /* 桌面端水平排列 */
  }
  .sidebar {
    width: 30%;
  }
  .main {
    flex: 1;
  }
}

优点

  • 适配各种设备屏幕
  • 移动端可优化为单列布局

适用场景:需要响应式设计的项目。

前三个实现方法是关键,是一定要记住的

面试回答总结

" 两栏布局的实现有多种方式,我会根据项目需求和兼容性要求选择合适的方案:

  1. 浮动布局适合需要兼容旧浏览器的场景,但需要处理清除浮动的问题。
  2. Flexbox现代项目的首选,代码简洁且支持灵活的对齐和响应式调整。
  3. Grid最强大的布局方式,特别适合复杂的二维布局,但兼容性稍差。
  4. 绝对定位:适合固定侧边栏的场景,但会脱离文档流。
  5. 表格布局:兼容性好,但语义不明确,灵活性较低。

在实际项目中,我会优先使用 Flexbox 或 Grid,并结合媒体查询实现响应式设计。例如,在移动端将两栏布局转为单列,提升用户体验。"

十、手写三栏布局的实现

三栏布局是前端开发里较为常见的页面结构,它主要包含左、中、右三个部分。

正如下面的实现

三栏布局稍微比两栏布局复杂一点点,如果看不懂,我推荐一个B站视频 b23.tv/9mLcRjd

浮动实现

左右两栏分别向左/右浮动脱离文档流,中间栏通过外边距(margin)避开浮动元素,形成三栏布局。

xml 复制代码
<!DOCTYPE html>
<html>
<head>
    <style>
        body {
            margin: 0;
            padding: 0;
        }
        
        .container {
            width: 100%;
        }
        
        .left {
            float: left;
            width: 200px;
            background-color: #f2f2f2;
            height: 300px;
        }
        
        .right {
            float: right;
            width: 200px;
            background-color: #f2f2f2;
            height: 300px;
        }
        
        .main {
            margin-left: 210px;  /* 大于左侧栏宽度 */
            margin-right: 210px; /* 大于右侧栏宽度 */
            background-color: #ccc;
            height: 300px;
        }
        
        .clearfix::after {
            content: "";
            display: table;
            clear: both;
        }
    </style>
</head>
<body>
    <div class="container clearfix">
        <div class="left">左侧栏</div>
        <div class="right">右侧栏</div>
        <div class="main">主要内容</div>
    </div>
</body>
</html>

Flexbox 实现

此方法利用 Flexbox 的弹性布局能力,能很方便地对列宽和对齐方式进行控制。

xml 复制代码
<style>
    .container {
        display: flex;
    }
    .left, .right {
        width: 200px;
        background: #f0f0f0;
    }
    .center {
        flex: 1;
        background: #e0e0e0;
    }
</style>
<div class="container">
    <div class="left">左列</div>
    <div class="center">中间列</div>
    <div class="right">右列</div>
</div>

Grid 实现

Grid 布局是专门为二维布局设计的,能够简洁地实现复杂的网格结构。

xml 复制代码
<style>
    .container {
        display: grid;
        grid-template-columns: 200px 1fr 200px;
    }
    .left, .right {
        background: #f0f0f0;
    }
    .center {
        background: #e0e0e0;
    }
</style>
<div class="container">
    <div class="left">左列</div>
    <div class="center">中间列</div>
    <div class="right">右列</div>
</div>

表格布局实现

该方法把容器当作表格,各列当作表格单元格来进行布局。

xml 复制代码
<style>
    .container {
        display: table;
        width: 100%;
    }
    .left, .right, .center {
        display: table-cell;
    }
    .left, .right {
        width: 200px;
        background: #f0f0f0;
    }
    .center {
        background: #e0e0e0;
    }
</style>
<div class="container">
    <div class="left">左列</div>
    <div class="center">中间列</div>
    <div class="right">右列</div>
</div>

绝对定位实现

这种方法通过绝对定位来固定左右两列的位置,中间列则利用边距来留出空间。

xml 复制代码
<style>
    .container {
        position: relative;
        height: 200px;
    }
    .left, .right {
        position: absolute;
        top: 0;
        width: 200px;
        background: #f0f0f0;
    }
    .left {
        left: 0;
    }
    .right {
        right: 0;
    }
    .center {
        margin: 0 200px;
        background: #e0e0e0;
        height: 100%;
    }
</style>
<div class="container">
    <div class="left">左列</div>
    <div class="center">中间列</div>
    <div class="right">右列</div>
</div>

下面的圣杯布局实现和双飞翼布局实现以及被现代技术淘汰,但是对于面试来说,还是有学习的必要的

圣杯布局实现

圣杯布局的特点是中间列优先加载,并且左右两列宽度固定。

通过 浮动 + 负边距(margin-left: -100%) + 相对定位(position: relative)让左右栏"挤"到中间栏两侧,并用父容器的 padding 预留空间

xml 复制代码
<style>
    .container {
        padding: 0 200px;
    }
    .columns {
        display: flex;
    }
    .center {
        flex: 1;
        order: 2;
        background: #e0e0e0;
    }
    .left {
        width: 200px;
        order: 1;
        margin-left: -100%;
        position: relative;
        right: 200px;
        background: #f0f0f0;
    }
    .right {
        width: 200px;
        order: 3;
        margin-right: -200px;
        background: #f0f0f0;
    }
</style>
<div class="container">
    <div class="columns">
        <div class="center">中间列</div>
        <div class="left">左列</div>
        <div class="right">右列</div>
    </div>
</div>

双飞翼布局实现

中间栏嵌套一层 div,用其 margin 预留左右空间,左右栏仅靠浮动 + 负边距(margin-left)定位,无需相对定位,简化实现。

xml 复制代码
<style>
    .container {
        overflow: hidden;
    }
    .center {
        float: left;
        width: 100%;
        background: #e0e0e0;
    }
    .center-inner {
        margin: 0 200px;
    }
    .left, .right {
        float: left;
        width: 200px;
        background: #f0f0f0;
    }
    .left {
        margin-left: -100%;
    }
    .right {
        margin-left: -200px;
    }
</style>
<div class="container">
    <div class="center">
        <div class="center-inner">中间列</div>
    </div>
    <div class="left">左列</div>
    <div class="right">右列</div>
</div>

响应式三栏布局实现

这种布局会依据屏幕尺寸自动调整为适合移动端的单列布局。

xml 复制代码
<style>
    .container {
        display: flex;
        flex-wrap: wrap;
    }
    .left, .right, .center {
        width: 100%;
    }
    @media (min-width: 768px) {
        .left, .right {
            width: 200px;
        }
        .center {
            flex: 1;
        }
    }
</style>
<div class="container">
    <div class="left">左列</div>
    <div class="center">中间列</div>
    <div class="right">右列</div>
</div>

在实际的项目开发中,建议优先考虑使用 Flexbox 或者 Grid 布局,因为它们的代码更简洁,也更容易维护。要是需要兼容旧版本的浏览器,浮动布局或者表格布局也是不错的选择。而圣杯布局和双飞翼布局则适用于需要中间列优先加载的特殊场景。

十一、水平垂直居中的实现方法

1. 行内元素 / 文本居中(单行)

通过将容器的 line-height 值设置为与容器高度相等,使单行文本在垂直方向上自动居中。同时使用 text-align: center 实现水平居中。

xml 复制代码
<style>
    .center {
        height: 100px;
        line-height: 100px;     /* 高度等于行高 */
        text-align: center;      /* 水平居中 */
        border: 1px solid #ccc;
    }
</style>
<div class="center">单行文本居中</div>

2. 行内元素 / 文本居中(多行)

将容器设置为 display: table-cell,模拟表格单元格的行为。利用 vertical-align: middle 实现垂直居中,配合 text-align: center 实现水平居中。

xml 复制代码
<style>
    .center {
        display: table-cell;
        width: 200px;
        height: 200px;
        text-align: center;      /* 水平居中 */
        vertical-align: middle;  /* 垂直居中 */
        border: 1px solid #ccc;
    }
</style>
<div class="center">多行文本居中示例<br>第二行文本</div>

3. 块级元素居中(已知宽高)

父元素设置 position: relative 作为定位参考。子元素使用 position: absolute 配合 top: 50%left: 50% 将左上角定位到父元素中心。通过 margin-topmargin-left 的负值(各为元素宽高的一半)将元素向上和向左偏移,实现完全居中。

xml 复制代码
<style>
    .container {
        position: relative;
        height: 300px;
        border: 1px solid #ccc;
    }
    .center {
        position: absolute;
        top: 50%;                /* 顶部偏移50% */
        left: 50%;               /* 左侧偏移50% */
        width: 200px;
        height: 100px;
        margin-top: -50px;       /* 高度的负一半 */
        margin-left: -100px;     /* 宽度的负一半 */
        background: #f0f0f0;
    }
</style>
<div class="container">
    <div class="center">块级元素居中</div>
</div>

★★★ 4. 块级元素居中(未知宽高)

父元素设置 position: relative。子元素使用 position: absolutetop: 50%, left: 50% 定位到父元素中心。通过 transform: translate(-50%, -50%) 动态将元素自身向上和向左偏移其宽度和高度的 50%,无需知道具体尺寸。

xml 复制代码
<style>
    .container {
        position: relative;
        height: 300px;
        border: 1px solid #ccc;
    }
    .center {
        position: absolute;
        top: 50%;                /* 顶部偏移50% */
        left: 50%;               /* 左侧偏移50% */
        transform: translate(-50%, -50%); /* 利用transform调整 */
        background: #f0f0f0;
    }
</style>
<div class="container">
    <div class="center">未知宽高元素居中</div>
</div>

5. Flexbox 居中(现代方案)

父元素设置 display: flexdisplay: inline-flex。使用 justify-content: center 实现水平居中,align-items: center 实现垂直居中。
优势:代码简洁,支持响应式布局,是现代前端开发的首选方案。

xml 复制代码
<style>
    .container {
        display: flex;
        justify-content: center; /* 水平居中 */
        align-items: center;     /* 垂直居中 */
        height: 300px;
        border: 1px solid #ccc;
    }
    .center {
        background: #f0f0f0;
        padding: 20px;
    }
</style>
<div class="container">
    <div class="center">Flexbox居中</div>
</div>

6. Grid 居中(现代方案)

父元素设置 display: griddisplay: inline-grid。使用 place-items: center 同时实现水平和垂直居中(等同于 justify-items: centeralign-items: center 的组合)。

xml 复制代码
<style>
    .container {
        display: grid;
        place-items: center;     /* 水平和垂直同时居中 */
        height: 300px;
        border: 1px solid #ccc;
    }
    .center {
        background: #f0f0f0;
        padding: 20px;
    }
</style>
<div class="container">
    <div class="center">Grid居中</div>
</div>

7. 绝对定位 + 弹性布局混合方案

父元素设置 position: relative。子元素使用 position: absoluteinset: 0(等同于 top: 0; right: 0; bottom: 0; left: 0)将元素扩展至父元素边界。通过 margin: auto 使浏览器自动计算并分配上下左右的边距,实现居中。

限制:需明确设置子元素的宽高,否则会填满父容器。

xml 复制代码
<style>
    .container {
        position: relative;
        height: 300px;
        border: 1px solid #ccc;
    }
    .center {
        position: absolute;
        inset: 0;                /* 等同于top:0;right:0;bottom:0;left:0; */
        margin: auto;            /* 自动计算边距 */
        width: 200px;
        height: 100px;
        background: #f0f0f0;
    }
</style>
<div class="container">
    <div class="center">绝对定位+弹性布局居中</div>
</div>

十二、谈一谈你对Flex的理解

这里给大家贴一篇我之前写过的文章掌握Flex布局:面向小白的Flex全面教程 - 掘金

Flexbox(Flexible Box Layout)是 CSS3 引入的一维布局模型,旨在为容器内的子元素提供弹性的空间分配和对齐方式。它的核心思想是让容器能够自动调整子元素的宽度、高度和排列顺序,以适应不同的屏幕尺寸和设备类型。

一、核心概念

  1. Flex 容器(Flex Container)

    应用 display: flexdisplay: inline-flex 的父元素,它定义了一个 Flex 布局的作用域。

  2. Flex 项目(Flex Items)

    容器内的直接子元素,它们会被 Flex 布局所控制。

  3. 主轴(Main Axis)和交叉轴(Cross Axis)

    • 主轴:由 flex-direction 定义的方向,默认为水平从左到右。
    • 交叉轴:垂直于主轴的方向,默认是垂直方向。

二、容器属性(控制整体布局)

1. flex-direction:定义主轴方向

sql 复制代码
.container {
  flex-direction: row | row-reverse | column | column-reverse;
}
  • row(默认):水平从左到右。
  • row-reverse:水平从右到左。
  • column:垂直从上到下。
  • column-reverse:垂直从下到上。

2. flex-wrap:控制换行

lua 复制代码
.container {
  flex-wrap: nowrap | wrap | wrap-reverse;
}
  • nowrap(默认):不换行,元素可能溢出。
  • wrap:换行,新行位于下方。
  • wrap-reverse:换行,新行位于上方。

3. flex-flowflex-directionflex-wrap 的简写

css 复制代码
.container {
  flex-flow: row wrap; /* 常用组合 */
}

4. justify-content:主轴对齐方式

sql 复制代码
.container {
  justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;
}
  • flex-start(默认):元素靠主轴起点。
  • flex-end:元素靠主轴终点。
  • center:元素居中。
  • space-between:两端对齐,间距平均分配。
  • space-around:每个元素两侧间距相等(边缘间距为中间间距的一半)。
  • space-evenly:所有间距完全相等。

5. align-items:交叉轴对齐方式

css 复制代码
.container {
  align-items: stretch | flex-start | flex-end | center | baseline;
}
  • stretch(默认):元素拉伸填充容器高度 / 宽度。
  • flex-start:靠交叉轴起点。
  • flex-end:靠交叉轴终点。
  • center:居中。
  • baseline:元素基线对齐。

6. align-content:多行对齐方式(当存在换行时生效)

sql 复制代码
.container {
  align-content: stretch | flex-start | flex-end | center | space-between | space-around;
}

三、项目属性(控制单个元素)

1. order:定义元素排列顺序

css 复制代码
.item {
  order: 0; /* 默认值,数值越小越靠前 */
}

2. flex-grow:定义元素的扩展比例

css 复制代码
.item {
  flex-grow: 1; /* 默认0,不扩展;值为1时平均分配剩余空间 */
}

3. flex-shrink:定义元素的收缩比例

css 复制代码
.item {
  flex-shrink: 1; /* 默认1,空间不足时等比例收缩;设为0则不收缩 */
}

4. flex-basis:定义元素在分配空间前的初始大小

css 复制代码
.item {
  flex-basis: auto | 200px; /* 默认auto,使用元素自身宽度/高度 */
}

5. flexflex-grow, flex-shrink, flex-basis 的简写

css 复制代码
.item {
  flex: 1 1 auto; /* 默认值 */
  flex: 1; /* 等同于 flex: 1 1 0 */
}

6. align-self:单独定义元素的交叉轴对齐方式

arduino 复制代码
.item {
  align-self: auto | flex-start | flex-end | center | stretch;
}

四、你什么时候会使用Flex布局?

  1. 导航栏

    • 水平排列菜单项,支持响应式折叠。
    • 使用 justify-content: space-between 实现左右对齐。
  2. 卡片布局

    • 等高卡片自动适应容器宽度。
    • 使用 flex-wrap: wrapjustify-content: center 实现卡片自动换行和居中。
  3. 垂直居中

    • 使用 align-items: centerjustify-content: center 快速实现元素的水平垂直居中。
  4. 自适应侧边栏

    • 主内容区域自动扩展,侧边栏固定宽度。
    • 使用 flex: 1 让主内容占满剩余空间。
  5. 响应式表单

    • 标签和输入框在小屏幕上垂直排列,大屏幕上水平排列。
    • 使用 flex-direction: column 和媒体查询实现。
  6. 底部固定页脚

    • 当内容不足时,页脚固定在底部;内容充足时,页脚随内容滚动。
    • 使用 min-height: 100vhflex-direction: column 实现。

五、Flex的优缺点对比

优点

  • 简洁灵活:大幅减少浮动和定位的使用,代码更简洁。
  • 响应式友好:天然支持根据容器尺寸动态调整元素。
  • 对齐能力强:轻松实现水平和垂直居中、等间距分布。
  • 顺序灵活 :通过 order 属性可随意改变元素显示顺序。

缺点

  • 兼容性问题:IE10 及以下不支持,需提供降级方案。
  • 二维布局能力有限:对于复杂的网格布局,Grid 更合适。

在向面试官介绍时,一定要介绍flex布局出现的原因,flex的关键参数,flex何时使用以及优缺点

十三、你是否理解BFC,在实际项目中如何运用(不是KFC)

BFC(Block Formatting Context,块级格式化上下文) 是CSS中的一个重要概念,它是页面上的一个独立的渲染区域,规定了内部的块级盒子如何布局,并且与外部区域互不影响。BFC可以看作是一个隔离的容器,容器内的元素布局不会影响到外部的元素。

BFC的特性(可以想象html的特性,因为html也是一个BFC):

  1. 内部盒子垂直排列:BFC内的块级盒子会按照垂直方向一个接一个地放置。
  2. 外边距折叠(Margin Collapse) :属于同一个BFC的两个相邻块级盒子的上下外边距会发生重叠。
  3. 独立布局:BFC的区域不会与浮动元素重叠,且可以包含浮动元素。
  4. 隔离性:BFC内的元素不会影响外部的元素,反之亦然。

如何创建BFC

可以通过以下CSS属性或条件触发BFC的创建:

  1. 根元素(<html> :整个页面默认是一个BFC。
  2. 浮动元素 :元素的 float 值不为 none(如 float: leftfloat: right)。
  3. 绝对定位元素 :元素的 positionabsolutefixed
  4. display: inline-block :设置为 inline-block 的元素。
  5. display: table-celltable-caption:表格单元格或表格标题。
  6. overflow 不为 visible :如 overflow: hiddenautoscroll
  7. display: flow-root:专门用于创建BFC的属性(现代浏览器支持,无副作用)。
  8. Flex或Grid的直接子项display: flexdisplay: grid 的容器的直接子元素。

常见应用场景

  1. 清除浮动:父元素创建BFC后可以包含浮动子元素(避免高度塌陷)。

    css 复制代码
    .parent {
        overflow: hidden; /* 触发BFC */
    }
  2. 避免外边距折叠:将相邻元素放入不同的BFC中。

    xml 复制代码
    <div class="bfc">
        <p>第一个段落</p>
    </div>
    <div class="bfc">
        <p>第二个段落</p>
    </div>
    css 复制代码
    .bfc {
        overflow: hidden; /* 创建BFC */
    }
  3. 阻止元素被浮动元素覆盖:非浮动元素通过BFC与浮动元素分栏。

    css 复制代码
    .content {
        overflow: hidden; /* 创建BFC,避免与浮动元素重叠 */
    }

在你对实际运用中,一般是如何创建BFC的?

  • 使用 display: flow-root 创建BFC,因为它无副作用(不会隐藏内容或产生滚动条)。

    css 复制代码
    .container {
        display: flow-root;
    }

十四、清除浮动的原因?如何清除?

清除浮动是 CSS 布局中的重要概念,主要用于解决浮动元素导致的父元素高度塌陷问题。

一、为什么需要清除浮动?

当子元素设置 float: left/right 后,会脱离正常的文档流,导致父元素无法计算其高度,出现高度塌陷(父元素高度为 0)。这会影响布局的正常显示,例如:

  • 父元素无法包裹浮动的子元素
  • 后续元素可能会与浮动元素重叠
  • 背景和边框无法正确显示

示例问题代码

xml 复制代码
<style>
  .parent {
    border: 1px solid red;
  }
  .child {
    float: left;
    width: 100px;
    height: 100px;
    background: #f0f0f0;
  }
</style>
<div class="parent">
  <div class="child"></div>
</div>
<!-- 父元素高度为0,边框无法包裹浮动子元素 -->

这里父元素并不能包含子元素,因为子元素浮动,而且父元素本身的大小比子元素小(或者没有大小)导致父元素高度塌陷(原本应该由子元素的大小撑开)

有的读者可能会思考------我将父元素的大小设置的比子元素大不就行了吗?

但是在实际的项目中子元素的大小很多情况下是不能确定的,所有清除浮动就很有必要

二、清除浮动的常见方式

1. 使用 clear 属性(传统方法)

在浮动元素后添加一个空元素,并设置 clear: both

xml 复制代码
<style>
  .parent {
    border: 1px solid #ccc;
  }
  .float {
    float: left;
  }
  .clear {
    clear: both;
  }
</style>
<div class="parent">
  <div class="float">浮动元素</div>
  <div class="clear"></div> <!-- 清除浮动 -->
</div>

缺点:需要额外的 HTML 元素,增加冗余代码。

2. BFC(块级格式化上下文)

通过触发父元素的 BFC 来包含浮动元素。常见方式:

css 复制代码
.parent {
  overflow: hidden; /* 触发BFC */
}

原理:BFC 会包含内部所有浮动元素,计算高度时会考虑浮动子元素。

3. 伪元素清除法(推荐)

使用 ::after 伪元素在父元素末尾插入一个清除浮动的元素。

css 复制代码
.parent::after {
  content: "";
  display: block;
  clear: both;
}

4. 浮动父元素本身

将父元素也设置为浮动:

css 复制代码
.parent {
  float: left; /* 触发BFC */
}

缺点:可能影响后续元素布局,需要再次清除浮动。

5. 设置父元素为表格单元格

css 复制代码
.parent {
  display: table-cell;
}

缺点:可能影响宽度和布局特性。

三、各方法对比

方法 优点 缺点 适用场景
空元素 + clear 兼容性好 增加 HTML 冗余 兼容老旧浏览器
overflow:hidden 代码简单 内容溢出会被隐藏 内容不会溢出的容器
display:flow-root 专门为清除浮动设计 IE 不支持 现代浏览器环境
伪元素清除法 无冗余 HTML,兼容性好 需要额外 CSS 类 大多数场景推荐使用
浮动父元素 简单直接 影响后续布局 临时解决方案

四、现代替代方案

在 Flexbox 和 Grid 布局中,浮动的使用场景大幅减少,因为它们会自动包含子元素,无需清除浮动:

css 复制代码
.parent {
  display: flex; /* Flex布局 */
  /* 或 display: grid; */
}

清除浮动的核心目标是让父元素正确包含浮动的子元素,避免高度塌陷。在现代前端开发中,优先使用伪元素清除法或BFC 触发,并尽量用 Flexbox/Grid 替代浮动布局,以减少布局复杂度。

十五、position的属性有哪些,区别是什么

推荐一个position的讲解视频 b23.tv/wwag429

CSS 中的position属性用于控制元素在文档中的定位方式,共有五种主要取值:staticrelativeabsolutefixedsticky

一、position: static(默认值)

  • 特性 :元素按照正常的文档流布局,toprightbottomleftz-index属性无效。

  • 示例

    arduino 复制代码
    .element {
      position: static; /* 默认值,无需显式声明 */
    }
  • 应用场景:大多数普通元素默认使用静态定位。

二、position: relative(相对定位)

  • 特性

    • 元素先按照正常文档流布局,然后相对于其正常位置进行偏移。
    • 偏移不会影响其他元素的布局(其他元素仍视为该元素在原位)。
    • 可通过toprightbottomleft调整位置。
    • 会创建新的层叠上下文(stacking context)。
  • 示例

    css 复制代码
    .element {
      position: relative;
      top: 10px; /* 向下偏移10px */
      left: 20px; /* 向右偏移20px */
    }
  • 应用场景 :作为绝对定位元素的容器(父元素设置relative)。

为了方便大家了解relative,我提供一个实例,大家可以复制运行

各位,这里不能给实例了,超出最大字符限制了,下面是演示实例,大家凑合看吧

三、position: absolute(绝对定位)

  • 特性

    • 元素脱离正常文档流,不再占据空间。
    • 相对于最近的已定位祖先元素 (即position值为relativeabsolutefixedsticky的元素)定位。
    • 若无已定位祖先元素,则相对于初始包含块(通常是浏览器视口)。
    • 可通过toprightbottomleft精确控制位置。
  • 示例

    css 复制代码
    .parent {
      position: relative; /* 作为参考容器 */
    }
    .child {
      position: absolute;
      top: 0;
      right: 0; /* 定位到父元素右上角 */
    }
  • 应用场景:悬浮菜单、弹出框、绝对定位的图标等。

再给大家提供一个实例,可以复制运行

各位,这里不能给实例了,超出最大字符限制了,下面是演示实例,大家凑合看吧

四、position: fixed(固定定位)

  • 特性

    • 元素脱离文档流,相对于浏览器视口固定位置。
    • 滚动页面时,元素位置保持不变。
    • 常用于创建导航栏、返回顶部按钮等。
  • 示例

    css 复制代码
    .navbar {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%; /* 固定在顶部的导航栏 */
    }
  • 应用场景:固定导航、悬浮广告、模态框遮罩层。

五、position: sticky(粘性定位)

  • 特性

    • 混合了relativefixed的特性,初始时按正常文档流布局。
    • 当滚动到特定位置时(通过toprightbottomleft指定阈值),变为相对于视口固定。
    • 必须指定至少一个偏移值(如top: 0)才能生效。
  • 示例

    css 复制代码
    .sidebar {
      position: sticky;
      top: 20px; /* 滚动到距离视口顶部20px时固定 */
    }
  • 应用场景:滚动时固定的侧边栏、标题栏。

六、关键区别对比表

属性值 定位参考对象 是否脱离文档流 滚动时表现
static 正常文档流 随文档滚动
relative 元素自身正常位置 随文档滚动
absolute 最近的已定位祖先元素 随祖先元素滚动(若祖先固定则不动)
fixed 浏览器视口 固定不动
sticky 正常文档流(滚动到阈值后固定) 初始滚动,达到阈值后固定

十六、实现一个三角形

这一题看似是考察我们使用css画图的能力,但是实时却不是这样,这题考察的是css中的border特性

在 CSS 中实现三角形主要基于边框(border)的特性。当元素的宽度和高度为 0 时,其边框会被相邻边框均分,形成一个由边框组成的多边形。通过调整边框的宽度和颜色,可以轻松创建各种三角形。

一、实现原理

边框的本质

每个元素的边框实际上是由四个梯形组成的,当元素宽度和高度为 0 时,梯形退化为三角形。

二、实现代码

1. 向上的三角形

xml 复制代码
<style>
  .triangle-up {
    width: 0;
    height: 0;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-bottom: 100px solid red; /* 底边显示颜色 */
  }
</style>
<div class="triangle-up"></div>

2. 向下的三角形

xml 复制代码
<style>
  .triangle-down {
    width: 0;
    height: 0;
    border-left: 50px solid transparent;
    border-right: 50px solid transparent;
    border-top: 100px solid blue; /* 顶边显示颜色 */
  }
</style>
<div class="triangle-down"></div>

3. 向左的三角形

xml 复制代码
<style>
  .triangle-left {
    width: 0;
    height: 0;
    border-top: 50px solid transparent;
    border-bottom: 50px solid transparent;
    border-right: 100px solid green; /* 右边显示颜色 */
  }
</style>
<div class="triangle-left"></div>

4. 向右的三角形

xml 复制代码
<style>
  .triangle-right {
    width: 0;
    height: 0;
    border-top: 50px solid transparent;
    border-bottom: 50px solid transparent;
    border-left: 100px solid purple; /* 左边显示颜色 */
  }
</style>
<div class="triangle-right"></div>

5. 任意角度的三角形

通过调整各边的宽度比例,可以创建不同角度的三角形:

xml 复制代码
<style>
  .triangle-custom {
    width: 0;
    height: 0;
    border-left: 30px solid transparent;
    border-right: 70px solid transparent;
    border-bottom: 100px solid orange;
  }
</style>
<div class="triangle-custom"></div>

最后给大家说明一下代码中可能困惑的地方

transparent是使不需要的边框透明,只有需要显示的边框我们才设置颜色

十七、如何解决1px 问题?

为何会产生1px问题?何为1px问题?

首先我们要明确三个概念,一个使逻辑像素,一个是物理像素,还有一个设备像素比(DPR)

  1. CSS 像素(逻辑像素)
    前端开发中使用的单位(如1px),是抽象的逻辑单位,用于描述元素的尺寸和位置。
  2. 物理像素
    设备屏幕实际的物理显示单元,例如 iPhone 13 的分辨率为 2532×1170 像素。
  3. 设备像素比(DPR)
    DPR = 物理像素 / CSS像素
  • 普通屏幕:DPR = 1,1 个 CSS 像素对应 1 个物理像素。
  • Retina 屏:DPR = 2 或 3,1 个 CSS 像素对应 4 个(2×2)或 9 个(3×3)物理像素。

由此我们可以得出当 CSS 中设置border: 1px时:

  • 在 DPR=1 的屏幕上,显示为 1 个物理像素宽。
  • 在 DPR=2 的屏幕上,实际渲染为 2 个物理像素宽,视觉上变粗(例如 iOS 设备)。
  • 在 DPR=3 的屏幕上,渲染为 3 个物理像素宽,更粗(例如部分 Android 设备)。

现在我们可以区解决1px问题了

1. 媒体查询 + transform 缩放(推荐)

通过检测设备像素比(DPR),使用伪元素和缩放创建精确的 1px 边框。

css 复制代码
.border {
  position: relative;
}
/* 1倍屏 */
@media (-webkit-min-device-pixel-ratio: 1), (min-device-pixel-ratio: 1) {
  .border::after {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 1px;
    background: #000;
  }
}
/* 2倍屏 */
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2) {
  .border::after {
    transform: scaleY(0.5);
    transform-origin: 0 0;
  }
}
/* 3倍屏 */
@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) {
  .border::after {
    transform: scaleY(0.333);
    transform-origin: 0 0;
  }
}

优点 :精确控制物理像素,兼容性好(IE9+)。
缺点:代码量较大,需为每个方向的边框单独设置。

2. viewport 缩放(整页解决方案)

根据设备像素比动态调整 viewport 的缩放比例:

ini 复制代码
const scale = 1 / window.devicePixelRatio;
document.write(`<meta name="viewport" content="width=device-width, initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}, user-scalable=no">`);

优点 :一劳永逸,所有 1px 问题自动解决。
缺点:需配合 rem/em 布局,可能影响第三方组件。

3. box-shadow 模拟边框

利用阴影的扩散特性模拟极细边框:

css 复制代码
.border {
  box-shadow: 0 0 0 1px #000; /* 1px边框 */
}

优点 :代码简单,兼容性好。
缺点:阴影可能模糊,无法精确控制单边。

4. SVG 边框(现代方案)

使用内联 SVG 定义精确的 1px 边框:

ini 复制代码
.border {
  border: 1px solid transparent;
  background-origin: border-box;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='1'%3E%3Crect x='0' y='0' width='1' height='1' fill='%23000'/%3E%3C/svg%3E");
}

优点 :精确控制物理像素,支持各种形状。
缺点:代码复杂,IE 不支持。

5. CSS 渐变(适合单边边框)

使用线性渐变创建精确的 1px 线条:

css 复制代码
.border-bottom {
  background: linear-gradient(to bottom, #000, #000 1px, transparent 1px) no-repeat;
  background-size: 100% 1px;
  background-position: bottom;
}

优点 :简单灵活,兼容性好。
缺点:只能模拟单边边框。

三、各方案对比

方法 优点 缺点 兼容性
transform 缩放 精确控制,支持圆角 代码复杂,需多方向处理 现代浏览器
viewport 缩放 全局生效,简单直接 影响整体布局 所有浏览器
box-shadow 实现简单 效果模糊,不支持圆角 所有浏览器
SVG 边框 精确控制,支持复杂形状 代码复杂 IE 不支持
CSS 渐变 简单灵活 只能单边,不支持圆角 现代浏览器

那么这些方案有没有什么弊端?

  1. 圆角问题:transform 缩放方案可能导致圆角在某些浏览器中显示不流畅。
  2. 性能影响:大量使用伪元素或 transform 可能影响渲染性能。
  3. 混合方案:复杂项目中可能需要结合多种方案(如 viewport 缩放 + 局部 transform)。

优先考虑 viewport 缩放或 transform 缩放方案。

相信大家看到这里的时候还是会有一些疑问的,诸如------

1.1px问题只会影响到边框的设置吗,不会影响字体等其他的效果吗 2.只有1px时会出现这种问题吗?2px时会有给个问题吗?

下面回答一下这些疑问

1. 关于1px问题的影响范围

1px问题主要影响边框、细线和微小UI元素,但基本不影响字体。这是因为:

  • 边框/线条是纯色块渲染,高DPR下1CSS像素直接映射为多个物理像素时,浏览器无法智能优化,导致过粗或模糊;
  • 字体则自带抗锯齿和次像素渲染技术,操作系统和浏览器会动态调整显示方式,确保在不同DPI下保持视觉一致性。不过,极端情况下(如极小字号或1px文字描边)仍可能受影响。

2. 关于2px是否会出现类似问题

2px不会出现典型的"1px问题" ,原因在于:

  • 物理像素对齐:2px的宽度足够大,在高DPR设备(如DPR=2)下会直接渲染为4物理像素,浏览器不会触发抗锯齿优化,显示清晰且稳定;
  • 视觉容忍度:即使DPR=3时2px变为6物理像素,其粗细变化是线性且可预测的,而1px的细微偏差(如虚边或半像素渲染)更容易被察觉。因此,只有1px会因渲染策略不一致引发显著问题,2px及以上则无此困扰。

结语

CSS面试上篇就截取到这里了,上篇的考点是比较高频的考点。

CSS面试下篇也会尽快发出来

相关推荐
岁忧1 小时前
(LeetCode 面试经典 150 题 ) 11. 盛最多水的容器 (贪心+双指针)
java·c++·算法·leetcode·面试·go
一斤代码2 小时前
vue3 下载图片(标签内容可转图)
前端·javascript·vue
中微子2 小时前
React Router 源码深度剖析解决面试中的深层次问题
前端·react.js
光影少年2 小时前
从前端转go开发的学习路线
前端·学习·golang
中微子3 小时前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
3Katrina3 小时前
深入理解 useLayoutEffect:解决 UI "闪烁"问题的利器
前端·javascript·面试
前端_学习之路3 小时前
React--Fiber 架构
前端·react.js·架构
伍哥的传说4 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
qq_424409194 小时前
uniapp的app项目,某个页面长时间无操作,返回首页
前端·vue.js·uni-app
我在北京coding4 小时前
element el-table渲染二维对象数组
前端·javascript·vue.js