给Markdown渲染网页增加一个目录组件(Vite+Vditor+Handlebars)(下)

1 引言

在上一篇文章《给Markdown渲染网页增加一个目录组件(Vite+Vditor+Handlebars)(上)》中笔者介绍了如何实现在Markdown渲染网页中加一个目录组件。不过,上一篇文章中只是介绍了一下功能也就是JavaScript部分的具体实现。其实要实现这个功能,另外一个关键就是样式也就是CSS部分的设计。

阅读本文可能需要的前置文章:

2 基础

在前端三剑客(HTML+CSS+JavaScript)中笔者认为CSS是最麻烦的,具体就麻烦在CSS并不是一个遵循程序员思维的语言,反而它遵循的是设计师的思维:要求对排版规则的掌控,要求能熟能生巧的积累,最好还要有一点美术审美。大部分的计算机语言都相通,但是CSS却特立独行。

另外,笔者觉得市面上关于CSS的中文教程也大多不太行,很多都是CSS属性的堆砌。笔者记得学习过一本CSS教程,光是开头介绍字体的设置就讲了一个很大的章节,这真的很难让初学者入门。有的人说CSS的属性要靠记忆,这是文科的思维,有一点道理在里面;但是笔者认为CSS作为一门计算机语言,还是有一些理性思维在里面的。其中,最理性最程序员思维的部分就是网页布局:设计出来的页面中的元素,至少要听你指挥,出现在你想要放置的位置。

2.1 盒子模型

盒子模型是网页布局中最基础的概念,定义了如何处理单个HTML元素。具体来说,就是每一个HTML元素都被看作是一个矩形的盒子,这个盒子由内容(content)、内边距(padding)、边框(border)和外边距(margin)组成。这个概念应该来说属于老生常谈了,基本上每个介绍CSS的教程都会首先介绍它。其实要掌握这个概念不用费那么多精力,对着浏览器后台开发工具调试一下padding、border和margin属性,看看每个参数的效果就可以了。如下图所示:

2.2 HTML文档流

HTML文档流(Document Flow)也是网页布局最基础概念之一,指的是HTML页面中的元素,默认按照从上到下、从左到右的顺序对页面元素进行排列和渲染的方式。这个理论听起来非常自然,甚至有点像废话文学;但确实就是网页布局的关键所在:

  1. HTML页面中的元素如果没有进行特定的排版设置,那么从上到下、从左到右的顺序就是HTML元素的默认位置,这让我们确定了通过CSS调整样式的起始值。
  2. HTML页面中元素大部分处于文档流中,即使通过CSS调整了位置,也只是改变了局部的布局方式,大体上仍然遵循这个规则。
  3. 大多数情况下HTML页面中的元素最好不要脱离文档流,因为脱离文档流往往需要比较精细的控制,除非少部分需求真的需要这么做。

在文档流中,HTML元素可以分为三种类型:

类型 特点 示例标签
块级元素 独占一行,自动换行,可设置宽高 div, p, h1h6
行内元素 不独占一行,宽度由内容决定,不能设置宽高 span, a, strong
行内块元素 行内显示,但可以设置宽高 display: inline-block

2.3 现代布局方式

与网页布局最直接相关的属性就是display,最基础的几种属性值如下:

  1. display: none; 元素不会被显示,也不占据任何空间,常用于隐藏元素。
  2. display: block; 元素作为块级元素显示(独占一行),可以设置宽度、高度、内外边距。前面介绍的块级元素display的默认属性值就是block
  3. display: inline;元素作为内联元素显示(和其他内联元素在同一行),不能设置宽度和高度。前面介绍的行内元素display的默认属性值就是inline
  4. display: inline-block;结合了inlineblock的特性,可以在同一行显示,并且可以设置宽度、高度、内外边距。常用于横向排列的导航栏或按钮组。

对于现代网页设计来说,更为重要的是display: flex;display: grid;这两种属性值。其中display: flex;更为重要一点,也就是所谓的弹性盒子布局(Flexbox)。

3 实现

3.1 弹性盒子布局

那么接下来就结合本文的具体实例来讲解Flexbox布局。回到本例的index.html:

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vite App</title>
</head>

<body>
  <div id="app">
    <div id="post-article-placeholder"></div>
    <div id="article-toc-placeholder"></div>
  </div>
  <script type="module" src="/src/main.js"></script>
</body>

</html>

对应的样式文件style.css是:

css 复制代码
body {
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f0f0f0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

#app {
  /* 将容器设置为弹性容器。 */
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: start;
  gap: 1rem;
  margin-top: 1rem;
  width: 100%;
}

#post-article-placeholder {
  min-width: 600px;
  max-width: 800px;
  flex: 1 1 auto; /* 允许伸缩 */
}

#article-toc-placeholder {
  width: 260px;
  position: sticky;
  top: 0;
  flex: 0 0 auto; /* 固定宽度不伸缩 */
}

在这里,我们想让app元素(包含博文内容控件和博文目录控件)居中显示,就设置根元素body为Flexbox布局:

css 复制代码
body {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

这里的样式设置的意思是:

  1. flex-direction定义主轴方向,设置成column表示body中的元素像列一样布局,也就是垂直布局。
  2. justify-content定义项目在主轴上的对齐方式,设置成center表示垂直方向上居中对齐。
  3. align-items定义项目在交叉轴上的对齐方式,设置成center表示在水平方向上居中显示。

app元素设置成居中之后,解下来我们想让app元素的子元素博文内容控件和博文目录控件左右布局,并且顶端对齐。要实现这个功能:当然还是要设置成Flexbox布局:

css 复制代码
#app {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: flex-start;
}

