从零开始建立个人博客VI:网页排版

Markdown默认并不支持文字排版,不过由于Markdown兼容HTML,因此可是使用HTML/CSS来实现排版。为此需要先了解一些HTML/CSS的基本知识。

HTML基础

我们通常看到的网页(静态页面)是一种名为超文本标记语言(Hyper Text Markup Language, HTML)的标记语言所写的文档,由浏览器解析渲染后呈现出我们看到的效果。
作为标记语言,与Markdown类似,HTML也是通过一套特殊的标记来定义文档结构。对于HTML,这套标记便是HTML标签(tag),由尖括号包围关键词组成,如<title>表示标题。
HTML标签通常成对出现,分别为开始标签和结束标签,用于限定范围;结束标签以/与开始标签作区分,如<title>...</title>

  • HTML文档(即网页)是由HTML元素组成的;
  • HTML元素是指由开始标签和结束标签所限定的代码块;
  • HTML元素大多需要嵌套使用,组成HTML文档;
  • HTML元素大多都有属性,如style、class、id、title等;
  • 部分HTML元素属于空元素,即没有元素内容,但可以有元素属性。
    如换行<br>、水平线<hr>、元信息<meta>、资源链接<link>、图片资源<img>等。
    空元素没有对应的结束标签,而是在开始标签中关闭:即将/直接放在开始标签内末尾,如<br>严格应写作<br />

元素实例

标题:<title>...</title> 定义标题,<h1><h6>可定义不同等级标题
段落:<p>...</p> 定义段落
主体:<body>...</body> 定义文档主体,以段落为嵌套子元素
全文:<html>...</html> 定义整个HTML文档,以主体为嵌套子元素
样式:<style>...</style> 定义文档样式信息,即CSS,位于文档头内
元信息:<meta ...> 定义文档的元信息,如作者、修改时间、关键词等,位于文档头内
文档头:<head>...</head> 文档头,定义关于文档的信息,如文档标题、样式、元信息等
文档块:<span>...</span> <div>...</div> 文档分块,前者是块级元素,后者为内联元素
更多: http://www.w3school.com.cn/tags/index.asp
<!--...-->注释、<br>换行、<hr>水平线、<a>链接/锚、<link>资源链接
<del>删除、<ins>插入:两者配合使用,前者标明要删除的文字,后者则标明要插入的文字
<b>粗体、<i>斜体、<em>强调、<strong>重要、<mark>标记、<sup>上标、<sub>下标
<ul>无序列表,<ol>有序列表, <li>列表项目、<table>表格, <td>表格单元
<figure>图片,<img>图片资源、<code>代码、<script>客户端脚本,如Javascript
<kbd>键盘文本、<iframe>内联框架、q短引用、<blockquote>引文、<cite>参考引用
注意:超链接是由标签<a>而不是<link>实现,后者用于链接外部资源,最常见的链接外部样式表(CSS文件)

元素详解

HTML元素大致可分为块级元素(block level element)和内联/行内元素(inline element)两种:

  • 块级元素显示时通常会以新行开始和结束,如标题、段落、列表、表格、图片figure、引文等;
  • 内联元素显示时通常不会换行,而是与相邻元素位于同一行(inline),如加粗、斜体、链接、强调、图片资源img、短引用、代码等;
  • 块级元素可以包含其他块级元素或内联元素,而内联元素只应容纳其他内联元素或文本;

<div><span>都是用于文档分块的元素,没有特定的含义(无语义),可作为向文档添加结构(划分文档块)的通用机制,通过元素的属性可设置文档块的样式布局(CSS)、ID等。
两者区别是前者是块级元素,后者为内联元素,因此<div>可作为组合其他HTML元素的容器,而<span>只能作为行内元素的容器。<div>元素的一个常见的用途便是文档布局。

<code>属于内联元素,默认会忽略换行及多余的空格,将所有内容置于一行,inline显示。对于代码块,为保持格式,可将其内嵌于预格式文本元素<pre>中:<pre><code>...</code></pre>
<pre>属于块级元素,用于显示预格式化文本,文本的排版结构会保持原样,字体为等宽字体。注意<pre>元素并非完全原样显示内容,文本仍会被格式化,加粗、链接、特殊字符等都依然生效。

