JavaWeb 复习讲义
原始讲义
第一部分 基础篇:从一张网页说起

第1章 Web 导论与 HTML

难度:★☆☆☆☆ | 前置知识:Java 基础语法(会写类、方法、变量即可,不需要任何前端基础) | 本章解决:能看懂并手写一个结构正确、能提交数据的 HTML 页面,并在脑子里建立"页面只是数据的外壳"这一整体观

本章导读

原文的小节编号与本章编号并不一致(原文的"1.1"指的是"HTML 的第 1 节",不是"第 1 章第 1 节")。为方便你对照原讲义复习,先给出映射表:

原文小节原文内容本章位置
0 JavaWeb 介绍Web/JavaWeb 定义、Java 十大领域、三者分工、Web 核心课程体系1.1
1.1 介绍HTML 定义、XML 是什么、XML 与 HTML 的区别、W3C 标准1.2
1.2 快速入门.html 后缀、结构标签、font 变色、五条总结1.3
1.3 基础标签h1~h6、hr、font、br、p、b/i/u、center、转义字符1.4
1.4 图片、音频、视频标签img/audio/video、尺寸单位、绝对路径与相对路径1.5
1.5 超链接标签a 的 href 与 target1.6
1.6 列表标签ol/ul/li 与 type1.7
1.7 表格标签table/tr/td/th、border、cellspacing、rowspan、colspan1.8
1.8 布局标签div 与 span 的换行差异1.9
1.9 表单标签form 的 action 与 method、name 的必要性1.10
1.10 表单项标签input 各 type、label、select、textarea、三条注意1.11
(原文未讲)HTML5 语义标签1.12

1.1 Web 与 JavaWeb

1.1.1 两个名词:Web 与 JavaWeb

Web:全球广域网,万维网(www)。

JavaWeb:用 Java 技术解决相关 Web 互联网领域的技术栈。

这两句话是定义,考试可以直接写。但要把它们读厚一点:Web 不是"互联网"的同义词。互联网是那张物理的网(网线、路由器、光缆),Web 是跑在这张网上的一种信息服务方式:你用浏览器输入一个地址,浏览器向某台服务器发一个 HTTP 请求,服务器把一段 HTML 文本发回来,浏览器再把它画成页面。整件事的三个角色就是:浏览器(客户端)、HTTP(传输方式)、服务器(服务端)。第 4 章讲 HTTP,第 5 章讲服务器那头的 Tomcat 和 Servlet。

"JavaWeb 是技术栈"这句话里的"栈"字要留意:它是一组技术叠在一起形成的整体。HTML/CSS/JS 在最上面(用户看得见的那层),HTTP/Tomcat/Servlet 在中间(请求怎么进来、怎么被处理),JDBC/MySQL 在最下面(数据存哪儿)。本章只碰最上面那层,但你要知道下面还有两层撑着。

你身边那些网站(百度、阿里、腾讯、字节、京东、华为、滴滴、美团、小米)背后大量系统就是这套栈写出来的。这份名单要说明的是一件事:这套技术属于工业界的主流选择之一,它并不是课堂上的教学玩具。

1.1.2 为什么对 Java 情有独钟:十大应用领域

Java 语言程序员人数众多,社区和资源丰富,Java 的应用领域几乎涉及社会生活的方方面面。概括来讲有以下十个方面:

  1. 嵌入式领域
  2. 软件工具开发
  3. 大数据技术
  4. Web 应用系统开发
  5. 企业级应用开发
  6. 科学应用
  7. 手机 App
  8. 电子商务领域
  9. 电子政务领域
  10. 游戏开发

这份清单的用处在于让你明白为什么这门课值得学,不在于让你逐条背下来:第 4 项"Web 应用系统开发"和第 5 项"企业级应用开发"就是 JavaWeb 的主战场,第 8、9 项(电商、电子政务)是它最典型的落地场景。凡是"很多人用浏览器访问、后台要连数据库、还要分权限管数据"的系统,基本都采用这个固定流程。

【考点】

JavaWeb 与"前端开发"的区别在分工,不在语言。结论:JavaWeb 是服务端技术栈,负责接收请求、处理业务逻辑、访问数据库、组织响应;前端负责把响应变成用户看得见、点得动的界面。理由:浏览器的能力边界决定了页面只能"展示 + 采集",校验、计算、持久化必须放到服务器上做,否则用户改一行源码就能绕过所有限制。

1.1.3 网页、数据库、JavaWeb 程序:一条数据的三段旅程

原文用三行字给出了全书最重要的一张分工表:

角色职责具体做什么
网页展现数据把文字、图片、表格、表单画出来,让用户看得见、填得进
数据库存储和管理数据把数据长期、结构化地存起来,支持增删改查
JavaWeb 程序逻辑处理接收请求、校验数据、调用数据库、组织响应

这三行不能只当口号背,跟着一条数据走一遍就明白了。以"用户注册"为例:

  1. 用户在注册页填好用户名、密码,点"免费注册"。此时数据还只在浏览器里,一个字符都没到服务器。这一层是网页。
  2. 浏览器按 form 的 action 和 method 把数据打包,发一个 HTTP 请求到服务器。请求到达 Tomcat,被某个 Servlet 接住。这一层是 JavaWeb 程序的入口。
  3. Servlet 里做判断:用户名是不是空的?两次密码一样吗?用户名是不是已经被注册了?上面这些判断叫逻辑处理,只能放在服务器上,因为放在页面上用户能改。这一层还是 JavaWeb 程序。
  4. 校验通过,程序用 JDBC 发一条 insert 语句,把这条记录写进 MySQL 表。这一层是数据库。
  5. 用户下次登录,程序用 select 去数据库里查这条记录,把查到的结果交给页面渲染出来。又回到网页。

于是主线就出来了:网页只负责展示数据,JavaWeb 程序负责逻辑处理,数据库负责存储。任何一环缺失,系统都不成立:只有页面没有程序,是静态海报;只有程序没有数据库,重启就丢数据;只有数据库没有页面,用户没法用。

【补充】

三者之间的"胶水"是协议和路径,本章要认准三处:form 的 action 决定数据发给谁(URL),method 决定数据放在哪儿发(URL 后面还是请求体里),name 决定数据叫什么名字(键)。到第 6 章,服务器端就是靠这个"名字"取值:request.getParameter("username") 里的 "username" 必须和本章 <input name="username"> 一字不差,否则取到的是 null。这是初学者最常遇到的"页面明明填了,后台却是 null",根因就在本章。

1.1.4 Web 核心课程体系与本复习文档的章节对照

原讲义给了一张课程体系图,把整个 Web 核心拆成"前端 / Web 核心(服务端)/ 数据库"三块。这张图信息量大但排版零散,这里重排成目录树,并在每一行后面标出它对应本复习文档的第几章。这张图就是你整学期的地图,复习时照着它点章节即可:

Web 核心
├── 前端(浏览器里跑的东西)
│   ├── HTML + CSS ................ 第1章(HTML)、第2章(CSS)
│   ├── JavaScript ................ 第3章
│   └── Ajax + Vue + ElementUI .... 本复习文档不展开(实验课按需查阅)
├── 服务端(Java 程序跑的地方)
│   ├── Tomcat + HTTP + Servlet ... 第4章(HTTP)、第5章(Tomcat 与 Servlet)
│   ├── Request + Response ........ 第6章
│   ├── JSP ....................... 第8章
│   ├── Cookie + Session .......... 第7章
│   ├── Filter + Listener ......... 第9章
│   └── 综合案例 .................. 第11章
└── 数据库(数据躺着的地方)
    ├── MySQL ..................... 第10章
    ├── JDBC ...................... 第10章
    ├── MyBatis ................... 本复习文档不展开
    └── Maven ..................... 本复习文档不展开

按"由静到动、由单机到联网"的顺序读这张图,本章你只点亮了第一行第一个格子。但它下面每一个技术,最终都要和 HTML 打交道:Servlet 要把 HTML 写进响应(第 6 章),JSP 本身就是"能写 Java 的 HTML"(第 8 章),Filter 拦的是对 HTML 页面的请求(第 9 章),综合案例是把表单数据一路送到数据库再渲染回页面(第 11 章)。HTML 是这套栈里唯一一个所有人都要经过的路口。

【对比】

网页 / JavaWeb 程序 / 数据库三者最容易被混淆的地方是"谁做校验"。对照记:页面上的校验(比如 required、maxlength)叫客户端校验,只为了体验好、少发一次请求,不能当作安全措施,因为用户可以禁用 JS、可以用工具直接发请求;服务器上的校验(Servlet 里写 if)叫服务端校验,才是真正有效的那道门。考试问"表单校验应该在哪端做",标准答案是"两端都做,但以服务端为准"。

1.2 HTML 介绍

1.2.1 什么是 HTML:超文本 + 标记语言

HTML 是一门语言,所有的网页都是用 HTML 这门语言编写出来的。像京东、12306 等网站,动辄成千上万个页面,每一页的结构都由 HTML 描述。

它的专业全称是:

HTML(Hyper Text Markup Language):超文本标记语言。

这个长长的名字拆成两半,正好是它的两个本质特征。