而这里的样式设置的意思是:

  1. flex-direction: row;表示app中的元素像行一样布局,也就是左右水平布局。
  2. justify-content: center;表示app中的元素在水平方向上向中心靠齐。
  3. align-items: flex-start表示app中的元素在垂直方向上,沿着起始位置靠齐,也就是顶端对齐。

可以调整一下这些属性的值来加深对布局的理解。比如align-items的值设置成center,那么博文目录控件就会与博文内容控件在垂直居中对齐;设置成flex-end博文目录控件则会与博文内容控件底部对齐。justify-content的设置在某些情况下非常有用:比如需要在水平方向上(这里的主轴方向)靠左对齐,就设置成flex-start;需要靠右对齐,就设置成flex-end;需要两端对齐,就设置成space-between

Flexbox布局的好处就在这里,它可以通过设置垂直布局和水平布局,形成了一种对网页布局的通解:只要你拆分的盒子数量和层级够多,那么网页元素可以始终控制在HTML文档流中。换句话说,一个前端程序员的基本素质就是将原型转换成多层级的包含垂直/水平布局的盒子。比如说稀土掘金网站的首页,根据其布局我们可以进行如下拆分:

3.2 响应式布局

使用Flexbox布局还有一个好处,那就是可以实现响应式布局。所谓响应式布局,指的是使网页能够在不同的分辨率下都有比较好的浏览体验。结合本文的例子来说:

css 复制代码
#app {
  display: flex;
}

#post-article-placeholder {
  flex: 1 1 auto; /* 允许伸缩 */
  min-width: 600px;
  max-width: 800px;
}

#article-toc-placeholder {
  width: 260px;
  flex: 0 0 auto; /* 固定宽度不伸缩 */
}

这里的flex 包含了三个子属性:

css 复制代码
flex: <flex-grow> <flex-shrink> <flex-basis>;

具体的含义是:

子属性 描述
flex-grow 定义项目的放大比例,默认为 0(不放大)
flex-shrink 定义项目的缩小比例,默认为 1(可缩小)
flex-basis 定义项目在分配多余空间之前的初始大小,可以是长度值(如 200px)、百分比(如 auto)等

对于博文内容控件,flex: 1 1 auto;的意思是:

  • flex-grow: 1:该元素会尽可能地占据剩余空间。
  • flex-shrink: 1:该元素在空间不足时可以被压缩。
  • flex-basis: auto:该元素的初始宽度由其内容或显式设置的 width 决定。

对于博文目录控件,flex: 0 0 auto;的意思是:

  • flex-grow: 0:该元素不会扩展。
  • flex-shrink: 0:该元素不会压缩。
  • flex-basis: auto:项目的初始大小由其内容或显式设置的 width 决定。

也就是说,博文目录的宽度保持原有大小不参与伸缩;而博文内容控件则会根据容器空间进行伸缩。这也是这种布局方式被称为弹性盒子布局的原因,在这种布局方式下,页面中的元素可以灵活响应页面分辨率宽高的变化。不过,在现代网页中文档内容区域的宽度范围通常在600px800px之间,这里就设置博文内容控件的最小宽度为600px,最大宽度为800px

当然响应式布局的内涵不止这一点,响应式布局最终希望在不同设备(如桌面电脑、平板电脑和手机)上都有最佳的浏览体验,这需要自动根据屏幕尺寸和方向自动调整布局结构、元素大小及位置;要实现这一点还是离不开Flexbox布局。

3.3 粘性定位

在这里,笔者就回答一下上一篇文章《给Markdown渲染网页增加一个目录组件(Vite+Vditor+Handlebars)(上)》中的问题:博文目录是如何始终保证粘在页面的右上角的?因为使用了粘性定位:

css 复制代码
#article-toc-placeholder {
  position: sticky;
  top: 0;
}

这里的意思就是当博文目录控件离开视图页面的时候,就将博文目录控件的位置粘在页面的顶部(top属性值为0的位置)。看起来这个实现非常简单,但是如果在一些复杂的情况下使用会有一些问题,因为粘性定位本质上是脱离了HTML文档流的。比如说,将博文目录粘在右侧控件的顶部还好,如果是粘在其他特定的位置,就需要精确地控制位置属性,否则就很容易与其他页面元素冲突。

不止粘性定位,浮动(float)、绝对定位(position: absolute)、固定定位(position: fixed)都会脱离HTML文档流,不推荐用来作为主要的布局方式,这里就不细说了。

4 结语

最终的博文目录粘在页面右上角的效果如下所示:

相关推荐
全宝8 小时前
🖲️一行代码实现鼠标换肤
前端·css·html
土豆125016 小时前
React-Markdown 完全上手指南
react.js·markdown
断竿散人16 小时前
彻底吃透CSS盒模型:从布局坍塌到精准控制!
前端·css
逝水如流年轻往返染尘18 小时前
CSS基础学习1
前端·css·学习
Hilaku18 小时前
我踩爆了 CSS Module 的所有坑,别再被骗了
前端·css·react.js
onebyte8bits19 小时前
打造丝滑滚动体验:Scroll-driven Animations 正式上线!
前端·javascript·css·html·html5
软件技术NINI19 小时前
html css js网页制作成品——HTML+CSS+js力学光学天文网页设计(4页)附源码
javascript·css·html
睡不着先生20 小时前
打造丝滑滚动体验:Scroll-driven Animations 正式上线!
css·chrome·html
有事没事实验室1 天前
web方向第一次考核内容
前端·css·开源·html5