HTML(超文本标记语言)
初识HTML
来看看维基百科的解释。
HTML(超文本标记语言——HyperText Markup Language)是构成 Web 世界的一砖一瓦。它定义了网页内容的含义和结构。除 HTML 以外的其它技术则通常用来描述一个网页的表现与展示效果(如 CSS),或功能与行为(如 JavaScript)。
“超文本”(hypertext)是指连接单个网站内或多个网站间的网页的链接。链接是网络的一个基本方面。只要将内容上传到互联网,并将其与他人创建的页面相链接,你就成为了万维网的积极参与者。
HTML 使用“标记”(markup)来注明文本、图片和其他内容,以便于在 Web 浏览器中显示。HTML 标记包含一些特殊“元素”如 <head>,<title>,<body>,<header>,<footer>,<article>,<section>,<p>,<div>,<span>,<img>,<aside>,<audio>,<canvas>,<datalist>,<details>,<embed>,<nav>,<output>,<progress>,<video>
等等等等。
HTML 元素大体分14类
- 主根元素
html
- 文档元数据
meta;title;head...
; - 分区根元素
body
- 内容分区
main;section;address...
- 文本内容
- 内联文本语义
- 图片和多媒体
- 内嵌内容
- 脚本
- 编辑标识
- 表格内容
- 表单
- 交互元素
- Web 组件
参考MDN文档
关于 meta 标签的一些用法
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- 默认使用最新浏览器 -->
<meta http-equiv="Cache-Control" content="no-siteapp">
<!-- 不被网页(加速)转码 -->
<meta name="robots" content="index,follow">
<!-- 搜索引擎抓取 -->
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- 删除苹果默认的工具栏和菜单栏 -->
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<!-- 设置苹果工具栏颜色 -->
<!-- 忽略浏览器自动识别数字为电话号码 -->
<meta name="format-detection" content="telephone=no">
<meta name="format-detection" content="date=no">
<meta name="format-detection" content="address=no">
<!-- 忽略浏览器自动识别邮箱账号 -->
<meta name="format-detection" content="email=no">
关闭iOS上的内容识别
<meta name="renderer" content="webkit|ie-comp|ie-stand">
对于多核浏览器,控制浏览器以哪种类型内核来显示,好像是 360 浏览器首先主导的
怎么使用
1.HTML 标签里的元素名不区分大小写
例如,<title>
标签可以写成 <Title>
,<TITLE>
或以任何其他方式。以下的几种写法都是可以的。
<div>我是div</div>
<DIV>我是DIV</DIV>
<div>我是DIV</DIV>
2. 闭合标签
大家都知道 html 元素都是使用start tag 开始,end tag结束。但是。。。
- 只有start tag
<body>
<div>1-我是div
<Div>2-我是DIV
</body>
会自动生成一个闭合end tag。同时,如果后继还有一个没有end tag的元素,会产生一个树状的结构依次布局。如上图。
第二种情况:
<body>
<div>我是 开合标签 DIV</DIV>
<div>1-我是div
<Div>2-我是DIV
<div>我是 开合标签 DIV</DIV>
</body>
会依次 生成一个子标签。
2. 只有end tag
<body>
我是文字
<div>我是div</div>
<div>我是DIV</DIV>
我是DIV-1</DIV>
我是DIV-2</div>
<DIV>我是DIV</DIV>
</body>
把只有end tag当作普通的文字处理。
3.非闭合标签
以 hr 为例:
<body>
</hr>
<hr>qq</hr>
</hr>
<hr>
<hr />
</body>
<hr>、<hr />
和<hr>qq<hr />
;均有效果,注意 <hr>qq<hr />
会在文字上产生一个横线。而</hr>
没有效果。
语义化标签
一个整体结构的语义类标签
<body>
<header>
<nav>
……
</nav>
</header>
<aside>
<nav>
……
</nav>
</aside>
<section>……</section>
<section>……</section>
<section>……</section>
<footer>
<address>……</address>
</footer>
</body>
优点是:
1)语义类标签对开发者更为友好,使用语义类标签增强了可读性,即便是在没有CSS的时候,开发者也能够清晰地看出网页的结构,也更为便于团队的开发和维护。
2)除了对人类友好之外,语义类标签也十分适宜机器阅读。它的文字表现力丰富,更适合搜索引擎检索(SEO),也可以让搜索引擎爬虫更好地获取到更多有效信息,有效提升网页的搜索量,并且语义类还可以支持读屏软件,根据文章可以自动生成目录等等
3)自然语境的补充
4)结构化文档的分级
语法
一个问题衍生出来:HTML5 为什么只需要写 <!DOCTYPE HTML>
?
HTML5 不基于 SGML,因此不需要对DTD进行引用,但是需要doctype来规范浏览器的行为(让浏览器按照它们应该的方式来运行);
而HTML4.01基于SGML,所以需要对DTD进行引用,才能告知浏览器文档所使用的文档类型。
基本语法
首先,HTML作为SGML的子集,它遵循SGML的基本语法:包括标签、转义等。
SGML还规定了一些特殊的节点类型,在我们之前的DOM课程中已经讲过几种节点类型,它们都有与之对应的HTML语法,我们这里复习一下:
标签语法
标签语法产生元素,我们从语法的角度讲,就用“标签”这个术语,我们从运行时的角度讲,就用“元素”这个术语。
HTML中,用于描述一个元素的标签分为开始标签、结束标签和自闭合标签。开始标签和自闭合标签中,又可以有属性。
-
开始标签:
<tagname>
- 带属性的开始标签:
<tagname attributename="attributevalue">
- 带属性的开始标签:
-
结束标签:
</tagname>
-
自闭合标签:
<tagname />
HTML中开始标签的标签名称只能使用英文字母。
这里需要重点讲一讲属性语法,属性可以使用单引号、双引号或者完全不用引号,这三种情况下,需要转义的部分都不太一样。
属性中可以使用文本实体(后文会介绍)来做转义,属性中,一定需要转义的有下面几种。
- 无引号属性:
<tab>
<LF>
<FF>
<SPACE>
&
五种字符。 - 单引号属性:
'
&
两种字符。 - 双引号属性:
\"
&
两种字符。
一般来说,灵活运用属性的形式,是不太用到文本实体转义的。
文本语法
在HTML中,规定了两种文本语法,一种是普通的文本节点,另一种是CDATA文本节点。
文本节点看似是普通的文本,但是,其中有两种字符是必须做转义的:<
和 &
。
如果我们从某处拷贝了一段文本,里面包含了大量的 <
和 &
,那么我们就有麻烦了,这时候,就轮到我们的CDATA节点出场了。
CDATA也是一种文本,它存在的意义是语法上的意义:在CDATA节点内,不需要考虑多数的转义情况。
CDATA内,只有字符组合]]>
需要处理,这里不能使用转义,只能拆成两个CDATA节点。
注释语法
HTML注释语法以<!--
开头,以-->
结尾,注释的内容非常自由,除了-->
都没有问题。
如果注释的内容一定要出现 -->
,我们可以拆成多个注释节点。
ProcessingInstruction语法(处理信息)
ProcessingInstruction多数情况下,是给机器看的。HTML中规定了可以有ProcessingInstruction,但是并没有规定它的具体内容,所以可以把它视为一种保留的扩展机制。对浏览器而言,ProcessingInstruction 的作用类似于注释。
ProcessingInstruction 包含两个部分,紧挨着第一个问号后,空格前的部分被称为“目标”,这个目标一般表示处理 ProcessingInstruction 的程序名。
剩余部分是它的文本信息,没有任何格式上的约定,完全由文档编写者和处理程序的编写者约定。
DTD(文档类型定义)
现在我们来讲一下DTD,DTD的全称是Document Type Defination,也就是文档类型定义。SGML用DTD来定义每一种文档类型,HTML属于SGML,在HTML5出现之前,HTML都是使用符合SGML规定的DTD。
如果你是一个上个时代走过来的前端,一定还记得HTML4.01有三种DTD。分别是严格模式、过渡模式和frameset模式。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
严格模式的DTD规定了HTML4.01中需要的标签。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
过渡模式的DTD除了html4.01,还包含了一些被贬斥的标签,这些标签已经不再推荐使用了,但是过渡模式中仍保留了它们。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
frameset结构的网页如今已经很少见到了,它使用frameset标签把几个网页组合到一起。
众所周知,HTML中允许一些标签不闭合的用法,实际上这些都是符合SGML规定的,并且在DTD中规定好了的。但是,一些程序员喜欢严格遵守XML语法,保证标签闭合性,所以,HTML4.01又规定了XHTML语法,同样有三个版本:
版本一
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
版本二
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
版本三
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
其实你看看就知道,这些复杂的DTD写法并没有什么实际作用(浏览器根本不会用SGML引擎解析它们),因此,到了HTML5,干脆放弃了SGML子集这项坚持,规定了一个简单的,大家都能记住的DTD:
<!DOCTYPE html>
但是,HTML5仍然保留了HTML语法和XHTML语法。
文本实体
不知道你注意到没有,HTML4.01的DTD里包含了一个长得很像是URL的东西,其实它是真的可以访问的——但是W3C警告说,禁止任何浏览器在解析网页的时候访问这个URL,不然W3C的服务器会被压垮。我相信很多好奇的前端工程师都把它下载下来打开过。
这是符合SGML规范的DTD,我们前面讲过,SGML的规范十分复杂,所以这里我并不打算讲SGML(其实我也不会),但是这不妨碍我们了解一下DTD的内容。这个DTD规定了HTML包含了哪些标签、属性和文本实体。其中文本实体分布在三个文件中:HTMLsymbol.ent HTMLspecial.ent和HTMLlat1.ent。
所谓文本实体定义就是类似以下的代码:
<
>
&
每一个文本实体由&
开头,由;
结束,这属于基本语法的规定,文本实体可以用#
后跟一个十进制数字,表示字符Unicode值。除此之外这两个符号之间的内容,则由DTD决定。
我这里数了一下,HTML4.01的DTD中,共规定了255个文本实体,在如下位置:
https://www.w3school.com.cn/tags/html_ref_symbols.html
浏览器是如何解析请求回来的HTML代码,DOM树又是如何构建的?
解析代码
1.词(token)是如何被拆分的
首先我们来看看一个非常标准的标签,会被如何拆分:
<p class="a">text text text</p>
如果我们从最小有意义单元的定义来拆分,第一个词(token)是什么呢?显然,作为一个词(token),整个p标签肯定是过大了(它甚至可以嵌套)。
那么,只用p标签的开头是不是合适吗?我们考虑到起始标签也是会包含属性的,最小的意义单元其实是“<p” ,所以“ <p” 就是我们的第一个词(token)。
我们继续拆分,可以把这段代码依次拆成词(token):
• <p“标签开始”的开始;
• class=“a” 属性;
• > “标签开始”的结束;
• text text text 文本;
• </p>
标签结束。
2.状态机
举个例子:
区分 “< ”和 “非<”:
• 如果获得的是一个非<字符,那么可以认为进入了一个文本节点;
• 如果获得的是一个<字符,那么进入一个标签状态。
当然了,我们这里的分析比较粗略,真正完整的HTML词法状态机,比我们描述的要复杂的多。
不过当我们在标签状态时,则会面临着一些可能性。
• 比如下一个字符是“ ! ” ,那么很可能是进入了注释节点或者CDATA节点。
• 如果下一个字符是 “/ ”,那么可以确定进入了一个结束标签。
• 如果下一个字符是字母,那么可以确定进入了一个开始标签。
• 如果我们要完整处理各种HTML标准中定义的东西,那么还要考虑“ ? ”“% ”等内容。
构建DOM树
前面我们的词(token)中,以下两个是需要成对匹配的:
• tag start
• tag end
一个简单的构建DOM树过程:
• 栈顶元素就是当前节点;
• 遇到属性,就添加到当前节点;
• 遇到文本节点,如果当前节点是文本节点,则跟文本节点合并,否则入栈成为当前节点的子节点;
• 遇到注释节点,作为当前节点的子节点;
• 遇到tag start就入栈一个节点,当前节点就是这个节点的父节点;
• 遇到tag end就出栈一个节点(还可以检查是否匹配)。
执行顺序 思路,有点像leetcode的有效的括号。下面有个简单的视频做演示。
https://www.yuque.com/presbyter/ysvc23/bpo4b2#97b884e1
栈构造 DOM 树的全过程
参考
- 现代浏览器工作原理
- 重学前端(winter):极客时间
- 浏览器工作原理与实践 (李兵):极客时间
- MDN
- Web Fundamentals