元素属性

HTML元素大多都有属性,如style、class、id、title等:

  • 属性在元素的开始标签中设置;
  • 属性总是以name="value"形式的属性名/取值对出现;
  • 属性取值最好加引号,如果属性值本身含有双引号,则可用单引号;
  • 全局属性,如style、class、id、title等,适用于所有元素;部分元素还有额外的属性。
    如链接<a>通过href属性设置链接地址:<a href="http://yelf.me">My blog</a>
    图片<img>通过src属性指定图片地址:<img src="/path/to/image" />

id属性为元素分配独一无二的名字,以字母或者下划线开始,不能以数字开始。id作为元素的唯一标识,作用丰富:可以指向样式表中特定的样式;可以作为链接转跳的锚点<a href="#id">...</a><a href="~URL~#id">...</a>;还可用于DOM脚本,定位特定元素等等。

class属性指定元素的类名,通常用于指向样式表中的类。与具有唯一性的id不同,多个元素可以同属于一个类;一个元素也可以同时指定多个类(用空格分割)。
通过对HTML元素进行分类(指定类名),可以非常方便的为相似元素设置统一的CSS样式,尤其常与自身无语义的结构元素<div><span>配合使用。

style属性指定元素的样式,比如设置文本的字体字号颜色对齐等:font-family, font-size, font-weight, font-style, color, background-color, text-align…
样式信息以键:值对的形式赋值给style属性,多个样式以分号间隔。

CSS基础

CSS全称层叠样式表(Cascading Style Sheets),是为了实现内容与表现的分离而引入的,用于对网页内容进行格式化,定义字体、颜色、背景、布局等。
HTML原本有部分排版元素,如居中<center>...</center>及字体<font>...</font>等,但HTML5移除了这部分元素,排版完全由CSS完成,从而实现了内容和表现相分离。

在HTML中引入CSS有三种方式:

  • 内联(inline)样式:style是HTML元素的全局属性,利用该属性可直接在开始标签内对任意元素设置样式,将CSS融入文档,如<p style=“font-size:24px;”></p>;利用style可以设置任何CSS属性。
  • 内部样式表:在文档头中,用样式元素<style>集中定义CSS样式表,如:
    1
    2
    3
    4
    5
    6
    7
    <head>
    <style type="text/css">
    body {
    color: red;
    }
    </style>
    </head>
  • 外部样式表:在文档头中,用资源链接<link>引入外部CSS样式表,如:
    1
    2
    3
    <head>
    <link rel="stylesheet" href=" ~url_to_~/mystyle.css " type="text/css" />
    </head>

其中内联样式最灵活,可以单独指定任意元素的样式;改动起来也最麻烦,只能逐一修改,而且内容与表现相混杂,违背了引入CSS的初衷。
内部样式可以做到单个网页的一改全改;而外部样式则可以做到所有(引入该样式的)网页的一改全改,还可在网上共享样式文件,这正是内容与表现相分离的优势。不懂排版的用户可以只关注内容,样式直接从网上获取,比如博客主题或者Bootstrap框架等。
优先级上内联样式最高,当属性设置出现冲突时,内联样式会覆盖其他两种样式的设置,其次为内部样式,最后是外部样式;而对于不冲突(不同)的属性,则会依次继承设置。