第一半:超文本(Hyper Text)。 超文本指超越了文本的限制,比普通文本更强大:除了文字信息,还可以定义图片、音频、视频等内容。你打开一个新闻首页,除了能看到标题和正文这些文字,还能看到大图、视频播放器、音频条:这种展示效果已经远远超出了"文本"能表达的范围,所以叫"超"文本。注意"超文本"还暗含另一层意思:文本之间可以互相跳转,一篇文档能链到另一篇文档,这就是 1.6 节要讲的超链接,也是"万维网"之所以能"织"成一张网的原因。

第二半:标记语言(Markup Language)。 标记语言指由标签构成的语言。之前学过的 XML 就是标记语言,由一个一个的标签组成;HTML 也是由标签组成。你在浏览器页面里右键"查看网页源代码",看到的就是密密麻麻的标签:每一个 <...> 都是一次"标记":这块是标题,那块是图片,这里是表格。

但是 HTML 的标签和 XML 有一个决定性的不同:HTML 中的标签都是预定义好的,运行在浏览器上并由浏览器解析,然后展示出对应的效果。你不能自己发明一个 <mytag> 让浏览器认识它。想展示图片,就得用预定义的 img 标签;想展示可以点击的链接,就得用预定义的 a 标签;想做输入框,就得用 input。所以原文那句话说得非常准:学习 HTML,其实就是学习这些预定义的标签。

HTML 预定义了很多标签,由于我们是 Java 工程师、是做后端开发的,所以不会每个都学习,页面开发是有专门的前端工程师来做的。那为什么还要学?因为在公司中或多或少也会涉及到前端开发。更直接的现实理由是:后端要能给页面递数据,就必须看得懂页面结构;第 8 章写 JSP 时,你是在 HTML 里嵌 Java 代码,不认标签根本写不下去。

开发流程是这样的:以后我们是通过 Java 程序从数据库中查询出来数据,然后交给页面进行展示,这样用户就能在浏览器通过页面看到数据。注意这个方向:数据是从数据库流向页面的,页面本身不产生数据(除了用户输入)。 这个方向很重要,它解释了为什么"页面写得再漂亮,也变不出数据库里没有的数据"。

【易错点】

最典型的错误认知是"HTML 是编程语言"。判断标准很简单:能不能做计算和判断。HTML 里没有变量、没有循环、没有 if、没有函数,它只能描述"这里有什么",不能描述"先做什么再做什么"。所以它叫标记语言(Markup Language),不叫编程语言(Programming Language)。真正让页面"会算、会判断"的是第 3 章的 JavaScript,让服务器"会算、会判断"的是 Java。

1.2.2 什么是 XML

在讲 HTML 之前先把 XML 说清楚,因为两者的对比是考试常客。

"可扩展"三个字是关键:XML 没有预定义标签,<student>、<price>、<book> 全是你自己起的名字。正因为名字可以随便起,XML 才能描述任意结构的数据,这也是它能当"数据传输格式"的原因。但代价是:浏览器看到 <student> 不知道该怎么画,因为它不在预定义标签表里。所以 XML 天生不适合做展示。

一个最小的 XML 长这样:

<?xml version="1.0" encoding="UTF-8"?>
<students>
    <student>
        <name>张三</name>
        <age>20</age>
    </student>
</students>

注意它严格遵守三条规矩:有且只有一个根元素(<students>)、每个标签都必须闭合、标签必须正确嵌套。这三条在 HTML 里全部可以违反:这就是下一小节要讲的"语法严格程度"差异。

1.2.3 XML 与 HTML 的四条区别

原文给出三条,这里补足成四条,因为第四条在考试里出现频率很高(大小写问题):

比较项HTMLXML
标签能否自定义标签不能自定义,只能用预定义标签标签必须自定义,且命名要有含义
语法严格程度语法要求不严格(松散)语法要求极其严格
用途用来展示数据用来传输和存储数据
大小写标签不区分大小写,<font> 与 <Font> 等价标签区分大小写,<name> 与 <Name> 是两个不同标签

第三条是最本质的:HTML 是给人看的,XML 是给程序读的。 给人看的东西要容错(用户不该因为少写一个结束标签就看到一片空白),给程序读的东西必须严格(少一个字符就该报错,否则解析出来的数据是错的)。

前两条是现象,第四条是"严格"的一个具体表现。把这张表归纳成一条:XML 严谨而自由(标签自定义),HTML 宽松而死板(标签固定)。

【对比】

"HTML 语法松散"到底松到什么程度?三件事都能跑:① 标签不闭合,<font color="red">abc 后面不写 </font>,浏览器会自动帮你补;② 标签大小写混写,<FONT>、<Font>、<font> 效果一样;③ 属性值不加引号,<font color=red> 也能识别(但不建议)。但松散不等于正确:不闭合的标签会让 DOM 树在你意想不到的地方嵌套错位,第 2 章写 CSS 选择器、第 3 章用 JS 取元素时就会莫名其妙选不中。所以从第一天起就按严格写法写,让浏览器的容错能力只用于"兜底",不用于"偷懒"。

1.2.4 W3C 标准:结构、表现、行为

W3C 是万维网联盟,这个组织是用来定义标准的。 他们规定了一个网页由三部分组成:

层对应语言管什么生活化的比喻
结构(Structure)HTML定义页面的整体结构:有哪些内容、谁包含谁建筑的承重结构
表现(Presentation)CSS美化页面,让页面看起来更加美观墙面的涂料、家具、灯
行为(Behavior)JavaScript使网页动起来电灯开关、自动门、电梯

原文用三个递进的页面说明了这个分工。只用 HTML 写出来的页面,内容已经全了:标题、段落、图片、链接都在,但页面是比较丑的:字体是浏览器默认的宋体或 Times,所有内容从上到下一条条排着,没有颜色也没有留白。加上 CSS 之后,瞬间感觉好看多了:有背景色、有圆角、有居中、有间距。这就是 CSS 的作用,用来美化页面。再加上 JavaScript,页面上多出了轮播图:多张图片会自动切换,切换的时候还有过渡动画。

这个三段式不是分类癖,它是一条工程原则,叫结构、表现、行为分离:

如果三者混在一起写(比如用 font 标签直接给每个字上色,用 onclick 属性把逻辑写进标签里),那么改一处颜色要翻遍全站每一行。这正是 1.4.4 节说 font 标签"已经不建议使用"的根本原因。

本章学的是三层里的第一层。第 2 章接第二层,第 3 章接第三层,顺序和 W3C 的排列完全一致。

【补充】

W3C 是 1994 年由 Tim Berners-Lee(万维网的发明者)在 MIT 创建的。HTML 最早的版本由他 1991 年在 CERN 提出,之后由 W3C 接管标准化;我们现在用的 HTML5 在 2014 年成为 W3C 正式推荐标准。这些年份不必背,但"标准由 W3C 制定"这一点要记住,因为浏览器厂商都按这套标准实现,你写的页面才能在 Chrome、Edge、Firefox 里表现一致。这也解释了一个常见现象:某些老页面在 IE 里正常、在新版 Chrome 里错位,原因是它当年依赖了某个非标准的私有写法。

1.2.5 【补充】浏览器到底怎么"解析"HTML

原文说 HTML "运行在浏览器上并由浏览器解析",但没有展开"解析"是什么。这一步不搞清楚,后面学 DOM、学 CSS 选择器、学 JavaScript 取元素,全是空中楼阁。

