聊一点你可能不知道的HTML

发布于 大漠

HTML(HyperText Markup Language)是构建Web的最基础部分之一,也可以说是构建Web世界的一砖一瓦。它定义了Web页面或应用的内容的含义结构。HTML中包含了很多种不同的标签元素,用来更好的帮助你构建Web能让客户端更好的理解,更正确的显示,也能更好的帮助搜索引擎理解页面,还可以更好的利于屏幕阅读技术。但不幸的是,很多开发者都认为HTML仅是一种由元素标签构建成的标签性语言,太过于简单,没有什么可以值得去研究和思考的。如果你也是这么想的,那么接下来的内容可能对你会没有任何的意义;如果不是的话,那么你可以继续往下阅读。

接下来在这篇文章中和大家聊一点你可能对HTML不太熟悉的东西。

你可能不知道的HTML标签

在现代Web开发中,特别是依赖于JavaScript的框架(比如React、Vue等),很多开发者构建的Web页面中能看到的标签元素屈指可数,甚至全文或者整个项目只有 **div**标签元素。这样做不能说是错的,但对于一位专业的Web开发者来说,是不好的。因为这样构建出来的页面,在可读性、可访问性方面是不友好的。为什么这么说不在这里讨论,如果你想知道原因,可以阅读A11Y系列中的《编写HTML时要考虑可访问性》一文。这里只想和大家聊几个有用但不多见的HTML标签元素。

<time>

<time>元素经历了一段坎坷的路程。它早在2009年就补引入到HTML5规范,不过在2011年被<data>所取代,同年晚些时候该元素又重新回到HTML5规范,并且还得到了一定的改进,允许新的date/time格式。

HTML的<time>元素可以用来表示机器可读的日期时间持续时间。它可以用于创建事件调度、归档和其他基于时间的函数。最为常见的使用场景,比如博客文章创建或发布的时间:

<time>元素常用来表示公历中的日期或时间。

<time>元素和<span>一样也是一个内联元素,并且必须有一个结束标记</time>。当以最简单的形式使用时,元素的内容必须是有效的日期或时间字符串。

<!-- 没有datetime内容,则必须是有效的日期、时间或精确的日期时间 -->
<time>2020-05-23</time>

<!-- 当使用datetime时,内容可以是任何相关的内容 -->
<time datetime="2020-05-23">23<sup>th</sup> May</time>
<time datetime="20:00">starting at 8pm</time>
<time datetime="2009-11-13T20:00+00:00">8pm on my birthday</time>

如果给定的日期不是正常日期或者在公历中最早的日期之前,请不要使用此元素。

简单地说呢,你可以在<time></time>之间放置任何内容 —— 这是人类可读的部分。然后,与datetime属性一起使用,它能帮助搜索引擎生成更智能的结果,并帮助浏览器识别日期,浏览器可能会将日期用于向日历添加事件等特殊功能。

<del><ins>

<del>用来定义从文档中删除的文本,而<ins>是定义插入的文本。比如我们平时在Github上提交的代码时,有删除的,有插入的,比如:

针对这样的场景,使用<del><ins>标签就非常贴合使用场景,或者说更符合语义化。

这两个元素除了支持所有的全局属性之外,还支持citedatetime属性:

  • cite:该属性的值指向一个文档的URL,该文档解释了文本被删除或插入的原因
  • datetime:该属性指示的文档修改发生的时间和日期,并且该属性的值必须是一个有效的日期或者时间字符串。如果该值不能被解析为日期或者时间,则该元素不具有相关联的时间标记

但有些场景,看上去是像插入或删除的意义,但使用的时候还是需要仔细斟酌,比如:

如果从UI样式上来看,<del><ins>有点符合,但从语义描述上来说有点牵强。但对于一些电子阅读产品,特别是能提供做笔记的阅读产品来说,<del><ins>就特符合,比如下面这个示例:

在HTML中还有另一个标签元素<s>,样式上它和<del>非常相似,而且也是一个语义标签,只不过<s>标签已从HTML5规范中移除,但还存在于HTML规范中。<s><del>不同之处是:

  • <s>表示不再准确或不再相关的内容
  • <del>表示从文档中删除的内容

也就是说,它们实际上在语义上代表了不同的东西。特别地,<del>将在你有一个现有文档并希望指示文档中已删除的文本的情况下使用。这与文档中的文本不同,文档中的文本不再准确,但还没有被删除,这个时候可以使用<s>。比如上图中的示例,折扣价就可以用<s>来构建会更合理一些。

看上去<s><del>非常接近,但它们有一个重要的区别。<ins><del>用于显示编辑更改,通常是对已发布的页面(比如前面提到的一些阅读产品,电子书之来,用户做笔记)。<s>用于指示不再相关或不准确的信息,但仍然提供一些值(比如前面示例中的商品折扣前后的价格),通常用于同时创建包含旧信息和新信息的文档。