CSS规则语法:

  • 每条CSS规则都由选择器和声明两部分组成,声明置于花括号内,选择器位于花括号前,以空格分割;
  • 选择器(selector)用于筛选样式所要修改的元素,可以是HTML元素(标签)、ID、类、属性等;
    元素选择器无需特殊标识,直接使用标签名;ID选择器以#做前缀;类选择器以.做前缀;属性选择器中属性需置于[]内;
    此外还有后代元素( )、子元素(>)、相邻兄弟元素(+)、伪类/伪元素(:)等更复杂的选择器,具体可查看参考链接;
    多个选择器可以共享同一声明,以逗号分割,置于同一声明之前(注意如果没有逗号则是后代元素选择器);
    子元素选择器与后代元素选择器的不同是其只选择一代子元素,不会考虑更深层次的后代元素;
  • 每条声明为property: value的键值对形式,多条声明(多个属性)以分号分割;
    属性取值如果有空格注意加引号;
  • 添加空格和换行可以提升CSS的可读性,而不会影响CSS的效果;
    示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <style type="text/css">
    p {font-family: Verdana, sans-serif;}

    h1,h2,h3,h4,h5,h6 {color: black;}

    #sidebar p {
    font-style: italic;
    text-align: right;
    margin-top: 0.5em;
    }

    .center {text-align: center;}

    [title] {color:black;}

    a[href] {color:blue;}

    a:visited {color: #00FF00}

    p:first-letter{color:#ff0000; font-size:xx-large;}
    </style>

Markdown混排

Markdown主要用于结构化文本,排版能力较弱,比如不提供居中、居右等对齐,为实现这些控制需要嵌入HTML。

https://daringfireball.net/projects/markdown/syntax#html
For any markup that is not covered by Markdown’s syntax, you simply use HTML itself. There’s no need to preface it or delimit it to indicate that you’re switching from Markdown to HTML; you just use the tags.

The only restrictions are that block-level HTML elements — e.g. <div>, <table>, <pre>, <p>, etc. — must be separated from surrounding content by blank lines, and the start and end tags of the block should not be indented with tabs or spaces. Markdown is smart enough not to add extra (unwanted) <p> tags around HTML block-level tags.

Note that Markdown formatting syntax is not processed within block-level HTML tags. E.g., you can’t use Markdown-style emphasis inside an HTML block.

Span-level HTML tags — e.g. <span>, <cite>, or <del> — can be used anywhere in a Markdown paragraph, list item, or header. If you want, you can even use HTML tags instead of Markdown formatting; e.g. if you’d prefer to use HTML <a> or <img> tags instead of Markdown’s link or image syntax, go right ahead.

Unlike block-level HTML tags, Markdown syntax is processed within span-level tags.

需要注意的是,如上所述,HTML与Markdown混排时,块级元素必须与周围的Markdown文本以空行相隔,且起始与终止标签不能缩进;而行内元素则没有任何限制。且对于块级元素,其内的Markdown格式化文本是不会被渲染引擎处理的,如插入的图片、链接等将不能正确显示;即使某些情况下可以渲染也会存在各种小问题,且依赖具体的渲染引擎。而对于行内元素,则不存在限制,Markdown语法会被正常处理。由此一个取巧的办法是,在某些必须在HTML块级元素内嵌入Markdown格式化文本的情况,可以在块级元素外添加<span>标签(行内元素),以保证Markdown语法被正常处理。
最后,根据所使用Markdown渲染引擎,可能存在Markdown文本中的换行原样保留,HTML块级元素中的换行却被忽略的情况,采用上述取巧办法时同样存在该问题(会出现不必要的空行)。

除了嵌入HTML外,还有一种神奇的做法,使用kramdown语法直接对元素设置CSS属性,置于{: }中,跟在元素后,如{: .center},不过Hexo不支持该语法。

简单排版实例

空行
Markdown中空行默认被视作分段的标志,且不支持插入连续的多个空行。视觉上分段的段落间隔与插入一个空行接近,但实际上两者并不相同,光标无法置于段落间隔中。HTML中,换行是 <br />,分段则是 <p>...</p>,后者有额外的段落间距。如需插入空行,尤其是多个连续空行时,可使用换行符,或者可以在行首插入下面提到的空白符的编码。

1
<br />

空格
Markdown不支持一个以上的连续空格,插入连续空白间隔时可借助中文的全角空格’ ’ (宽度为字体宽度),或者使用空格的HTML编码符号。常见的符号有:

1
2
3
4
&nbsp;  'no-break space' 普通空格,特点是再多也不换行(no-break),因此宽度不定
&emsp; 'em space' em代表当前字体宽度
&ensp; 'en space' en代表字体宽度的1/2
&thinsp; 'thin space' 字体宽度的1/4

彩色文本

1
2
<span style="color: #00bfff">Text with color</span>
[<span style="color: #00bfff">Link with color</span>](http://yelf.me)

文本右对齐

1
2
3
4
<div style="text-align: right;">
text
text
</div>

需要注意的是,某些Markdown渲染引擎下,HTML块级元素中的换行会被忽略,上述文本最终显示时会位于同一行上,而不会保留换行信息,这对于诗歌等内容尤为不利。Hexo默认的Markdown解析器marked会自动添加换行符。为了更好的支持公式,我将解析器更换为markdown-it,之后发现了问题。
<pre>元素会保留空格空行换行等空白字符,是一个不错的选择,但需注意的是其网页显示效果类似于代码块,可能会太过突出。
另一种选择是在每一行文字后添加换行 <br /> 。这种方法的问题是,当你使用默认的Markdown解析器marked时,会自动再添加一个换行符,从而每行后面都会多出一个空行。可以选择源文件本身不换行,只用 <br /> 来分割行,这样在所有解析器下最终效果都正常,只是源文件本身会显得凌乱。
最后,某些Hexo Markdown渲染引擎可以在配置中设置Markdown及HTML换行的具体处理方式。

标题居中
由于转换为网页时,Markdown中由###等产生的标题会转换为HTML中对应层级的标题h1, ..., h6,因此我们可以直接调整标题元素样式。如三级标题居中,可通过插入以下HTML/CSS代码实现。

1
2
3
<style type="text/css">
h3{text-align: center;}
</style>

图像居中
由于转换为网页时,Markdown插入图片的语句会对应于img元素,因此我们可以直接调整img元素样式。在Markdown文档中插入以下HTML/CSS代码,便可使文章中所有插入的图片都居中。图片的其他属性如大小/宽度等也可以在此处统一调整,如width: 80%;

1
2
3
<style type="text/css">
img{text-align: center; margin: 0 auto;}
</style>

更详细的讨论可参考Markdown中如何设置图片居中

分栏布局
首先,可借助表格实现分栏布局:

1
2
3
4
5
6
7
<table>
<tr>
<td>AAA</td>
<td>BBB</td>
<td>CCC</td>
</tr>
</table>

除了表格,还可用CSS自定义标签,指定各栏宽度实现分栏,具体参考stackoverflow
最后,还可借助CSS FlexBox,具体参考W3Schools。此时需注意内容中不能有空行,不过可在行末添加换行符</br>实现插入空行。

1
2
3
4
5
6
7
8
9
10
11
12
13
<style type="text/css">
.custom-row {display: flex;}
.custom-column {flex: 50%;}
</style>

<div class="custom-row">
<div class="custom-column">
....
</div>
<div class="custom-column">
....
</div>
</div>

基于分栏布局可实现简单的图片九宫格/瀑布流布局,具体参考网页图片展示


HTML的锚可以实现文章内部转跳或者转跳到另一文章的特定位置,具体使用分两步:

  • 首先利用HTML元素的ID属性,赋予跳转目的地一个标识
  • 之后在进行跳转的文字处,添加链接,指向上述ID

比如要转跳到第一小节HTML基础,代码如下:
首先给第一小节标题添加ID

1
### <span id="html">HTML基础</span>

之后添加链接,指向该ID,注意要以#为前缀

1
...转跳到第一小节<a href="#html">HTML基础</a>,代码...

对于后者(插入链接),可直接使用Markdown语法:

1
...转跳到第一小节[HTML基础](#html),代码...

利用锚还可以转跳到外部文章中的指定位置,做法类似,只需在插入的链接后添加锚点ID。
比如要转跳到文章《Markdown基础》,小节链接中的引用部分,代码如下:

1
....小节链接中的[引用](../markdown-basics#reference)部分....

参考链接
HTML基础-W3school:HTML元素HTML标签HTML5标签
CSS基础-W3school:CSS样式表CSS基础语法CSS选择器
浮动与对齐-W3school:CSS框模型CSS浮动CSS水平对齐 padding内边距、border边框、margin外边距
使用css让markdown生成的网页里图片居中
You Can’t Float Center with CSS
markdown中插入图片怎么定义图片的大小或比例
How to semantically tag poem text

扩展阅读
XTML v.s. HTML:http://www.w3school.com.cn/xhtml/xhtml_html.asp
HTML5中的新元素:http://www.w3school.com.cn/html/html5_new_elements.asp