新增的元素
section 元素
section 元素表示页面中的一个内容块,比如章节、页眉、页脚或者页面中的其他部分。
html
<section>...</section>
article 元素
表示页面中的一块与上下文不相关的独立内容。
html
<article>.....</article>
aside 元素
aside 元素 article 元素的内容之外的、与 article 元素的内容相关的辅助信息。
html
<aside>...</aside>
header 元素
header 元素表示页面中一个内容区块或者整个页面的标题。
html
<header>...</header>
footer 元素
footer 元素表示整个页面或者页面中一个内容区块的脚注。
html
<footer>...</footer>
nav 元素
nav 元素表示页面中导航链接的部分。
html
<nav>...</nav>
figure 元素
figure 元素表示一段独立的流内容,一般表示文档主题流内容中的一个独立单元。
使用 figcaption 元素为 figure 元素组添加标题。
html
<figure>
<figcaption>标题</figcaption>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Corporis cum ipsum
vel consequatur, totam iure porro quo asperiores laboriosam cumque culpa
tempora architecto blanditiis laborum veniam natus qui inventore
perferendis.
</p>
</figure>
main 元素
main 元素表示网页中的主要内容。
html
<main>...</main>
新增其他元素
video 元素
video 元素用于定义视频,比如电影片段或者其他视频流。
html
<video src="movie.ogg" controls="controls">video元素</video>
audio 元素
audio 元素用于定义音频,比如音乐或其他音频流。
html
<audio src="audio.wav">audio元素</audio>
embed 元素
embed 元素用来插入各种多媒体,格式可以是 Midi、Wav、AIFF、AU、MP3 等。
html
<embed src="horse.wav" />
mark 元素
mark 元素主要用来在视觉上向用户呈现那些需要突出显示或高亮显示的文字。mark 元素的一个比较典型的应用就是在搜索结果中向用户高亮显示搜索关键词。
html
<mark>mark</mark>
progress 元素
progress 元素表示运行中的进程,可以使用 progress 元素来显示 JS 中耗费时间函数的进程。
html
<progress>progress</progress>
meter 元素
meter 元素表示度量衡,仅用于已知最大值和最小值的度量。
html
<meter value="10" min="1" max="20"></meter>
time 元素
time 元素用于表示日期或者时间
html
<time>2026年1月2日14:07:33</time>
ruby 元素
ruby 元素表示 ruby 注释(中文注音或字符)
html
<h1>汉字注音示例</h1>
<!-- 单个汉字 -->
<p>
我喜欢<ruby>学<rt>xué</rt></ruby
>习
</p>
<!-- 多个汉字 -->
<p>
<ruby>
北 <rp>(</rp><rt>Běi</rt><rp>)</rp> 京 <rp>(</rp><rt>jīng</rt
><rp>)</rp> </ruby
>是中国的首都
</p>
<!-- 日语假名注音 -->
<p>
日语:<ruby>漢<rp>(</rp><rt>かん</rt><rp>)</rp></ruby>
<ruby>字<rp>(</rp><rt>じ</rt><rp>)</rp></ruby>
</p>
<!-- 多音字 -->
<p>
<ruby>重<rt>zhòng</rt></ruby
>要 vs <ruby>重<rt>chóng</rt></ruby
>复
</p>
canvas 元素
canvas 元素表示图形,比如图表和其他图像。
html
<canvas id="my-canvas" width="200" height="200"></canvas>
details 元素
details 元素表示用户要求得到并且可以得到的细节信息。
html
<details>
<summary>HTML5</summary>
This document teaches you everything you have to learn about HTML5.
</details>
datalist 元素
datalist 元素表示可选数据的列表,与 input 元素配合使用,可以制作出输入值的下拉列表。
html
<input list="browsers" />
<datalist id="browsers">
<option value="Chrome"></option>
<option value="Firefox"></option>
<option value="Safari"></option>
<option value="Edge"></option>
<option value="Opera"></option>
</datalist>
datagrid 元素
datagrid 元素表示可选数据的列表,它以树型列表的形式显示。
未被广泛使用
output 元素
output 元素表示计算或者用户操作的结果,通常与表单结合使用
html
<form oninput="result.value=parseInt(a.value)+parseInt(b.value)">
<input type="range" id="a" value="50" /> +
<input type="number" id="b" value="25" /> =
<output name="result" for="a b">75</output>
</form>
source 元素
source 元素为媒介元素(比如
<video>和<audio>)定义媒介资源。指定多个媒体资源,让浏览器选择最合适的格式。
| 属性 | 用途 | 示例 |
|---|---|---|
| srcset | 图片 URL 集合 | srcset="img.jpg,img-2x.jpg 2x" |
| sizes | 图片显示尺寸 | sizes="(max-width:600px)100vw,50vw" |
| media | 媒体查询条件 | media="(min-width:768px)" |
| type | MIME 类型 | type="image/webp",type="video/mp4" |
| src | 媒体文件 URL | src="audio.mp3" |
html
<picture>
<!-- WebP 格式(现代浏览器优先) -->
<source
srcset="
https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&h=600&fit=crop&fm=webp
"
type="image/webp"
/>
<!-- 高清大图(大屏幕) -->
<source
media="(min-width: 1200px)"
srcset="
https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1200&h=800&fit=crop
"
type="image/jpeg"
/>
<!-- 中等图片(平板) -->
<source
media="(min-width: 768px)"
srcset="
https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&h=600&fit=crop
"
type="image/jpeg"
/>
<!-- 小图片(手机) -->
<source
media="(max-width: 767px)"
srcset="
https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=300&fit=crop
"
type="image/jpeg"
/>
<!-- 后备图片 -->
<img
src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&h=600&fit=crop"
alt="高山风景"
style="width:100%; height:auto;"
/>
</picture>
dialog 元素
dialog 元素用于创建模态框和非模态框。
html
<dialog id="myDialog">
<h2>对话框标题</h2>
<p>对话框内容...</p>
<button onclick="this.closest('dialog').close()">关闭</button>
</dialog>
<button onclick="document.getElementById('myDialog').showModal()">
打开对话框
</button>
input 元素的类型
- email 表示必须输入 e-mail 地址的文本输入框。
- url 表示必须输入 URL 地址的文本输入框。
- number 表示必须输入数字的文本输入框。
- range 表示必须输入一定范围内数字值的文本输入框。
全局属性
contentEditable
该属性的主要功能是允许用户编辑元素中的内容,所以该元素必须是可以获得鼠标焦点的元素,而且在点击鼠标后要向用户提供一个插入符号,提示用户该元素中的内容允许编辑。
html
<div>
<ul contenteditable="true">
<li>列表A</li>
<li>列表B</li>
<li>列表C</li>
<li>列表D</li>
</ul>
</div>
在编辑完元素中的内容后,只能把该元素的 innerHTML 内容保存。因为改变元素内容后该元素的 innerHTML 内容也会随着改变。
designMode
用来指定整个页面是否可编辑,当页面可编辑时,页面中任何支持 contentEditable 属性的元素都变成可编辑状态。designMode 属性只能在 Javascript 脚本中被编辑修改。该属性有两个值
on和off。当该属性值被指为on时表示可编辑,为off时,页面不可编辑。
javascript
document.designMode = "on";
hidden
通知浏览器不渲染该元素,使该元素处于不可见状态。但是元素中的内容还是被浏览器所创建的。取消后元素变为可见状态,同时元素中的内容也即时显示出来。hidden 属性是一个布尔值的属性,当设为
true时,元素处于不可见状态,当设为 false 时,元素处于可见状态。
html
<div hidden>div hidden</div>
<div>div not hidden</div>
spellcheck
spellcheck 属性是 Html5 针对
inputtype='text'与textarea这两个输入框提供的一个新的属性,它的功能对用户输入的文本内容进行拼写和语法检查。
html
<textarea spellcheck="false"></textarea>
<textarea spellcheck="true"></textarea>
tabindex
当不断敲击
Tab键让窗口或者页面中的控件获得焦点。当把元素的 tabindex 值设为负数(通常是
-1)按下 Tab 键时该元素就不能获得焦点。
新增的事件
| 元素或对象 | 事件 | 触发时机 |
|---|---|---|
| window对象body元素 | beforeprint | 即将开始打印之前触发 |
| window对象body元素 | afterprint | 打印完毕时触发 |
| window对象body元素 | resize | 浏览器窗口大小发生改变时触发 |
| window对象body元素 | error | 页面加载出错时触发 |
| window对象body元素 | offline | 页面变为离线状态时触发 |
| window对象body元素 | online | 页面变为在线状态时触发 |
| window对象body元素 | pageshow | 页面加载时触发,类似load事件,区别在于load事件在页面第一次加载时触发,而pageshow事件在每一次加载时触发,即从 网页缓存中读取页面时只触发pageshow事件,不触发load事件 |
| window对象body元素 | beforeunload | 当前页面被关闭时触发 |
| window对象body元素 | hashchange | 当页面URL地址字符串中的哈希部分(#后面的部分)发生改变时触发 |
| 任何元素 | mousewheel | 当用户鼠标指针悬停在元素上并滚动鼠标滚轮时触发 |
| 任何容器元素 | scroll | 当元素滚动条被滚动时触发 |
| input元素textarea元素 | input | 当用户修改文本框中内容时触发,input事件与change事件的区别为input事件在元素尚未失去焦点时已触发,change事件只在元素失去焦点时触发 |
| form元素 | reset | 当用户按下表单元素中type类型为reset的input元素或者js脚本代码中执行表单对象的reset方法时触发 |
只监听一次事件
html
<input type="button" value="点击我" id="button" />
<script>
const btn = document.getElementById("button");
btn.addEventListener(
"click",
(e) => {
console.log("btn");
},
{
once: true,
}
);
</script>
文件 API
FileList 对象与 file 对象
FileList 对象表示用户选择的文件列表。
js
<input type="file" id="imageUpload" accept="image/*" multiple>
<div id="preview"></div>
<script>
document.getElementById('imageUpload').addEventListener('change', function(e) {
const fileList = e.target.files;
const preview = document.getElementById('preview');
preview.innerHTML = '';
if (!fileList.length) {
preview.innerHTML = '<p>未选择文件</p>';
return;
}
// 限制最多处理5个文件
const filesToProcess = Array.from(fileList).slice(0, 5);
filesToProcess.forEach(file => {
// 验证文件类型
if (!file.type.startsWith('image/')) {
console.warn(`${file.name} 不是图片文件`);
return;
}
// 验证文件大小(限制2MB)
const maxSize = 2 * 1024 * 1024; // 2MB
if (file.size > maxSize) {
console.warn(`${file.name} 文件太大`);
return;
}
// 创建文件预览
const reader = new FileReader();
reader.onload = function(e) {
const div = document.createElement('div');
div.className = 'file-item';
div.innerHTML = `
<img src="${e.target.result}" height="100">
<div>
<p>名称: ${file.name}</p>
<p>大小: ${(file.size / 1024).toFixed(2)} KB</p>
<p>类型: ${file.type}</p>
</div>
`;
preview.appendChild(div);
};
reader.readAsDataURL(file);
});
});
</script>
ArrayBuffer 与 ArrayBufferView 对象
ArrayBuffer 对象代表一个固定长度的用于装载数据的缓冲区。不能直接对 ArrayBuffer 对象中的内容进行操作。
需要使用 ArrayBufferView 对象将缓存区中的数据转换为各种数值类型的数组。
ArrayBuffer
ArrayBuffer 对象代表一个存储固定长度的二进制数据的缓存区,不能直接存取 ArrayBuffer 缓存区中的内容,必须通过 ArrayBufferView 对象来读写 ArrayBuffer 缓存区中的内容。
js
const buffer = new ArrayBuffer(32);
ArrayBufferView
通过 ArrayBufferView 对象以一种准确的格式来表示 ArrayBuffer 缓存区中的数据。
一般不直接使用 ArrayBufferView 对象,而是使用继承 ArrayBufferView 类的子类的实例对象来存取 ArrayBuffer 缓存区中的数据。
| 类型 | 字节长度 | 描述 |
|---|---|---|
| Int8Array | 1 | 8位整数数组 |
| Uint8Array | 1 | 8位无符号整数数组 |
| Uint8ClampedArray | 1 | 8位无符号整数数组 |
| Int16Array | 2 | 16位整数数组 |
| Uint16Array | 2 | 16位无符号整数数组 |
| Int32Array | 4 | 32位整数数组 |
| Uint32Array | 4 | 32位无符号整数数组 |
| Float32Array | 4 | 32位IEEE浮点数数组 |
| Float64Array | 8 | 64位IEEE浮点数数组 |
FileReader 对象的事件
| 事件 | 描述 |
|---|---|
| onabort | 数据读取中断时触发 |
| onerror | 数据读取出错时触发 |
| onloadstart | 数据开始读取时触发 |
| onprogress | 数据读取中 |
| onload | 数据读取成功完成时触发 |
| onloadend | 数据读取完成时触发,无论成功或者失败 |
案例
html
<label>请选择一个文件</label>
<input type="file" id="file" />
<input type="button" value="读取图像" onclick="readURL()" />
<input type="button" value="读取二进制数据" onclick="readBinary()" />
<input type="button" value="读取文本文件" onclick="readText()" />
<div name="result" id="result"></div>
<script>
const domFile = document.getElementById("file");
const domResult = document.getElementById("result");
function readURL() {
read("readAsDataURL");
}
function readBinary() {
read("readAsBinaryString");
}
function read(eventName) {
domResult.innerHTML = "";
const [file] = domFile.files;
const reader = new FileReader();
reader[eventName](file);
reader.onload = (e) => {
domResult.innerHTML = `<img src='${e.target.result}' height='200' width='200' />`;
};
}
function readText() {
read("readAsText");
}
</script>
html
<!DOCTYPE html>
<head>
<meta charset="UTF-8" />
<title>fileReader对象的事件先后顺序</title>
</head>
<p>
<label>请选择一个图像文件:</label>
<input type="file" id="file" />
<input type="button" value="显示图像" onclick="readFile()" />
</p>
<div name="result" id="result">
<!-- 这里用来显示读取结果 -->
</div>
<script language="javascript">
var result = document.getElementById("result");
var input = document.getElementById("input");
if (typeof FileReader == "undefined") {
result.innerHTML = "<p class='warn'>抱歉,你的浏览器不支持 FileReader</p>";
input.setAttribute("disabled", "disabled");
}
function readFile() {
var file = document.getElementById("file").files[0];
var reader = new FileReader();
reader.onload = function (e) {
result.innerHTML = '<img src="' + this.result + '" alt=""/>';
alert("load");
};
reader.onprogress = function (e) {
alert("progress");
};
reader.onabort = function (e) {
alert("abort");
};
reader.onerror = function (e) {
alert("error");
};
reader.onloadstart = function (e) {
alert("loadstart");
};
reader.onloadend = function (e) {
alert("loadend");
};
reader.readAsDataURL(file);
}
</script>
本地存储
Cookie
Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据(通常不超过 4KB)。浏览器会存储这些 Cookie,并在后续向同一服务器发起请求时自动携带它们。
Cookie 结构
text
name=value; expires=date; path=/; domain=.example.com; Secure; HttpOnly; SameSite=Strict
- name:Cookie 名称(区分大小写)
- value:Cookie 值(URL 编码存储)
- GMT 格式:Thu, 18 Dec 2025 12:00:00 GMT
- 不设置:会话 Cookie(浏览器关闭时删除)
- 设置为过去时间:立即删除
- expires:过期时间
- max-age:存活时间(秒)
- 优先级高于 expires
- max-age=3600(1 小时)
- max-age=0:立即删除
- max-age=-1:立即删除
- domain:作用域
- 指定 Cookie 有效的域名
- 子域名自动包含:.example.com 对所有子域名有效
- 精确匹配:www.example.com 只对该域名有效
- path:路径限制
- 指定 Cookie 有效的 URL 路径
- /admin:只对 /admin/* 路径有效
- /:对整个域名有效(默认)
- Secure:安全标记
- 仅通过 HTTPS 传输
- 防止中间人攻击
- HttpOnly:HTTP 限制
- 禁止 JavaScript 访问(Document.cookie)
- 有效防止 XSS 攻击窃取 Cookie
- SameSite:跨站限制
- Strict:严格模式,完全禁止跨站携带
- Lax:宽松模式,GET 请求允许跨站
- None:允许跨站(必须配合 Secure 使用)
- Priority:优先级(Chrome)
- Low:低优先级
- Medium:中优先级(默认)
- High:高优先级
工作原理
text
客户端请求 → 服务器响应(Set-Cookie) → 浏览器存储 → 后续请求自动携带
WebStorage
sessionStorage
将数据保存在 session 对象中。所谓 session,是指用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。session 对象可以用来保存在这段时间内所要求保存的任何数据。
localStorage
将数据保存在客户端本地的硬件设备(通常指硬盘,也可以是其它硬件设备)中,即时浏览器关闭了,而 localStorage 为永久保存。
js
// 存储字符串数据
sessionStorage.setItem("username", "张三");
localStorage.setItem("theme", "dark");
// 存储非字符串会自动转为字符串
sessionStorage.setItem("count", 42); // 存储为 "42"
localStorage.setItem("user", JSON.stringify({ name: "张三", age: 25 }));
// 获取字符串数据
const username = sessionStorage.getItem("username"); // "张三"
const theme = localStorage.getItem("theme"); // "dark"
// 获取不存在的键
const notExist = sessionStorage.getItem("not-exist"); // null
// 获取并解析JSON
const userStr = localStorage.getItem("user");
const user = userStr ? JSON.parse(userStr) : null;
// 删除单个键值对
sessionStorage.removeItem("username");
localStorage.removeItem("theme");
// 删除不存在的键不会报错
sessionStorage.removeItem("not-exist"); // 静默失败
// 清空当前域下的所有存储
sessionStorage.clear();
localStorage.clear();
// 注意:只清除当前域的数据,不会影响其他域名
// 监听存储变化(localStorage 独有)
window.addEventListener("storage", (event) => {
console.log("存储发生变化:", {
key: event.key, // 变化的键
oldValue: event.oldValue, // 旧值
newValue: event.newValue, // 新值
url: event.url, // 触发变化的页面URL
storageArea: event.storageArea, // 变化的存储区域
});
});
- 监听存储变化(localStorage 独有)
- 仅在另一个标签页修改时触发
- 当前标签页的修改不会触发
- sessionStorage 不支持此事件
indexDB
indexDB 是一个事务型数据库系统,类似于基于 SQL 的 RDBMS,但它使用 Javascript 对象存储数据,支持结构化数据、二进制大对象(Blob)等。
连接数据库
indexDB 连接数据库 - indexDB.open(dbName,dbVersion)
- dbName:数据库名称
- dbVersion:无符号长整型数值,代表数据库版本号
- 返回值:IDBOpenDBRequest 对象,代表一个请求连接数据库的请求对象
监听数据库连接的请求对象的onsuccess与onerror事件来定义数据库连接成功时与失败时所需执行的事件处理函数。
js
dbConnect.onsuccess = (e) => {
// e.target.result为一个IDBDatabase对象,代表连接成功的数据库对象
idb = e.target.result;
};
关闭数据库连接
js
idb.close();
当数据库被关闭后,不能继续执行任何对数据库进行的操作,否则浏览器抛出
InvalidStateError异常,代表数据连接被关闭。
数据库的版本更新
在使用 indexDB 数据时,所有对数据的操作都在一个事务内部执行。事务分为三种:只读事务、读写事务、版本更新事务。
对于创建对象仓库与索引的操作,我们只能在版本更新事务内部进行,因为 indexDB API 中不允许数据库的数据仓库(相当于关系型数据库中的数据表)在同一个版本中发生变化,所以当我们创建或删除数据仓库时,必须使用新的版本号来更新数据库的版本,以避免重复修改数据库结构。
js
// 数据库版本更新
dbConnect.onupgradeneeded = (e) => {
idb = e.target.result;
const tx = e.target.transaction;
const newVersion = e.newVersion;
const oldVersion = e.oldVersion;
};
创建对象仓库
当连接数据库时发现指定的版本号大于数据当前版本号时触发该事件,当事件被触发时一个数据库的版本更新事务已经被开启,同时数据库的版本号已经被自动更新完毕。
creatObjectStore方法使用两个参数
- 第一个参数值为一个字符串,代表对象仓库名。
- 第二个参数为 Javascript 对象
keyPath属性值用于指定对象仓库中的每条记录使用哪个属性值来作为该记录的主键值,一条记录的主键为数据仓库中该记录的唯一标识符,在一个对象仓库中,只能有一个主键,但是主键可以是重复,相当于关系型数据库中数据表的 id 字段为数据库的主键,多条记录的 id 字段值可以重复,除非将主键指定为唯一主键。autoIncrement属性值为 true,相当于在关系型数据库中将主键指定为自增主键,如果添加数据记录时不指定主键值,则在数据库仓库内部将自动指定该主键值为既存的最大主键值+1。false 时就必须显示的指定主键值。
- 返回值,返回一个
IDBObjectStore对象,该对象代表被创建成功的对象仓库。
js
idb.createObjectStore(name, { keyPath: "id", autoIncrement: true });
创建索引
indexDB 数据库中的索引类似于关系型数据库中的索引,需要通过数据记录对象的某个属性值来创建。创建索引可以提高对数据仓库中的所有记录检索时的性能。
在 indexDB 中只能针对被设为索引的属性值进行检索。
js
const idx = store.createIndex(name, key, options);
createIndex 方法使用三个参数
- name:索引名称
- key:创建索引的属性
- option
- unique:是否唯一
- multiEntry:索引是否可以为一个数组
- 返回一个 IDBindex 方法
indexDB2.0 版本中,允许在版本更新事务中修改对象仓库的名称和索引的名称。
js
dbConnect.onupgradeneeded = (e) => {
const tx = e.target.transaction;
const store = tx.objectStore("User");
store.name = "UserNewName";
const idx = store.index("id");
idx.name = "idNew";
};
使用事务
事务分为三类
- 版本更新事务
- 只读事务
- 读写事务
如何开启只读事务和读写事务
js
const storeName = ["User"];
//只读事务
const mode = "readonly";
//读写事务
// const mode = 'readwrite'
// 开启事务
const tx = idb.transaction(storeName, mode);
// 也可以针对某个对象仓库进行
const tx = idb.transaction("User", "readonly");
objectStoreNames属性值为由该数据库中所有对象仓库名构成的数组
transaction 方法参数
-
第一个参数,为
objectNames可以针对数据库中任何一个对象仓库进行数据的存取操作。 -
第二个参数,为可选参数,指定事务的读写模式,省略该参数时,所开启的事务为只读事务。
-
返回值,IDBTransaction 对象,代表开启的事务。
考虑事务对性能的影响,应该要指定事务的作用范围。
-
abort:中止事务
-
oncomplete:事务结束时触发
-
onabort:事务中止时触发
保存数据
- put put 返回一个 IDBRequest 对象,代表一个向数据库发出的请求。
js
const req = store.put(value);
req.onsuccess = function (e) {
// 保存成功执行
};
req.onerror = function (e) {
// 保存失败执行
};
js
// 主键为自增、内联主键时不需要指定主键值
store.put({ name: "alex", age: 12 });
// 主键为内联、非自增主键时需要指定主键值
store.put({ id: 1, name: "tome", age: 11 });
// 主键为外部主键时需要指定主键值
store.put({ name: "jack", age: 13 }, 1);
保存 Blob 对象
Ajax
XMLHttpRequest
H5 中,为 XMLHttpRequest 对象新增 responseType 属性与 response 属性。
- responseType 属性
- 用于指定服务器返回数据的数据类型,可指定值为
text、arraybuffer、blob、json或document。如果将属性值为空字符串或者不使用,则默认值为text
- 用于指定服务器返回数据的数据类型,可指定值为
- response 属性:响应结果
- text:response 属性值为一串字符串
- arraybuffer:response 为一个 Blob 对象
- blob:response 为一个 JSON 对象
- document:属性值为一个 Document 对象
ArrayBuffer 响应
将 responseType 属性指定为
arraybuffer
以下代码实现从服务端获取图片,并下载
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>请求接口</title>
</head>
<body>
<div>
<button onclick="getImage()">获取图片</button>
<button onclick="downloadImage()">下载图片</button>
</div>
<div id="image-container"></div>
<script>
let imageBlob = null; // 保存 Blob 对象
let imageURL = ""; // 用于显示的 Base64 URL
let blobURL = ""; // 用于下载的 Blob URL
const getImage = () => {
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:3000/image", true);
xhr.responseType = "arraybuffer";
xhr.onload = (e) => {
if (xhr.status === 200) {
const arrayBuffer = e.target.response;
// 创建 Blob 对象
imageBlob = new Blob([arrayBuffer]);
// 为下载创建 Blob URL(更好的性能)
if (blobURL) {
URL.revokeObjectURL(blobURL); // 释放之前的 URL
}
blobURL = URL.createObjectURL(imageBlob);
// 为显示创建 Base64 URL
const reader = new FileReader();
reader.onload = (event) => {
imageURL = event.target.result;
// 创建图片元素并显示
const img = document.createElement("img");
img.src = imageURL;
img.alt = "加载的图片";
img.style.maxWidth = "500px";
img.style.margin = "20px 0";
// 添加到页面
const container = document.getElementById("image-container");
container.innerHTML = ""; // 清空之前的内容
container.appendChild(img);
};
reader.readAsDataURL(imageBlob);
} else {
console.error("请求失败:", xhr.status);
}
};
xhr.onerror = (e) => {
console.error("请求发生错误:", e);
};
xhr.send();
};
const downloadImage = () => {
if (!blobURL) {
alert("请先获取图片");
return;
}
// 创建下载链接
const a = document.createElement("a");
a.href = blobURL; // 注意这里是 href,不是 src
a.download = "downloaded-image.png"; // 设置下载的文件名
// 添加到 DOM 中触发点击
document.body.appendChild(a);
a.click();
// 清理
document.body.removeChild(a);
// 注意:不需要在这里释放 URL,让用户决定何时释放
// 或者可以在页面卸载时释放:window.addEventListener('beforeunload', () => URL.revokeObjectURL(blobURL));
};
// 页面卸载时清理 Blob URL
window.addEventListener("beforeunload", () => {
if (blobURL) {
URL.revokeObjectURL(blobURL);
}
});
</script>
</body>
</html>
Blob 响应
指定 responseType 属性值为
blob,从而将服务端下载的二进制数据用作以 Blob 对象。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>请求接口</title>
</head>
<body>
<div>
<button onclick="getImage()">获取图片</button>
<button onclick="downloadImage()">下载图片</button>
</div>
<div id="image-container"></div>
<script>
let url = "";
const getImage = (e) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:3000/image/blob", true);
xhr.responseType = "blob";
xhr.onload = function (e) {
if (this.status === 200) {
url = URL.createObjectURL(this.response);
const img = document.createElement("img");
img.src = url;
img.style.width = 500 + "px";
img.style.margin = "20px";
const container = document.getElementById("image-container");
container.appendChild(img);
const reader = new FileReader();
reader.readAsDataURL(this.response);
reader.onload = function () {};
}
};
xhr.send();
};
const downloadImage = (e) => {
if (!url) {
alert("请先获取图片");
return;
}
const a = document.createElement("a");
a.href = url;
a.download = "dog";
a.click();
};
</script>
</body>
</html>
text 类型请求
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>请求接口</title>
</head>
<body>
<div id="container">
<input type="text" id="text" />
<button onclick="send()">发送</button>
</div>
<script>
const send = (e) => {
const value = document.getElementById("text").value;
if (!value) return alert("请输入内容");
const xhr = new XMLHttpRequest();
xhr.open("POST", "http://127.0.0.1:3000/post/text", true);
xhr.responseType = "text";
xhr.onload = function (e) {
if (this.status === 200) {
const container = document.getElementById("container");
const span = document.createElement("span");
span.innerText = this.response;
container.appendChild(span);
}
};
xhr.onerror = (e) => {
console.log(e);
};
xhr.send(value);
};
</script>
</body>
</html>
发送表单
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>请求接口</title>
</head>
<body>
<div id="container">
<label for="name">姓名</label>
<input type="text" id="name" />
<hr />
<label for="age">年龄</label>
<input type="number" id="age" />
<hr />
<button onclick="send()">发送</button>
<hr />
<div id="result"></div>
</div>
<script>
const send = (e) => {
const name = document.getElementById("name").value;
const age = document.getElementById("age").value;
const xhr = new XMLHttpRequest();
xhr.open("POST", "http://127.0.0.1:3000/post/form", true);
const form = new FormData();
form.append("name", name);
form.append("age", age);
xhr.onload = function (e) {
if (this.status === 200) {
const result = document.getElementById("result");
const res = JSON.parse(this.response);
const span = document.createElement("span");
span.innerText = `姓名:${res.name},年龄:${res.age}`;
result.appendChild(span);
}
};
xhr.onerror = function (e) {
alert("请求失败");
};
xhr.send(form);
};
</script>
</body>
</html>
上传文件
通过 FormData 对象,不仅可以向服务端发送表单数据,而且可以向服务端上传文件。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>请求接口</title>
</head>
<body>
<div id="container">
<input type="file" id="upload" />
<br />
<input type="button" value="发送" onclick="send()" />
</div>
<script>
function send() {
const dom = document.getElementById("upload");
console.log(dom.files, 17);
const [file] = dom.files;
const form = new FormData();
form.append("file", file);
const xhr = new XMLHttpRequest();
xhr.open("POST", "http://127.0.0.1:3000/post/form/upload", true);
xhr.onload = function () {
if (this.status === 200) {
alex("发送成功");
}
};
xhr.send(form);
}
</script>
</body>
</html>
发送 Blob 对象
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>请求接口</title>
</head>
<body>
<div id="container">
<button onclick="copyDom()">复制页面元素</button>
<br />
<progress min="0" max="100" id="progress"></progress>
<hr />
<div id="result"></div>
</div>
<script>
function copyDom() {
console.log(document.documentElement.outerHTML);
const el = document.documentElement.outerHTML;
const blob = new Blob([el]);
console.log(blob);
const xhr = new XMLHttpRequest();
xhr.open("POST", "http://127.0.0.1:3000/post/el", true);
xhr.responseType = "blob";
const pg = document.getElementById("progress");
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) {
pg.value = (e.loaded / e.total) * 100;
const container = document.getElementById("result");
container.innerHTML = `已完成进度:${pg.value}%`;
}
};
xhr.send(blob);
}
</script>
</body>
</html>
发送 ArrayBuffer
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>send</title>
</head>
<body>
<input type="button" onclick="postArrayBuffer()" value="提交" />
<br />
<div id="checkBox"></div>
<hr />
<input type="button" onclick="getResult()" value="获取" />
<script>
function create(n) {
const container = document.getElementById("checkBox");
for (let i = 1; i <= n; i++) {
const label = document.createElement("label");
label.style.display = "block"; // 每个复选框占一行
label.style.margin = "5px 0";
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.name = "check";
// 直接在label中添加文本
label.appendChild(checkbox);
label.appendChild(document.createTextNode(`checkBox-${i}`));
container.appendChild(label);
}
}
create(10);
function postArrayBuffer() {
const checkbox = document.getElementsByName("check");
const arr = new Array();
for (let i = 0; i < checkbox.length; i++) {
if (checkbox[i].checked) {
arr.push(i);
}
}
console.log(arr, 36);
const buffer = new ArrayBuffer(arr.length);
const byteBufer = new Int8Array(buffer);
for (let i = 0; i < arr.length; i++) {
byteBufer[i] = arr[i];
}
const xhr = new XMLHttpRequest();
xhr.open("POST", "http://127.0.0.1:3000/post/buffer", true);
xhr.send(buffer);
xhr.onload = function () {
if (this.status === 200) {
console.log(this, 53);
}
};
}
function getResult() {
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:3000/get/arr", true);
xhr.onload = function () {
if (this.status === 200) {
console.log(this.response);
}
};
xhr.send();
}
</script>
</body>
</html>
XMLHttpRequest 相关 API
- 创建与初始化
js
const xhr = new XMLHttpRequest();
// 初始化请求
xhr.open(method, url, async, user / password);
-
method:Http 方法(GET、POST、PUT、DELETE 等)
-
url:请求地址
-
async:是否异步(默认 true)
-
user/password:HTTP 认证凭据
-
发送请求
xhr.send()
-
设置请求头
- 必须在 open 后,send 前
- xhr.setRequestHeader(header,value)
- xhr.setRequestHeader('Content-Type','application/json')
-
获取响应头
const contentType = xhr.getResponseHeader('Content-Type')
-
终止请求
xhr.abort()
-
readyState(请求状态)
- 0 未打开
- 1 已打开
- 2 已调用,响应头已接收
- 3 下载中,响应体正在加载
- 4 请求完成
-
status 和 statesText
- 状态码
- 状态文本
-
响应数据
- xhr.responseText 响应文本
- xhr.responseXML 响应 XML
- xhr.response 响应数据(根据 response 而定)
- xhr.responseType
- text
- json
- document
- blob
- arraybuffer
-
事件
- xhr.onreadystatechange 状态改变触发
- onloadend 请求完成(无论成功或失败)
- onload 请求成功完成
- onerror 请求出错
- ontimeout 请求超时
- onprogress 上传或下载进度 上传进度需在 send 前设置
跨域数据请求
实现 AJAX 方式的跨域数据请求,需要在被请求域中提供一个用于响应请求的服务端脚本文件,在响应头信息中添加
Access-Control-Allow-Origin
js
const express = require("express");
const app = express();
// 允许的域名列表
const allowedOrigins = [
"http://localhost:3000",
"http://localhost:5173", // Vite 默认端口
"http://127.0.0.1:5500", // Live Server
"http://127.0.0.1:8080", // 其他本地服务器
"https://yourdomain.com",
"https://www.yourdomain.com",
"https://staging.yourdomain.com",
];
app.use((req, res, next) => {
const origin = req.headers.origin;
// 检查请求的 origin 是否在允许列表中
if (allowedOrigins.includes(origin)) {
res.header("Access-Control-Allow-Origin", origin);
}
// 或者允许所有(不推荐生产环境)
// res.header('Access-Control-Allow-Origin', '*');
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.header("Access-Control-Allow-Credentials", "true"); // 如果需要带cookie
// 处理预检请求
if (req.method === "OPTIONS") {
return res.sendStatus(200);
}
next();
});