很全面的前端面试——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面试下篇也会尽快发出来

相关推荐
文刀竹肃4 分钟前
DVWA -SQL Injection-通关教程-完结
前端·数据库·sql·安全·网络安全·oracle
LYFlied9 分钟前
【每日算法】LeetCode 84. 柱状图中最大的矩形
前端·算法·leetcode·面试·职场和发展
Bigger11 分钟前
Tauri(21)——窗口缩放后的”失焦惊魂”,游戏控制权丢失了
前端·macos·app
Bigger30 分钟前
Tauri (20)——为什么 NSPanel 窗口不能用官方 API 全屏?
前端·macos·app
bug总结31 分钟前
前端开发中为什么要使用 URL().origin 提取接口根地址
开发语言·前端·javascript·vue.js·html
zwjapple1 小时前
全栈开发面试高频算法题
算法·面试·职场和发展
程序员爱钓鱼1 小时前
Node.js 编程实战:Redis缓存与消息队列实践
后端·面试·node.js
一招定胜负2 小时前
网络爬虫(第三部)
前端·javascript·爬虫
Shaneyxs2 小时前
从 0 到 1 实现CloudBase云开发 + 低代码全栈开发活动管理小程序(13)
前端
半山烟雨半山青2 小时前
微信内容emoji表情包编辑器 + vue3 + ts + WrchatEmogi Editor
前端·javascript·vue.js