浏览器拿到 HTML 文本后,大致做四步:

  1. 字节 → 字符:按 <meta charset="UTF-8"> 声明的编码把一堆二进制字节翻译成字符。编码声明写错,中文就变成乱码,这是初学者第一个容易出错的地方。
  2. 字符 → 词法单元(Token):一边扫字符一边切分,识别出"开始标签 <div>""属性 class="box"""文本""结束标签 </div>",就像把一段文本切成词。
  3. 词法单元 → 节点(Node):每个标签变成一个元素节点,每段文字变成一个文本节点。
  4. 节点 → 树(DOM 树):按嵌套关系把节点挂到一棵树上。<html> 是树根,<head> 和 <body> 是它的两个孩子,<body> 里的每个 <div>、每个 <p> 又是它自己的孩子。

以本章 1.3 节那个最简单的页面为例:

<html>
    <head>
        <title>html 快速入门</title>
    </head>
    <body>
        <font color='red'>I love Sanda~</font>
    </body>
</html>

浏览器解析后得到的树是这样的:

document
└── html
    ├── head
    │   └── title
    │       └── "html 快速入门"(文本节点)
    └── body
        └── font(属性 color="red")
            └── "I love Sanda~"(文本节点)

这棵树就是 DOM(Document Object Model,文档对象模型)。 它有三个直接后果,全都影响后面的章节:

【考点】

"浏览器解析 HTML 得到什么?"标准答案:得到一棵 DOM 树(文档对象模型),根节点是 document,其下是 html,再分为 head 与 body。理由:只有把扁平的文本变成有层次的对象树,程序(CSS 引擎、JavaScript)才能按"父子、兄弟"的关系去查找和修改页面内容。

1.3 快速入门

1.3.1 三步写出第一个页面

需求:做一个最简单的页面,浏览器内容区显示一行字。

要实现这个页面,从以下三步进行:

第一步:新建文本文件,后缀名改为 .html。

页面文件的后缀名是 .html,所以需要该后缀名。这一步看着 trivial,是新手第一个卡点:Windows 默认隐藏已知文件类型的扩展名,你新建"新建文本文档.txt"再改名成"index.html",实际文件名可能还是 index.html.txt,双击打开就还是记事本。正确做法是先在资源管理器的"查看"里勾上"文件扩展名",或者用 IDEA 直接新建 HTML 文件。

第二步:编写 HTML 结构标签。

HTML 是由一个一个的标签组成的,但它也有一些用于表示结构的标签。原文给出的结构框架(PDF 抽取时被劈成了两栏,这里还原):

<html>
    <head>
        <title></title>
    </head>
    <body>

    </body>
</html>

html 标签是根标签,下面有 head 标签和 body 标签这两个子标签。而 head 标签的 title 子标签是用来定义页面标题名称的,它定义的内容会展示在浏览器的标题栏(标签页)位置。body 标签的内容会被展示在内容区中,也就是你在页面上真正看到的那一大片白色区域。

四个标签的分工概括如下:html 是总壳子,head 是"给浏览器看的说明书"(标题、编码、引入的 CSS),body 是"给用户看的内容"。你写的所有可见内容都必须放在 body 里,写进 head 里的文字是不会显示在页面上的。

第三步:在 body 中定义文字。

代码如下:

<html>
    <head>
        <title>html 快速入门</title>
    </head>
    <body>
        I love Sanda
    </body>
</html>

用浏览器打开这个文件,你会看到:浏览器标签页上显示"html 快速入门",页面内容区左上角显示"I love Sanda"。标题是加粗的默认字号,正文是默认的黑色小字,页面没有任何留白和装饰。这就是"只有结构、没有表现"的原始样子,也就是 1.2.4 节说的"比较丑"的那个阶段。

1.3.2 让文字变红:font 标签的 color 属性

你在访问其他网站页面时会看到字体颜色是五颜六色的。我们可以改字体颜色吗?当然可以。

font 标签就可以使用,该标签有一个 color 属性可以设置字体颜色,比如把 color 设成 red,文字就变成了红颜色。我们只需要将需要变成红色的文字放在标签体部分(也就是 <font> 和 </font> 之间)就可以了:

<html>
    <head>
        <title>html 快速入门</title>
    </head>
    <body>
        <font color='red'>I love Sanda~</font>
    </body>
</html>

刷新页面,"I love Sanda~"就变成红色了。注意这里 color 的取值 'red' 用的是单引号,这一点在下一小节会变成一条规则。

1.3.3 五条必须记住的规则

原文把入门要点做成了一组"总结",逐条列出并补上一句原因,这样你就不用死记:

#规则说明与原因
1HTML 文件以 .htm 或 .html 为扩展名两个都合法,.html 是主流写法;浏览器和服务器靠扩展名(或 Content-Type)判断这是网页而不是纯文本
2必须有 HTML 结构标签html/head/title/body 构成页面结构的主体;缺了浏览器会帮你补,但补出来的结构未必是你想要的
3HTML 标签不区分大小写上例中的 font 写成 Font 也一样可以展示出对应的效果。因为 HTML 的标签名在解析时会被统一转成小写
4HTML 标签属性值单双引皆可上例中 color 属性值使用双引号也是可以的:<font color="red">。但属性值内部含引号时要换另一种引号,或使用转义字符
5HTML 语法松散比如 font 标签不加结束标签也是可以展示出效果的。但建议你在写的时候还是不要这样做,严格按照要求去写

第 3 条和第 4 条合起来说明一件事:浏览器对 HTML 极度宽容。这既是好事(写错一点不至于白屏),也是坏事(错了不报错,等到第 2 章样式不生效、第 3 章 JS 取不到元素时才发现,而且很难定位)。所以第 5 条的态度是:能容错,但不要依赖容错。

【易错点】

规则 3 有个致命例外:标签名不区分大小写,但属性值区分。<input type="TEXT"> 里 TEXT 不是合法取值(合法的是小写 text),浏览器会把它当成未知类型,退化成默认的文本框;而 <input TYPE="text"> 反而完全正常。记法:标签名和属性名随便大小写,属性值必须按标准写小写。另外自定义属性(如 data-*)的值在 JS 里取值时也是区分大小写的。

1.4 基础标签

基础标签就是一些和文字相关的标签。原文列出了八组,这里逐一讲解,顺序按"从大到小、从结构到修饰"排:先标题,再分隔线,再字体,再换行与段落,最后加粗斜体下划线与居中。

1.4.1 准备工作:模块与页面文件

在 IDEA 中创建模块。因为现在不需要写 Java 代码,所以 src 目录可以删除掉。在模块下创建一个 html 文件夹,今天所有的页面文件都放在该文件夹下。模块目录如下:

module01
└── html
    ├── 01-基础标签.html
    ├── img
    │   ├── a.jpg
    │   └── aa.jpg
    ├── b.mp3
    └── c.mp4

然后选中 html 文件夹右键创建页面文件(01-基础标签.html)。创建好后 IDEA 会自动加上结构标签,我们只需要在 body 标签中书写标签即可。IDEA 生成的默认结构是:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
</html>

比 1.3 节多了两行,这两行要认识:

1.4.2 标题标签:h1 ~ h6

标题标签用于表示各级标题,h1 最大,h6 最小,字号逐级递减。

<h1>我是标题  h1</h1>
<h2>我是标题  h2</h2>
<h3>我是标题  h3</h3>
<h4>我是标题  h4</h4>
<h5>我是标题  h5</h5>
<h6>我是标题  h6</h6>

浏览器里的效果是:六行字从上到下排列,每一行的字都比上一行小一点,并且全部加粗、各自独占一行、上下带一点间距。这就是"块级元素 + 默认样式"的样子。想快速打开看效果,IDEA 提供了快捷的打开方式(编辑器右上角会浮出几个浏览器的图标,点一下即可)。

1.4.3 hr 标签:一条横线

hr 标签在浏览器中呈现出横线的效果。

<hr>

它是一条占满整行宽度的灰色细线,上下各带一点间距。hr 是单标签(不需要结束标签),在 HTML5 里它的语义是"段落级的话题转换",但实际使用中主要就是当分隔线用。

1.4.4 字体标签:font

font 是字体标签,有三个属性:

属性作用取值
face用来设置字体如 "楷体"、"宋体"、"微软雅黑"
color设置文字颜色三种写法,见下表
size设置文字大小1~7 的整数,默认 3,数字越大字越大

color 的颜色有三种表示方式,这是考试喜欢考的小点:

写法示例取值范围说明
英文单词red、pink、blue一百多个预设单词这种方式表示的颜色特别有限,所以一般不用
rgb(值1,值2,值3)rgb(255,0,0)每个值 0~255三原色(红绿蓝)设置方式,书写起来比较麻烦,一般不用
#值1值2值3#ff0000每个值 00~FFrgb 方式的简化写法(十六进制),以后基本都用此方式。值1 表示红色的范围,值2 表示绿色,值3 表示蓝色

这三种写法表示的是同一个颜色:red = rgb(255,0,0) = #ff0000。因为 255 的十六进制正好是 FF,0 是 00,所以两种写法能一一对应。记住几个常用色值:#ff0000 红、#00ff00 绿、#0000ff 蓝、#ffffff 白、#000000 黑、#cccccc 浅灰。

代码演示:

<font face="楷体" size="5" color="#ff0000">上海杉达</font>

效果是:用楷体、比默认字号大两号、红色的"上海杉达"。

【补充】

注意: font 标签已经不建议使用了,以后如果要改变文字字体、大小、颜色,可以使用 CSS 进行设置。

【补充】 font 标签在 HTML4 就被标记为"不推荐(deprecated)",HTML5 中正式移除(虽然浏览器为兼容老页面仍然支持它)。它被淘汰的原因正是 1.2.4 节讲的"结构表现分离":font 把"表现"硬编码进了"结构",一个页面里可能有几百个 <font>,改一次主色调要全部重写。现代写法是在 <head> 里写 <style>p { color: #ff0000; font-family: 楷体; font-size: 18px; }</style>,一处定义、全站生效。这正是第 2 章的内容。考试如果问"font 能不能用",答"能,但已废弃,应改用 CSS"。

1.4.5 换行标签:br

先在页面文件里写两段文字(注意源码里是换行写的):

刚察草原绿草如茵,沙柳河水流淌入湖。藏族牧民索南才让家中,茶几上摆着馓子、麻花和水果,炉子上刚煮开的奶茶香气四溢……
6月8日下午,习近平总书记来到青海省海北藏族自治州刚察县沙柳河镇果洛藏贡麻村,走进牧民索南才让家中,看望慰问藏族群众。

在浏览器里看,并没有换行:两段文字连成了一大段。浏览器能"看见"你源码里的回车,但是 HTML 会把连续的空白字符(空格、Tab、换行)统统折叠成一个空格。所以在 HTML 里,源码怎么折行完全不影响显示,折行只是为了你自己好读。

如果要实现换行效果,需要使用换行标签(br 标签)。修改页面文件内容如下:

刚察草原绿草如茵,沙柳河水流淌入湖。藏族牧民索南才让家中,茶几上摆着馓子、麻花和水果,炉子上刚煮开的奶茶香气四溢……<br>
6月8日下午,习近平总书记来到青海省海北藏族自治州刚察县沙柳河镇果洛藏贡麻村,走进牧民索南才让家中,看望慰问藏族群众。

现在就有换行效果了。br 也是单标签,它只换行、不产生空行,也不会新起一个"段落"。

1.4.6 段落标签:p

上面文字用 br 展示的效果还是不太好:行与行之间贴得太紧,看不出"这是两段"。我们想让每一段上下都加空行,此时就需要使用段落标签(p 标签)。

<p>
刚察草原绿草如茵,沙柳河水流淌入湖。藏族牧民索南才让家中,茶几上摆着馓子、麻花和水果,炉子上刚煮开的奶茶香气四溢……
</p>
<p>
6月8日下午,习近平总书记来到青海省海北藏族自治州刚察县沙柳河镇果洛藏贡麻村,走进牧民索南才让家中,看望慰问藏族群众。
</p>

在浏览器展示的效果是:两段文字之间出现了一段明显的空白,呈现出"段落"的效果,比之前好一些。

br 与 p 的区别要分清楚:br 是行内的强制换行,段落还是同一段;p 是块级的段落容器,它自带上下外边距。文章正文用 p,诗歌、地址这类"必须在这个位置断行"的用 br。

1.4.7 加粗、斜体、下划线:b / i / u

<b>沙柳河水流淌</b><br>
<i>沙柳河水流淌</i><br>
<u>沙柳河水流淌</u><br>

三行字分别是粗体、斜体、带下划线。这三个都是行内标签,不会换行,所以原文在每行末尾补了 <br> 来强制断行。这个细节恰好印证了 1.4.5 节的规则。

【补充】

HTML5 更推荐用有语义的标签表达"强调":<strong> 表示重要(浏览器默认加粗),<em> 表示强调语气(浏览器默认斜体)。b/i 只表示"看起来粗/斜",strong/em 表示"内容重要/需要强调",后者对屏幕阅读器和搜索引擎更友好。u 要慎用:在网页里带下划线的文字会被用户误认为是超链接,这是可用性上的忌讳。

1.4.8 居中标签:center

center 标签让内容文本居中。

<hr>
<center>
    <b>沙柳河水流淌</b>
</center>

效果是:一条横线下面,加粗的那行字居中显示。center 会让它内部的所有内容(包括块级元素)整体居中。

【补充】

center 标签和 font 一样已废弃,HTML5 中移除。现代做法是给元素加 CSS:text-align: center;(让内部的行内内容居中)或 margin: 0 auto;(让一个有固定宽度的块级盒子自己居中)。两者的区别第 2 章讲盒模型时会非常清楚。原文保留 center 是为了配合老讲义的例子,你写作业时用哪个都行,但要知道哪个是"现在该用的"。

1.4.9 转义字符(实体)

原文的 1.3.8 节给了一个综合案例,要求用学过的基础标签做出一个页面,页脚"版权所有"那一行里有特殊字符,需要使用转义字符。PDF 抽取时那张表丢了,这里补全。常用的转义字符如下:

转义字符显示结果说明
&nbsp;一个空格不换行空格(non-breaking space),HTML 里连续空格会被折叠,想显示多个空格就用它
&lt;<小于号,left than
&gt;>大于号,greater than
&amp;&和号,ampersand
&quot;"双引号
&apos;'单引号
&copy;©版权符号,copyright
&reg;®注册商标,registered
&trade;™商标
&times;×乘号
&divide;÷除号
&yen;¥人民币符号
&mdash;—长破折号
&hellip;…省略号

为什么必须用转义字符?因为 <、>、& 这三个字符在 HTML 里有语法含义:< 表示"一个标签开始了",& 表示"一个转义字符开始了"。你想显示一个 <,就必须写成 &lt;,否则浏览器会以为你要开标签,然后把后面的内容吃掉。这也解释了 1.4.5 节那条新闻里"……"为什么可以直接写:省略号不是保留字符,但写成 &hellip; 更保险。

案例的参考写法(只用本节学过的标签,页脚用 &copy;):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>基础标签案例</title>
</head>
<body>
    <center>
        <h1>上海杉达学院</h1>
        <hr>
        <p><b>沙柳河水流淌</b>,<i>刚察草原绿草如茵</i>,<u>奶茶香气四溢</u>。</p>
        <font face="楷体" size="4" color="#0066cc">信息科学与技术学院</font>
        <hr>
        <font size="2" color="#999999">&copy; 2024 版权所有&nbsp;&nbsp;上海杉达学院</font>
    </center>
</body>
</html>
【易错点】

有两个容易出错的地方特别常见。第一,转义字符末尾的分号不能省:&lt 多数浏览器能容错,但是 &amp 后面紧跟字母时会被误读(&ampersand 会被解析成 &ersand 之类)。第二,想知道某个符号的转义写法,不要背表:直接在页面里写 < 看它是否被吃掉,或者查 MDN 的"HTML 实体字符参考"。考试只考 &nbsp;、&lt;、&gt;、&amp;、&copy; 这几个,记住就够。

1.4.10 【补充】块级元素与行内元素(第 2 章盒模型的伏笔)

学完这一节的标签,你已经能感觉到它们分成两类脾气完全不同的家伙:

这不是巧合。两类元素的默认显示方式本来就不同:

比较项块级元素(block)行内元素(inline)
典型标签div、p、h1~h6、hr、ul、ol、li、table、formspan、a、b、i、u、font、label、strong、em
是否独占一行是,前后强制换行否,和相邻的行内元素排在同一行
宽度默认占满父容器的 100%由内容撑开,内容多宽就多宽
设置 width/height有效无效(设了也不生效)
上下 margin/padding有效,能把周围元素推开左右有效,上下的设置不撑开行高,会视觉重叠
能否嵌套可以包含块级和行内元素只能包含行内元素(a 在 HTML5 里例外,可以包块级)

这张表的用处是给你一个判断依据,不需要现在背下来:以后写 CSS 时遇到"我明明设了 width: 200px 却没反应",先看这个元素是不是行内元素。如果是行内元素,加一句 display: block;(或 inline-block)就好了。第 2 章的盒模型(content / padding / border / margin 四层)讲的就是"每个元素都是一个矩形盒子",而块级与行内的差别来自这两类盒子的排版规则不同。

【考点】

结论:块级元素独占一行、可设宽高,行内元素并排显示、宽高由内容决定。理由:浏览器默认样式表给不同标签预设了不同的 display 值(block 或 inline),而 display 决定了这个盒子在"普通流"里怎么摆放,这是 CSS 布局的第一条基础规则。

1.5 图片、音频、视频标签

1.5.1 三个媒体标签

网页能"超文本",靠的就是这三个标签把非文字内容放进页面:

标签作用支持的格式属性
img定义图片JPG、PNG、GIF、WebP 等src:规定显示图像的 URL(统一资源定位符);height:定义图像的高度;width:定义图像的宽度
audio定义音频MP3、WAV、OGGsrc:规定音频的 URL;controls:显示播放控件
video定义视频MP4、WebM、OGGsrc:规定视频的 URL;controls:显示播放控件

img 是单标签(写作 <img ...>,没有 </img>)。audio 和 video 是双标签,需要写成 <audio ...></audio> 和 <video ...></video>;如果不写 controls 属性,音频和视频虽然被加载了,但页面上什么都没有,用户看不到播放按钮,也就没法播。

完整示例:

<img src="../img/a.jpg" width="300" height="400">
<audio src="b.mp3" controls></audio>
<video src="c.mp4" controls width="500" height="300"></video>

浏览器里会看到:一张 300×400 的图片、一个带播放/暂停/进度条的音频控件、一个 500×300 的视频播放器。

两个实用细节:只写 width 不写 height 时,图片会等比缩放(不会变形);两个都写死,图片就会被拉伸变形。所以照片一般只写宽度,图标类才写死宽高。

1.5.2 尺寸单位:px 与百分比

height 和 width 有两种设置方式:

百分比的关键词是"父标签":它算的是父容器的宽度,不是浏览器窗口的宽度(除非父容器正好是 body)。所以一个 width: 50% 的图片,如果它被放在一个宽度 400px 的 div 里,它就是 200px 宽;把 div 改成 800px,图片自动变 400px。这是第 2 章做"自适应布局"的基础。

<img src="../img/a.jpg" width="50%">

1.5.3 资源路径之绝对路径

图片、音频、视频标签都有 src 属性,而 src 是用来指定对应的图片、音频、视频文件的路径。此处的图片、音频、视频就称为资源。资源路径有两种设置方式。

绝对路径:完整路径。 这里的绝对路径指的是网络中的绝对路径,格式为:

协议://ip地址:端口号/资源名称

例如:

<img src="https://th.bing.com/th/id/R.33674725d9ae34f86e3835ae30b20afe?rik=Pb3C9e5%2b%2b3a9Vw&riu=http%3a%2f%2fwww.desktx.com%2fd%2ffile%2fwallpaper%2fsenery%2f20180626%2f4c8157d07c14a30fd76f9bc110b1314e.jpg&ehk=9tpmnrrRNi0eBGq3CnhwvuU8PPmKuy1Yma0zL%2ba14T0%3d&risl=&pid=ImgRaw" width="300" height="400">

这里 src 属性的值就是网络中的绝对路径:https 是协议,th.bing.com 是主机名,后面是资源在服务器上的路径和一堆查询参数。它的好处是不管你的 HTML 放在哪儿都能显示,坏处是必须联网,而且对方服务器一改地址或做了防盗链,图就裂了。

【补充】

网页里还有第三种路径,叫"站内绝对路径":以 / 开头,如 <img src="/img/a.jpg">。它不以协议和域名开头,浏览器会自动补上"当前网站的域名",所以完整含义是 http://当前域名/img/a.jpg。这个写法在做 JavaWeb 项目时极其常用,因为项目部署后上下文路径可能变,用 / 开头的路径不受 HTML 文件所在层级影响。第 5 章部署到 Tomcat 后你会天天见到它。

1.5.4 资源路径之相对路径

相对路径:相对位置关系。 也就是找"页面"和"其他资源"之间的相对位置。

写法含义
./表示当前路径(可以省略不写)
../表示上一级路径
../../表示上两级路径

假设模块目录结构如下:

module01
└── html
    ├── 01-基础标签.html        ← 当前页面在这
    ├── aa.jpg                  ← 和页面同级
    └── img
        └── a.jpg               ← 在子目录里

在 01-基础标签.html 里找不同的图片,路径写法不同:

<!--在该页面找a.jpg,就需要先回到上一级目录,该级目录有img目录,进入该目录就可以找到a.jpg图片-->
<img src="../img/a.jpg" width="300" height="400">
<!--该页面和aa.jpg 是在同一级下,所以可以直接写 图片的名称,也可以写成 ./aa.jpg-->
<img src="aa.jpg" width="300" height="400">

等等,上面第一条注释和目录树对不上。按这棵树,img 目录就在 html 目录里面,和页面同级,所以正确的写法应该是 img/a.jpg 或 ./img/a.jpg,不需要 ../。原文注释写 ../img/a.jpg,是因为原讲义的目录结构里 img 目录和 html 目录是兄弟关系(都在模块根下):

module01
├── html
│   └── 01-基础标签.html        ← 当前页面在这
└── img
    └── a.jpg                  ← 和 html 目录同级,所以要 ../ 回到 module01 再进 img

这就是相对路径最容易出错的地方,也是考试最爱设陷阱的地方。把规则说死:

【易错点】

三个高频事故:① ./ 不能写成 . 或省略不写之外的形式,.. 也不能写成 ...;② Windows 资源管理器里复制出来的路径是反斜杠 D:\xxx\img\a.jpg,在 HTML 里必须改成正斜杠 img/a.jpg 或者用 file:///D:/xxx/img/a.jpg,反斜杠在 URL 里是转义符;③ 中文文件名和空格在本地能显示,部署到 Tomcat 后经常 404,项目里的资源一律用英文名。这三条记住,能省掉半个学期的"图片显示不出来"。

1.6 超链接标签

在网页中可以看到很多超链接:导航栏上的"首页""课程""关于我们",正文里蓝色的带下划线的词,图片点一下跳走。当我们点击这些超链接时会跳转到其他的页面或者资源。超链接使用的是 a 标签。

a 标签有两个属性:

属性作用取值
href指定访问资源的 URL(hypertext reference)任意 URL:网络地址、相对路径、#、mailto: 等
target指定打开资源的方式_self:默认值,在当前页面打开;_blank:在空白页面(新标签页)打开

代码演示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <a href="https://www.sandau.edu.cn/" target="_self">点我有惊喜</a>
</body>
</html>

把 target 改成 _blank,再点一次,链接就在新标签页里打开,原页面还在,这是导航到"站外"时的常规做法,因为用户不希望点一个外链就把自己的页面弄丢。

href 的几种常见取值,考试和实战都会遇到:

写法效果
href="https://www.sandau.edu.cn/"跳转到外部网站(绝对路径)
href="02-基础标签.html"跳转到同目录下的另一个页面(相对路径)
href="#"跳回当前页面顶部,不发请求(常配合 JS 做按钮)
href="#top"跳到页面内 id="top" 的元素处(锚点)
href="mailto:abc@qq.com"打开邮件客户端
href="javascript:void(0)"什么都不做,纯粹给 JS 挂点击事件用
【补充】

a 标签在 HTML5 里可以包裹块级元素(比如整张卡片、整块 div 都能被点),这在 HTML4 里是不允许的。另外 target 除了 _self/_blank,还有 _parent(在父框架打开)和 _top(在最顶层窗口打开),但只有用了 iframe 框架时才有意义,现代项目基本不用。考点就两个:_self 是默认、在当前页开;_blank 是新开标签页。

【易错点】 用 target="_blank" 打开外部链接时,新页面能通过 window.opener 反过来操纵你的页面(叫"钓鱼攻击")。标准做法是同时加上 rel="noopener noreferrer":<a href="..." target="_blank" rel="noopener noreferrer">。这个细节笔试不考,但实验报告和面试里是加分项。

1.7 列表标签

HTML 中的列表分为两种:

标签说明:

标签作用
ol定义有序列表(ordered list)
ul定义无序列表(unordered list)
li定义列表项(list item),必须放在 ol 或 ul 里面

type 属性在两处的含义不同:

原文只说了"有哪些类",没给具体取值,这里补齐:

标签type 取值效果
ol1(默认)1. 2. 3.
ola / Aa. b. c. / A. B. C.
oli / Ii. ii. iii.(小写罗马数字)/ I. II. III.
uldisc(默认)实心圆点 ●
ulcircle空心圆圈 ○
ulsquare实心方块 ■

代码演示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ol type="A">
        <li>咖啡</li>
        <li>茶</li>
        <li>牛奶</li>
    </ol>

    <ul type="circle">
        <li>咖啡</li>
        <li>茶</li>
        <li>牛奶</li>
    </ul>
</body>
</html>

浏览器效果:第一组是"A. 咖啡 / B. 茶 / C. 牛奶",第二组是三个前面带空心圆圈的"咖啡 / 茶 / 牛奶"。两组列表各占一块,因为 ol、ul、li 都是块级元素,每项独占一行,并且整体带缩进。

【补充】

无序列表是网页布局里用得最多的标签之一,因为导航栏本质就是一个无序列表:<ul><li><a href="#">首页</a></li>...</ul>,再用 CSS 把 li 变成横排(display: inline-block 或 float: left)、把默认的圆点去掉(list-style: none)。第 2 章做导航条时用的就是这个固定流程。这也说明一件事:HTML 负责"这是一组并列的项",CSS 负责"横着排还是竖着排"。

【易错点】 li 不能脱离 ol/ul 单独写,也不能直接放进 div 里当"列表"用。虽然浏览器容错会把它显示出来,但语义错了,第 2 章写 ul li 选择器时会选不中。另外 ol 的 start 属性可以指定起始编号(<ol start="3"> 从 3 开始),li 的 value 可以单独改某一项的编号。

1.8 表格标签

表格在页面上呈现为行列分明的网格,用来展示结构化数据(成绩单、商品列表、时刻表)。表格使用如下标签定义:

标签作用主要属性
table定义表格border:规定表格边框的宽度;width:规定表格的宽度;cellspacing:规定单元格之间的空白
tr定义行(table row)align:定义表格行内容的对齐方式
td定义单元格(table data)rowspan:规定单元格可横跨的行数;colspan:规定单元格可横跨的列数
th定义表头单元格(table header)内容默认加粗居中

代码演示(原文被双栏排版劈成了四段,这里还原成完整页面,并把被拆开的 <img ...> 标签补全):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <table border="1" cellspacing="0" width="500">
        <tr>
            <th>序号</th>
            <th>品牌logo</th>
            <th>品牌名称</th>
            <th>企业名称</th>
        </tr>
        <tr align="center">
            <td>010</td>
            <td><img src="../img/三只松鼠.png" width="60" height="50"></td>
            <td>三只松鼠</td>
            <td>三只松鼠</td>
        </tr>
        <tr align="center">
            <td>009</td>
            <td><img src="../img/优衣库.png" width="60" height="50"></td>
            <td>优衣库</td>
            <td>优衣库</td>
        </tr>
        <tr align="center">
            <td>008</td>
            <td><img src="../img/小米.png" width="60" height="50"></td>
            <td>小米</td>
            <td>小米科技有限公司</td>
        </tr>
    </table>

</body>
</html>

浏览器效果:一个 500px 宽、带 1px 边框、单元格之间没有缝隙的四列表格。第一行是表头(序号 / 品牌logo / 品牌名称 / 企业名称),文字加粗居中;下面三行的内容都水平居中,第二列是 60×50 的小 logo 图。

三个属性各管一件事,容易混,对照记:

th 与 td 的区别只有两点:th 里的文字默认加粗并居中,语义上表示"这一列/行的标题"。考试常问"表头用哪个标签",答案就是 th。

rowspan 和 colspan 是合并单元格,考试必考。原文只给了定义没给例子,这里补一个小例子:

<table border="1" cellspacing="0" width="300">
    <tr>
        <th>姓名</th>
        <th>科目</th>
        <th>成绩</th>
    </tr>
    <tr align="center">
        <td rowspan="2">张三</td>
        <td>语文</td>
        <td>90</td>
    </tr>
    <tr align="center">
        <td>数学</td>
        <td>95</td>
    </tr>
    <tr align="center">
        <td colspan="2">平均分</td>
        <td>92.5</td>
    </tr>
</table>

"张三"这个单元格用 rowspan="2" 纵向吃掉两行,所以下面那一行就只需要写"数学"和"95"两个格子,不能再写姓名;"平均分"这个单元格用 colspan="2" 横向吃掉两列,所以那一行只写两个格子。

【易错点】

合并单元格之后,同一行里写的 td 数量要相应减少,否则这一行会比别人多出一格,表格整体错位,这是手写表格最常见的 bug。记忆法:rowspan 往下吃,被吃的那几行少写格子;colspan 往右吃,被吃的那一行少写格子。

【补充】 用表格做页面布局(把整个页面切成几块)是老做法,现在已经被 div + CSS 取代,HTML5 里 table 的语义回归为"展示表格数据"。所以考试如果问"能不能用 table 布局",答"能但不推荐,应该用 div + CSS"。另外 table 还有 thead、tbody、tfoot 三个子标签用来分组表头、表体、表尾,caption 用来给表格加标题,大型数据表格会用到。

1.9 布局标签:div 与 span

这两个标签,一般都是和 CSS 结合到一块使用来实现页面的布局。它们本身没有任何默认的视觉样式:不改变字体、不加边框、不加背景,纯粹是"一个用来装东西的盒子"。

它们的区别只有一条,但很关键:div 标签在浏览器上会有换行的效果,而 span 标签在浏览器上没有换行效果。

代码演示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>我是div</div>
    <div>我是div</div>
    <span>我是span</span>
    <span>我是span</span>
</body>
</html>

浏览器效果:两行"我是div"上下排列,后面紧跟一行"我是span我是span",两个 span 挤在同一行,中间只隔一个空格(源码里的换行被折叠成了空格)。

这正是 1.4.10 节那张表的具体化:div 是块级元素,span 是行内元素。它们存在的意义是:当现有标签都不合适时,给你一个"无意义"的容器。比如你想把导航栏、主体内容、页脚各包一块好加背景色,就用 div;你想把一段文字里的某几个字单独标红,就用 span。

<div class="header">导航栏</div>
<div class="content">
    正文里这几个字要标红:<span class="red">重点内容</span>,其他不变。
</div>
<div class="footer">页脚</div>
【对比】

div 与 span 的分工如下:div 是"块",独占一行,用来切分页面的区域;span 是"段",跟在文字流里,用来标记一小段文字。 判断该用哪个的技巧:如果这块内容左右两边不该有别的东西,用 div;如果它必须和前后文字排在同一行,用 span。

【考点】 为什么真实项目里 div 用得比 span 多得多?因为页面布局是按"区域"划分的(头部、导航、主体、侧栏、页脚),每个区域都要独立设宽高、背景、边距,这些只有块级盒子才做得到。span 的主要用途是"在一段文字内部做局部修饰",量小但不可替代。

1.10 表单标签

表单标签的效果你并不陌生,像登录页面、注册页面等都是表单。像这样的表单就是用来采集用户输入的数据,然后将数据发送到服务端,服务端会对数据库进行操作:比如注册就是将数据保存到数据库中,而登录就是根据用户名和密码进行数据库的查询操作。

到这里,1.1.3 节那条主线第一次接上了:表单是"网页"这一层向"JavaWeb 程序"那一层递数据的唯一通道。 前面学的 h1、img、table 都只能"往外显示",只有 form 能"往里送"。所以表单是很重要的标签,需要重点学习。

1.10.1 表单与表单项

form 是表单标签,它在页面上没有任何展示的效果:你写一个 <form></form>,页面什么都不会变。它必须借助于表单项标签来展示不同的效果。可以这么理解:form 是一个"隐形的信封",表单项是"信封里的表格",用户填的是表单项,而 form 决定了这张表格寄给谁、怎么寄。

1.10.2 form 的 action 属性

action:规定当提交表单时向何处发送表单数据,该属性值就是 URL。

以后会将数据提交到服务端,该属性需要书写服务端的 URL。而今天我们可以书写 #,表示提交到当前页面来看效果。写 # 是学习阶段的好办法:它能让你在不写任何后端代码的情况下,亲眼看到"数据被发出去了、长什么样"。

action 的几种写法:

写法含义
action="#"提交到当前页面(不发到别处),学习阶段用
action="regServlet"相对路径,提交到当前目录下的 regServlet(第 5 章你会写这个 Servlet)
action="/项目名/regServlet"站内绝对路径,最常用
action="https://..."提交到外部服务器(跨域,很少用)

如果不写 action,浏览器默认提交到当前页面的 URL,效果和 # 差不多。

1.10.3 form 的 method 属性

method:规定用于发送表单数据的方式。取值有两种:

取值请求参数放哪儿长度限制说明
get拼接在 URL 后边URL 的长度有限制,约 4KB默认值。如果不设置 method 属性则默认就是该值
post浏览器会将数据放到 HTTP 请求消息体中请求参数无限制数据不出现在 URL 里

这是本章最重要的一张表,考试几乎必考。三个点要理解到位:

  1. 默认是 GET。 你什么都不写,就是 GET。这一点很多人到考试才发现:"我没写 method 啊,怎么参数跑地址栏去了?"
  2. 4KB 的限制是 URL 的限制,不是表单的限制。 因为 GET 把参数拼在 URL 上,而 URL 本身有长度上限(不同浏览器/服务器不一样,教材取 4KB 这个数量级)。所以上传文件绝不能用 GET。
  3. "POST 无限制"是相对的。 服务器端通常也有个请求体大小上限(Tomcat 的 maxPostSize,默认 2MB),只是比 4KB 宽松得多。教材说"无限制"是为了和 GET 形成对比,理解成"不受 URL 长度约束"更准确。

1.10.4 代码演示:从最简表单到能提交

由于表单标签在页面上没有任何展示的效果,所以在演示的过程中会先使用 input 这个表单项标签展示输入框效果。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form>
        <input type="text">
        <input type="submit">
    </form>
</body>
</html>

浏览器效果:页面有一个输入框,用户可以在输入框中输入自己想输入的内容;右边有一个"提交"按钮。点击提交按钮以后会将数据发送到服务端,当然现在肯定不能实现(还没有服务端)。

现在我们可以将 form 标签的 action 属性值设置为 #,让它将数据提交到当前页面。还需要注意一点,要想提交数据,input 输入框必须设置 name 属性:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="#">
        <input type="text" name="username">
        <input type="submit">
    </form>
</body>
</html>

在输入框输入 hehe,然后点击"提交"按钮,就能看到:浏览器的地址栏 URL 后面拼接上了 ?username=hehe。

username 就是输入框 name 属性的值,而 hehe 就是我们在输入框输入的内容。这就是"name 是键、输入内容是值"的含义:表单提交时,浏览器把每个有 name 的表单项拼成 name=value 的形式,多个之间用 & 连接。

接下来看 method 属性。默认是 method='get',所以数据会拼接到 URL 的后面。那我们将 method 属性值设置为 post:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>表单提交演示</title>
</head>
<body>
    <form action="#" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="登录">
    </form>
</body>
</html>

这时数据并没有拼接到 URL 后,地址栏还是干干净净的。那怎么看提交的数据呢?我们可以使用浏览器的开发者工具来查看:按 F12 打开开发者工具,切到 Network(网络)面板,勾选 Preserve log(保留日志),然后重新点一次"登录"按钮,就能在列表里看到这次请求;点开这条请求,在 Payload(或老版本的 Form Data)标签页里,能看到提交的数据:

username: hehe
password: 123456

这就是 POST 提交的参数,它在请求体里,不在 URL 里。原文用截图演示的正是这个操作过程,这里用文字把它说清楚,你照着做一遍比看十张图都管用。

1.10.5 【补充】GET 与 POST 在浏览器里能"看见"的差异

原文讲了"参数位置不同",但没讲这件事在浏览器里怎么被观察到。这一小节把它做成一张可验证的对照表。这些差异都是你能亲手实验出来的,不是背书:

观察点GET(method="get")POST(method="post")
地址栏变成 xxx.html?username=hehe&password=123,参数明文可见只有 action 的地址,看不到任何参数
F12 → Network → 请求详情参数在 Headers 的 Query String Parameters 里参数在 Payload(Form Data)里
刷新页面浏览器提示"要重新提交表单吗",确认后会再提交一次同样会提示,但很多浏览器直接拒绝刷新
收藏 / 分享链接收藏的是带参数的完整 URL,别人打开能看到同样的结果收藏的只是地址,参数丢失
浏览器历史记录参数(可能含密码)被记进历史不记录
能否直接构造可以在地址栏手敲 ?id=1 试参数,调试方便必须借助工具或表单才能发
长度受 URL 长度限制(约 4KB)受服务器请求体上限限制(通常宽松得多)
安全性参数暴露在地址栏、历史、服务器访问日志里参数在请求体里,相对不暴露(但不等于加密,抓包照样能看到)

由此引出两条工程惯例,考试和面试都会问:

【考点】

"GET 和 POST 的区别"标准答法分三层:① 参数位置:GET 拼在 URL 后,POST 放在请求体;② 长度限制:GET 受 URL 长度限制(约 4KB),POST 基本不受限;③ 语义与安全:GET 用于查询、幂等、可缓存可收藏,POST 用于提交、改变数据、参数不暴露在地址栏。补充一句"POST 也不加密"能明显加分。

【易错点】 一个高频困惑:"我写了 method="post",为什么地址栏还是有参数?"三种可能:① 属性拼错,写成了 mathod="post" 或 method=post(值没加引号,多数情况也能认,但拼错属性名就一定会退回默认 GET);② form 标签没包住输入框(input 写到了 </form> 外面),此时点提交根本不走表单逻辑;③ 用的是 type="submit" 之外的按钮(比如 type="button"),它不会触发表单提交。逐个排查,先看 F12 里这条请求的 Request Method 到底是 GET 还是 POST。

1.11 表单项标签

表单项标签有很多,不同的表单项标签有不同的展示效果。表单项标签可以分为以下三类:

1.11.1 input 的 type 属性

input 标签有个 type 属性。type 属性的取值不同,展示的效果也不一样。原文列了九种,逐个说清楚"长什么样、什么时候用、关键属性是什么":

type 取值展示效果用途与关键属性
text单行文本输入框最常用。输入普通文字,如用户名、搜索关键词
password单行文本输入框,输入内容显示为圆点密码。只是视觉遮挡,不是加密,页面上看不到但请求里是明文
radio圆形单选按钮单选(性别、学历)。同组必须同名 name 才能互斥;必须写 value
checkbox方形复选框多选(爱好、权限)。同组同名 name,可选中多个,提交时同名的值重复出现
file一个"选择文件"按钮上传文件。必须写 name,且 form 的 method 必须是 post、enctype 必须是 multipart/form-data
hidden页面上完全看不见隐藏域,用来携带用户不该改但服务端需要的数据,如主键 id
submit提交按钮点击后触发表单提交。value 决定按钮上的文字
reset重置按钮点击后把表单内所有控件恢复到初始值(恢复为页面刚加载时的状态,不做清空)
button普通按钮点了什么都不做,纯粹给 JS 挂事件用

submit、reset、button 三者长得几乎一样,区别全在"点击后发生什么":submit 提交表单、reset 重置表单、button 什么也不干。它们的显示文字都由 value 属性决定,不写 value 时浏览器会给个默认文字(如"提交查询内容")。

1.11.2 label 标签与 for 属性

label 用来给表单项加"文字说明",并且有一个非常实用的能力:点文字等于点控件。

<label for="username">用户名:</label>
<input type="text" name="username" id="username">

for 属性的值必须等于目标控件的 id 值。这样用户点"用户名:"这几个字,光标就会自动跳进输入框;对单选和复选框来说,点文字就能选中,鼠标不用瞄准那个小圆点。

这就是 1.11.5 节"id 必须唯一"的实际用途之一:for 是靠 id 找人的,id 重了就会指错。

1.11.3 select 与 option:下拉列表

select 定义下拉列表,option 定义列表项。默认只显示一行,点一下才展开所有选项。

<select name="city">
    <option>北京</option>
    <option value="shanghai">上海</option>
    <option>广州</option>
</select>

注意这里刻意混用了两种写法,正好说明 value 的作用:

规则是:有 value 用 value,没 value 用文本。 上面这个下拉框选"上海",提交的是 city=shanghai(英文),而选"北京"提交的是 city=北京(中文)。这种不一致会给后端处理带来麻烦,所以规范做法是每个 option 都显式写 value,让"显示给用户看的"和"提交给程序用的"彻底分开:显示中文,提交编号。这也是数据库设计里的常见做法,页面显示"男/女",数据库存 1/2。

1.11.4 textarea:文本域

<textarea cols="20" rows="5" name="desc"></textarea>

textarea 是文本域,它可以输入多行文本,而 input 文本框只能输入一行文本。

属性作用
cols文本域的列数(宽度,以字符为单位)
rows文本域的行数(高度,以字符为单位)
name提交时的参数名,必写

要特别注意 textarea 的写法:它的内容写在标签之间,不是写在 value 属性里。<textarea value="abc"></textarea> 是错的,什么都不会显示。正确写法是 <textarea>abc</textarea>。

1.11.5 三条必须记住的注意

原文在这里加了三条"注意",每一条都是实际出错之后才总结出来的:

注意一:以上标签项的内容要想提交,必须得定义 name 属性。

没有 name 的表单项,浏览器根本不会把它放进请求里,它不会提交一个空值,这个参数压根就不会出现。这是"页面明明填了、后台 getParameter 却是 null"的第一大原因。1.10.4 节演示输入 hehe 能看到 ?username=hehe,前提就是那个 input 写了 name="username"。

注意二:每一个标签都有 id 属性,id 属性值是唯一的标识。

id 在一个页面里不能重复。它的用途有三个:label 的 for 靠它找控件(1.11.2);第 2 章的 CSS 用 #id 精确选中某一个元素;第 3 章的 JS 用 document.getElementById("id") 拿到这个元素。id 重了,这三件事全部出错,而且往往不报错、只是"选中的不是我想要的那个"。

注意三:单选框、复选框、下拉列表需要使用 value 属性指定提交的值。

如果 radio/checkbox 不写 value,浏览器提交的默认值是 on,你会在后台看到 gender=on、hobby=on,完全分不清用户选了哪个。所以这三个必须写 value。

另外补两条原文没提但同类的规则:同一组单选按钮必须用同一个 name(这样才能互斥,一次只能选一个);同一组复选框用同一个 name(这样多选的值会以同名参数的形式一起提交,后台用 getParameterValues 取数组,第 6 章讲)。

1.11.6 完整注册页(原文代码还原)

原文 1.10 节的最终示例被双栏排版劈开了(<input type="hidden" name="id" 与 value="123"> 分处两栏,<label> 与 <input> 的缩进也乱了)。下面还原成一个完整、可直接运行的单页面,缩进统一为 4 空格,内容与原意一致:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
    <form action="#" method="post">
        <input type="hidden" name="id" value="123">

        <label for="username">用户名:</label>
        <input type="text" name="username" id="username"><br>

        <label for="password">密码:</label>
        <input type="password" name="password" id="password"><br>

        性别:
        <input type="radio" name="gender" value="1" id="male"> <label for="male">男</label>
        <input type="radio" name="gender" value="2" id="female"> <label for="female">女</label>
        <br>

        爱好:
        <input type="checkbox" name="hobby" value="1"> 旅游
        <input type="checkbox" name="hobby" value="2"> 电影
        <input type="checkbox" name="hobby" value="3"> 游戏
        <br>

        头像:
        <input type="file"><br>

        城市:
        <select name="city">
            <option>北京</option>
            <option value="shanghai">上海</option>
            <option>广州</option>
        </select>
        <br>

        个人描述:
        <textarea cols="20" rows="5" name="desc"></textarea>
        <br>
        <br>
        <input type="submit" value="免费注册">
        <input type="reset" value="重置">
        <input type="button" value="一个按钮">
    </form>
</body>
</html>

在浏览器里的效果:一个竖排的注册表单,从上到下依次是隐藏的 id(看不见)、用户名输入框、密码输入框(输进去显示为圆点)、性别两个单选按钮、爱好三个复选框、一个"选择文件"按钮、一个城市下拉框、一个 20 列 5 行的多行文本域,最后一行三个按钮:"免费注册""重置""一个按钮"。

点"免费注册"后,如果填了用户名 hehe、密码 123456、性别男、爱好勾了旅游和游戏、城市选北京、描述写了"你好",请在 F12 的 Payload 里核对,提交的数据长这样:

id: 123
username: hehe
password: 123456
gender: 1
hobby: 1
hobby: 3
city: 北京
desc: 你好

对着这份结果,前面所有规则都得到了验证:

【易错点】

上面这份代码里,"头像"那个 <input type="file"> 没有写 name,所以它不会出现在提交的数据里。这是原文示例留下的瑕疵,正好当成反面教材。想上传文件必须写成 <input type="file" name="photo">,并且 form 要加 enctype="multipart/form-data"、method 必须是 post;否则服务器收到的只是文件名,不是文件内容(第 6 章讲文件上传)。同理,三个爱好复选框没有写 id,也就无法配 label,只能点小方框本身。

1.12 【补充】HTML5 语义标签一览

原文没有讲这一块,但你翻开任何一个 2020 年之后的网站源码都会看到它们,考试也可能出"说出三个 HTML5 语义标签"这类题。所谓"语义标签",就是标签名本身说明了内容是什么,而不是像 div 那样只说明"这是个盒子"。

语义标签表示什么传统 div 写法
<header>页面或某个区域的头部(logo、标题、搜索框)<div id="header">
<nav>导航链接区<div id="nav">
<main>页面的主体内容,一个页面只能有一个<div id="main">
<article>独立、完整、可单独分发的内容(一篇文章、一条微博)<div class="article">
<section>一个主题性的区块,通常带小标题<div class="section">
<aside>侧边栏、附注、相关推荐<div id="sidebar">
<footer>页面或区域的页脚(版权、联系方式)<div id="footer">
<figure> / <figcaption>插图及其说明文字<div class="img-box">
<mark>需要高亮标记的文字(默认黄底)<span class="highlight">
<time>日期/时间,可带 datetime 属性给机器读<span>
<details> / <summary>可折叠的详情块,点标题展开内容,不需要 JS需要 JS 实现
<progress> / <meter>进度条 / 度量条需要 CSS 画

意义有三层:一是可读性,<nav> 一眼就知道是导航,div class="nav" 要靠命名约定;二是可访问性,屏幕阅读器能靠这些标签给视障用户提供"跳到主体内容""跳到导航"的快捷操作;三是 SEO,搜索引擎能更准确地判断页面哪部分是正文、哪部分是广告。

和 div 的关系是分工,两者并不互相替代:先考虑用语义标签把页面的大结构搭出来,结构内部再用 div 做纯布局的容器。

<body>
    <header>站点头部</header>
    <nav>导航:首页 | 课程 | 关于</nav>
    <main>
        <article>
            <h1>文章标题</h1>
            <p>正文段落,其中<mark>这几个字要高亮</mark>。</p>
        </article>
        <aside>相关推荐</aside>
    </main>
    <footer>&copy; 2024 版权所有</footer>
</body>
【补充】

这些标签都是 HTML5 新增的。老版本 IE(IE8 及以下)不认识它们,会当成未知的行内元素处理,导致样式失效,当年要引入一个叫 html5shiv 的 JS 补丁才能用。现在(Chrome、Edge、Firefox 的现代版本)全部原生支持,可以放心用。但对搜索引擎和屏幕阅读器来说,语义标签的价值和兼容性无关,永远有效。

本章小结

本章从一个定义出发,落到一个能提交数据的页面。把知识串成一条线:HTML 是标记语言 → 标签是预定义的 → 浏览器把标签解析成 DOM 树 → DOM 树决定页面长什么样 → 表单把用户数据送出浏览器。

知识块要点这一章的知识下一章用在哪
Web 与 JavaWeb网页展现、程序处理、数据库存储,三者分工不可替代第 5 章 Servlet 接请求、第 10 章 JDBC 存数据,都是这条主线的落点
HTML 的本质超文本 + 标记语言,标签预定义、由浏览器解析第 8 章 JSP 就是"能嵌 Java 的 HTML",标签功底直接决定 JSP 写得好不好
XML vs HTMLXML 严谨自由(自定义),HTML 宽松死板(固定)第 8 章 EL、第 10 章数据库配置、MyBatis 映射文件都是 XML,靠"严谨"吃饭
W3C 三层结构 HTML、表现 CSS、行为 JavaScript,必须分离第 2 章接表现,第 3 章接行为
DOM 树标签的嵌套关系变成父子节点树,程序可读写第 3 章全部 JS 操作的舞台,第 8 章 EL 取值也走这棵树
基础标签h1~h6 逐级变小、hr 横线、font 三属性、br 换行、p 段落、b/i/u、center、转义字符第 2 章给它们套 CSS 换样式,font/center 正是被 CSS 取代的对象
块级与行内块级独占一行可设宽高,行内并排显示宽高无效第 2 章盒模型的直接前提
媒体与路径img/audio/video、px 与百分比、绝对与相对路径第 5 章部署到 Tomcat 后的路径问题,第 6 章响应里输出图片
超链接href 目标、target 决定在当前页还是新标签页打开第 6 章重定向、第 9 章过滤器拦截的正是这些请求
列表与表格ol/ul 的 type、table 的 border/cellspacing/rowspan/colspan第 8 章 JSTL 遍历集合生成表格行
div 与 span块级容器 vs 行内容器,无默认样式,为 CSS 而生第 2 章布局的主力
表单action 决定发给谁、method 决定怎么发、name 决定叫什么第 6 章 request.getParameter("name"),全书最直接的一条因果链
表单项input 九种 type、label for、select/option、textarea,name 必填、id 唯一、value 必写第 6 章取单个值/取数组、文件上传

七条收口要点:

  1. HTML 的定义:HTML 是超文本标记语言,用预定义标签描述网页的结构,由浏览器解析成 DOM 树后渲染。
  2. 三条铁律:表单项要有 name 才会被提交;id 全页唯一;单选/复选/下拉要显式写 value。
  3. 两对易混:块级 vs 行内(换行与宽高);GET vs POST(参数在 URL 还是请求体)。
  4. 一组路径规则:起点是当前 HTML 文件所在目录,./ 当前、../ 上一级、子目录直接写名字。
  5. 一条主线:网页只负责展示数据,JavaWeb 程序负责逻辑处理,数据库负责存储,本章只完成了"展示"和"采集"。
  6. 一个伏笔:每个标签都是一个矩形盒子,块级与行内的差别只是盒子的排版规则不同,第 2 章盒模型正式展开。
  7. 一个态度:HTML 容错但不该依赖容错,从第一天就写严格闭合、正确嵌套的代码。

自测

基础题

  1. 分别定义 Web 与 JavaWeb,并说明"网页、数据库、JavaWeb 程序"三者的分工。
  2. 写出 HTML、XML 的四条区别(标签能否自定义、语法严格程度、用途、大小写)。
  3. 一个网页由哪三部分组成?分别对应哪三门语言?各举一个具体作用。
  4. 指出下面代码的三处错误,并说明修正后的写法:
<html>
    <head>
        <title>测试
    </head>
    <body>
        <font color=red size=5>红色大字</font>
    <body>
</html>
  1. 说出下面每个标签/属性的作用:hr、br、p、cellspacing、colspan、target="_blank"。
  2. 页面文件在 html 目录下,图片在 img 目录下(与 html 目录同级),图片名 a.jpg。写出在页面里引用这张图的 src 值。

进阶题

  1. 写出下面表单提交后地址栏的完整 URL,并说明为什么是这个结果:
<form action="reg" method="get">
    <input type="text" name="username" value="hehe">
    <input type="password" name="pwd" value="123">
    <input type="submit" value="提交">
</form>
  1. 某同学写了 <input type="text" id="username">,页面上能正常输入,但点击提交后后台 request.getParameter("username") 取到 null。请找出原因,并说明如果他把 id 改名为 name 会有什么副作用。
  2. 下面这段代码想做出"性别:男/女 二选一",但现在两个都能选中。请指出错误并改正:
性别:
<input type="radio" name="gender1" value="1"> 男
<input type="radio" name="gender2" value="2"> 女
  1. 下面的表格想做成"张三跨两行",但显示错位。请指出错误并改正:
<table border="1">
    <tr>
        <td rowspan="2">张三</td>
        <td>语文</td>
    </tr>
    <tr>
        <td>张三</td>
        <td>数学</td>
    </tr>
</table>

参考答案

  1. Web:全球广域网,万维网(www);JavaWeb:用 Java 技术解决相关 Web 互联网领域的技术栈。分工:网页展现数据,数据库存储和管理数据,JavaWeb 程序做逻辑处理。
  2. ① 标签能否自定义:HTML 不能、只能用预定义标签,XML 必须自定义;② 语法:HTML 不严格(松散),XML 极其严格;③ 用途:HTML 展示数据,XML 传输和存储数据;④ 大小写:HTML 标签不区分,XML 区分。
  3. 结构对应 HTML(定义页面结构)、表现对应 CSS(美化、控制颜色字号布局)、行为对应 JavaScript(让页面动起来,如轮播图)。三者应分离。
  4. ① <title> 未闭合,应写 </title>;② 属性值未加引号,应写 color="red";③ 第二个 <body> 应为 </body>(结束标签写成了开始标签)。另外 size 取值 1~7,5 合法。
  5. hr 水平横线;br 强制换行(单标签);p 段落,上下留白;cellspacing 表格单元格之间的空白;colspan 单元格横向跨的列数;target="_blank" 在新标签页打开链接。
  6. ../img/a.jpg(先回上一级到模块根,再进 img)。
  7. reg?username=hehe&pwd=123。因为 method="get",浏览器把各表单项拼成 name=value 并用 & 连接后附在 action 的 URL 后面;submit 按钮没有 name,所以不参与提交。
  8. 缺少 name 属性。浏览器只提交带 name 的表单项,id 只用于 CSS/JS/label 定位,不参与提交。改成 name 后能提交了,但该元素就没有 id 了,label 的 for 和 document.getElementById 都会失效,正确做法是 name 和 id 都写(值可以相同)。
  9. 两个单选的 name 不同(gender1/gender2),不构成一组,所以不互斥。改为同名:name="gender",并保留各自的 value。
  10. 第一行已用 rowspan="2" 让"张三"占两行,第二行就不应再写"张三"这个单元格;多余的格子把整行挤错位了。删除第二行的 <td>张三</td> 即可。