<s><del><ins>几个标签都具有一些默认样式:

  • <ins>:新插入的内容,默认带下划线
  • <del>:删除的内容,默认带删除线
  • <s>:折扣价$89.00,默认带删除线

<s><del><ins>都是语义化标签,也利于可访问性。不幸的是,没有浏览器将这些标签元素显示在可访问树中,因此屏幕阅读器无法传递关于这些标记的任何信息。我们可以使用CSS来绕开它,让屏幕阅读器可以获取用户的标签:

ins::before,
ins::after,
s::before, 
s::after,
del::before,
del::after {
    clip-path: inset(100%);
    clip: rect(1px, 1px, 1px, 1px);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}

del::before {
    content: " [deletion start] ";
}

del::after {
    content: " [deletion end] ";
}

s::before {
    content: " [start of stricken text] ";
}

s::after {
    content: " [end of stricken text] ";
}

ins::before {
    content: " [insert start] ";
}

ins::after {
    content: " [insert end] ";
}

<mark>

我们平时在浏览Web的时候,在页面中搜索自己需要的关键词时,如果有匹配的情况下对应的关键词会高亮显示,如下图所示:

HTML5的<mark>元素就是用来强调内容或突出显示搜索中的搜索词。<mark>元素的典型使用场景包括:

  • 当用在引用(<q><blockquote>)中时,通常用来显示有特殊意义的文本,但不在原材料中标记出来;或者是用来显示特殊审查的材料,即使原作者不认为它特别重要。
  • 另外,<mark>元素还用来显示与用户当前活动相关的一部分文档内容。例如,它可能被用于显示匹配搜索结果中的单词。
  • 不要为了语法高亮而用 <mark> 元素; 你应该用 <strong> 元素结合适当的CSS来实现这个目的(语法高亮)。

不要把 <mark> 元素和 <strong> 元素搞混淆;<strong> 元素用来表示文本在上下文的重要性的, 而 <mark> 元素是用来表示上下文的关联性的

<picture>

HTML <picture> 元素通过包含零或多个 <source> 元素和一个 <img> 元素来为不同的显示/设备场景提供图像版本。浏览器会选择最匹配的子 <source> 元素,如果没有匹配的,就选择 <img> 元素的 src 属性中的URL。然后,所选图像呈现在<img>元素占据的空间中。

要决定加载哪个URL,user agent 检查每个 <source>srcsetmediatype 属性,来选择最匹配页面当前布局、显示设备特征等的兼容图像。

  • srcset:为浏览器提供了一组可供选择的源,以及这些源的大小;url之间以逗号(,)作为分隔,并与宽度配对
  • media:允许你提供一个用于给用户代理作为选择 <source> 元素的依据的媒体条件(类似于媒体查询)。如果这个媒体条件匹配结果为 false,那么这个 <source> 元素会被跳过
  • type:允许你为 <source> 元素的 srcset 属性指向的资源指定一个 MIME 类型。如果用户代理不支持指定的类型,那么这个 <source> 元素会被跳过

<progress>

在Web开发中,时间会碰到进度条的需求:

一般情况之下,都会像下面这样来做:

<div class="progress__bar">
    <div class="progress"></div>
</div>

但在HTML5中专门为这样的场景提供了一个独立的元素<progress>。该元素就是用来显示一项任务的完成进度

<progress></progress>
<progress value="0.7">Progress</progress>
<progress value="41" max="100">Alex</progress>

不做任何样式的处理下,你看到的效果如下:

注意,不同的浏览器下,效果会有所差异。不过我们可以通过一些CSS技术来让他们在不同的客户端下统一UI效果。比如@Pankaj Parashar在《The HTML5 progress Element》中介绍的相关方法。使用CSS来美化<progress>的话,需要借助用户代理的Shadow DOM相对应的伪元素(在前面的《聊聊input元素》一文中有提到过),就比如<progress>元素在Chrome中来说,它具有:

具体的代码可以看下面这个示例:

<progress>元素和其他HTML元素一样,除了具有全局属性之外,还有:

  • max:该属性描述了这个progress元素所表示的任务一共需要完成多少工作
  • value:该属性用来指定该进度条已完成的工作量。如果没有value属性,则该进度条的进度为"不确定",也就是说,进度条不会显示任何进度,你无法估计当前的工作会在何时完成(比如在下载一个未知大小的文件时,下载对话框中的进度条就是这样的)

HTML的<progress>还可以和<video>结合起来使用,实现自定义的视频播放进度条效果:

<figure>
    <video id="video" src="http://html5videoformatconverter.com/data/images/happyfit2.mp4"></video>
    <figcaption>
        <button id="play" aria-label="Play" role="button">►</button>
        <progress id="progress" max="100" value="0">Progress</progress>
    </figcaption>
</figure>

加上一些CSS,就可以得到像下面这个示例的效果:

有关于这方面更详细的介绍,还可以阅读@Geoff Graham的《Some Innocent Fun With HTML Video and Progress》一文。

<meter>

<meter>看上去和<progress>非常地相似,但它们之间也有很大的变差异,<meter>根据不同的属性值会有不同的UI风格:

HTML <meter>元素用来显示已知范围的标量值或者分数值。比如磁盘使用量,查询结果的相关性或者投票人口中选择特定候选人的比例。

上图就是OSX硬盘使用量的示意图

<meter>除了全局属性之外,另外还有六个属性:

  • value:当前的数值。如果设置了最小值(min)和最大值(max),它必须介于最小值和最大值之间。如果没有指定或者格式有误,值即为0。如果给定的值不在最小值和最大值之间,它的值就等于它最接近的一端的值。
  • min:值域的最小边界值。如果设置了,它必须比最大值(max)要小。如果没设置,默认为0
  • max:值域的上限边界值。如果设置了,它必须比最小值(max)要大。如果没设置,默认为1
  • low:定义了低值区间的上限值。如果设置了,它必须比最小值(min)属性大,并且不能超过high值和最大值(max)。未设置或者比最小值还要小时,其值即为最小值(min)。换句话说,如果value介于minlow之间,该元素就会表现出低值(low)的视觉效果,value落在[min,low][high,max]等不同的区间会使浏览器渲染该元素时出不同的视觉效果。
  • high:定义了高值区间的下限值。如果设置了,它必须小于最大值(max),同时必须大于low值和最小值(min)。如果没有设置,或者比最大值还大,其值即为最大值(max)。
  • optimum:这个属性用来指示最优/最佳取值。它必须在最小值(min)和最大值(max)范围内。当使用了lowhigh属性时,它指明哪一个取值范围是更好的。如果optimumminlowmin ≤ optimum ≤ low )之间,则下限值(low)认为是首选区域;如果optimumhighmaxhigh ≤ optimum ≤ max )之间,则认为上限值(high)为优选区域
  • form:该属性将本元素与对应的form元素关联。例如,一个计量器可能用来显示某个数值输入框(input元素,number类型)的范围。只有当计量器元素被用作表单关联的元素时,该属性才应当被使用;即便如此,如果它作为表单的后代元素出现,它仍然有可能被省略。

来看一个简单的示例:

<meter></meter>
<meter value=".5"></meter>
<meter value="-.5"></meter>
<meter value=".5" high=".8"></meter>
<meter value=".9" high=".8"></meter>
<meter low=".25" optimum=".15" high=".75" value=".5"></meter>
<meter low=".25" optimum=".2" high=".75" value=".8"></meter>

效果如下:

meter在样式上的处理和progress类似,有关于这方面更详细的介绍可以阅读@Pankaj Parashar的《The HTML5 meter Element》一文。

在HTML5中,input[type="range"]<progress><meter>在某些方面有点类似,但在细节上还是有很大的差异,具体的可以参考下面的Demo:

<details>

HTML <details>元素可创建一个组件,仅在被切换成展开状态时,它才会显示内含的信息。<summary> 元素可为该部件提供概要或者标签。

HTML的<details>的交互行为就像手风琴的交互效果,用户点击时,它包含的内容会显示与隐藏之间切换,如下图所示:

<details> 元素有一个 open 布尔值属性,默认它包含的内容(除了 summary)是隐藏的,加了这个属性之后,内容就显示出来了。

<summary> 总是和 <detail> 配合使用。其实,上面代码里,虽然我们没有在 <details> 包含 <summary>,但是 ,<details> 会有一个缺省的 summary ,也就是说:

<details></details>

<!-- 等同于(中文环境下) -->
<details>
    <summary>详细信息</summary>
</details>

<!-- 如果我们手动设置 `<summary>`,就会覆盖掉缺省的 -->
<details>
    <summary>一首小诗</summary>
</details>

具体的示例如下:

有关于这方面更详细的介绍,还可以阅读:

<output>

HTML的<output>标签元素表示计算或用户操作的结果。该元素和input[type="range"]配合使用:

<input type="range" name="foo">
<output for="foo" onforminput="value = foo.valueAsNumber;"></output>

具体的示例效果如下:

有关于<output>更详细的介绍,还可以阅读:

小结

文章中向大家介绍了几个HTML标签元素,这些标签元素并不常见,但他们非常有用,特别是在某些具体的场景中,这些标签比我们使用其他的HTML标签元素,比如div更有用,而且更有语义。虽然在社区中,众多开发者都认为HTML太过于简单,没有什么可以值得大家去思考或考究的,但事实并非如此,使用具有语义化的HTML标签对于我们构建Web页面是非常有用和有意义的。

我想说的是,任何一门语言,不管是程序语言还是描述性语言对于Web的构建来说都是有其自身意义的,如果你在这方面有更多的经验,欢迎在下面的评论中与我们共享。