💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 在线和离线事件 ### 概述 为了构建一个支持离线的 web 应用,你需要知道你的应用何时真正处于离线状态。同时,你还需要知道应用何时重新回到了「在线」状态。实际上,我们可以把需求分解成如下内容: 1. 你需要知道用户何时回到在线状态,这样你就可以与服务器重新同步。 2. 你需要知道用户何时处于离线状态,这样你就可以将对服务器的请求放入队列中以便稍后使用。 ### API **`navigator.onLine`** [`navigator.onLine`](https://developer.mozilla.org/en/DOM/window.navigator.onLine) 是一个值为 `true`/`false` \(`true` 表示在线, `false` 表示离线\) 的属性。当用户通过选择对应的菜单项 \(Firefox 中为 文件 -&gt; 离线工作\) 切换到「离线模式」时,这个值就会被更新。 #### 注意 如果浏览器没有实现该 API,你可以使用其他方式来检测是否离线,包括 [AppCache 错误事件](http://www.html5rocks.com/en/mobile/workingoffthegrid.html#toc-appcache) 和 [XMLHttpRequest 的响应](http://www.html5rocks.com/en/mobile/workingoffthegrid.html#toc-xml-http-request)。 ### Storage DOM存储的机制是通过存储字符串类型的键/值对,来提供一种安全的存取方式.这个附加功能的目标是提供一个全面的,可以用来创建交互式应用程序的方法\(包括那些高级功能,例如可以离线工作一段时间\). 基于Mozilla的浏览器, Internet Explorer 8+, Safari 4+ 以及 Chrome 都提供了自己的DOM存储规范的实现. \(如果你想让自己的代码兼容多个浏览器,则你需要照顾一下老版本的IE浏览器,IE下有一个类似的特性,在IE8之前版本也可以使用,叫做"[userData behavior](http://msdn.microsoft.com/zh-cn/library/ms531424%28VS.85%29.aspx)",它允许你在多重浏览器会话中永久地保存数据.\) > 以下所提到的对象都是全局对象,作为 [window 对象](https://developer.mozilla.org/zh-cn/DOM/window) 的属性存在。这意味着可以以 `sessionStorage` 或者 `window.sessionStorage` 的形式访问这些对象`。`\(这点很重要,因为可以使用iframe来存储、访问除了直接包含在页面的数据之外的附加数据。\) #### Storage 它是所有Storage实例(`sessionStorage`和`globalStorage[location.hostname]`)的构造函数,设置`Storage.prototype.removeKey = function(key) { this.removeItem(this.key(key)) }`,可通过`localStorage.removeKey`和`sessionStorage.removeKey`访问到。 > 虽然可以直接通过标准的 JavaScript 属性访问方法来设置和读取值,但是推荐的做法是使用 getItem 和 setItem 方法。 > > 所有数据在被保存到下面将要介绍的任何一个存储器之前,都将通过它的`.toString`方法被转换成字符串。所以一个普通对象将会被存储为 `"[object Object]"`,而不是对象本身或者它的 JSON 形式。使用浏览器自身提供的 JSON 解析和序列化方法来存取对象是比较好的,也是比较常见的方法。 #### sessionStorage `sessionStorage`是个全局对象,它维护着在页面会话\(page session\)期间有效的存储空间。只要浏览器开着,页面会话周期就会一直持续。当页面重新载入\(reload\)或者被恢复\(restores\)时,页面会话也是一直存在的。每在新标签或者新窗口中打开一个新页面,都会初始化一个新的会话。 ``` // 保存数据到当前会话的存储空间 sessionStorage.setItem("username", "John"); // 访问数据 alert( "username = " + sessionStorage.getItem("username")); ``` **例子:** 自动保存一个文本域中的内容,如果浏览器被意外刷新,则恢复该文本域中的内容,所以不会丢失任何输入的数据。 ``` // 获取到我们要循环保存的文本域 var field = document.getElementById("field"); // 查看是否有一个自动保存的值 // (只在浏览器被意外刷新时) if ( sessionStorage.getItem("autosave")) { // 恢复文本域中的内容 field.value = sessionStorage.getItem("autosave"); } // 每隔一秒检查文本域中的内容 setInterval(function(){ // 并将文本域的值保存到session storage对象中 sessionStorage.setItem("autosave", field.value); }, 1000); ``` #### localStorage `localStorage`和`sessionStorage`一样是用来键值对规则但它是持久性的。 > 当浏览器进入私人模式\(private browsing mode,Google Chrome 上对应的应该是叫隐身模式\)的时候,会创建一个新的、临时的、空的数据库,用以存储本地数据\(local storage data\)。当浏览器关闭时,里面的所有数据都将被丢弃。 ## IndexedDB **IndexedDB** 是一种低级API,用于客户端存储大量结构化数据\(包括, 文件/ blobs\)。该API使用索引来实现对该数据的高性能搜索。虽然 [Web Storage](https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Storage_API)对于存储较少量的数据很有用,但对于存储更大量的结构化数据来说,这种方法不太有用。**IndexedDB**提供了一个解决方案。 > IndexedDB API是强大的,但对于简单的情况可能看起来太复杂。如果你更喜欢一个简单的API,请尝试类库,如[localForage](https://localforage.github.io/localForage/), [dexie.js](http://www.dexie.org/), 和 [ZangoDB](https://github.com/erikolson186/zangodb),使IndexedDB更方便用户。 ### 关键概念和用法 {#关键概念和用法} IndexedDB是一个事务型数据库系统,类似于基于SQL的RDBMS。 然而不同的是它使用固定列表,IndexedDB是一个基于JavaScript的面向对象的数据库。 IndexedDB允许您存储和检索用键索引的对象; 可以存储[structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/The_structured_clone_algorithm)支持的任何对象。 您只需要指定数据库模式,打开与数据库的连接,然后检索和更新一系列事务中的数据。 IndexedDB 分别为同步和异步访问提供了单独的 API 。同步 API 本来是要用于仅供 [Web Workers](https://developer.mozilla.org/en-US/docs/DOM/Worker)内部使用,但是还没有被任何浏览器所实现。异步 API 在 Web Workers 内部和外部都可以使用。 ### 异步 API {#异步_API} 异步 API 方法调用完后会立即返回,而不会阻塞调用线程。要异步访问数据库,要调用[window](https://developer.mozilla.org/en-US/docs/DOM/window)对象[`indexedDB`](https://developer.mozilla.org/en-US/docs/IndexedDB/IDBEnvironment#attr_indexedDB)属性的[`open`](https://developer.mozilla.org/en-US/docs/IndexedDB/IDBFactory#open)`()`方法。该方法返回一个 IDBRequest 对象 \(IDBOpenDBRequest\);异步操作通过在 IDBRequest 对象上触发事件来和调用程序进行通信。 > `indexedDB`对象在旧版本的浏览器上是带有前缀的 \(在 Gecko &lt;16的情况下是`mozIndexedDB`属性,Chrome 中是`webkitIndexedDB`,以及IE10 的 `msIndexedDB`\)。 ## 在web应用使用文件 使用在HTML5中被加入DOM的File API,使页面内容请求用户选择本地文件,然后读取这些文件的内容成为可能。选择可以由使用 HTML[`<input>`](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input)元素或者通过drag 和 drop实现。 ### 访问选择的文件 {#访问选择的文件} ``` <input type="file" id="input"> ``` File API使访问包含[`File`](https://developer.mozilla.org/zh-CN/docs/Web/API/File)对象的 [`FileList`](https://developer.mozilla.org/zh-CN/docs/Web/API/FileList)成为可能,FileList代表被用户选择的文件。 如果用户只选择了一个文件,那么只需要考虑FileList中的第一个File对象。 使用传统的DOM选择器访问一个被选择的文件。 ``` var selectedFile = document.getElementById('input').files[0]; ``` 通过change事件访问被选择的文件,可以通过change事件访问[`FileList`](https://developer.mozilla.org/zh-CN/docs/Web/API/FileList)(但不是强制的)。 当用户选择一个文件时,`handleFiles()`方法会被调用,同时传入包含[`File`](https://developer.mozilla.org/zh-CN/docs/Web/API/File)对象的[`FileList`](https://developer.mozilla.org/zh-CN/docs/Web/API/FileList)对象。[`File`](https://developer.mozilla.org/zh-CN/docs/Web/API/File)对象代表被用户选择的文件。如果你想让用户选择多个文件,只需在input元素上使用`multiple`属性: ``` <input type="file" id="input" multiple onchange="handleFiles(this.files)"> ``` ### 获取被选择文件的信息 {#获取被选择文件的信息} [`FileList`](https://developer.mozilla.org/zh-CN/docs/Web/API/FileList)对象由DOM提供,列出了所有用户选择的文件,每一个代表了一个[`File`](https://developer.mozilla.org/zh-CN/docs/Web/API/File)对象。你可以通过检查文件列表的length属性决定用户可以选则多少文件。 [`File`](https://developer.mozilla.org/zh-CN/docs/Web/API/File)对象提供的三个属性,包含了关于文件的一些有价值的信息。 name:文件名称,只读字符串。只包含文件名称,不包含任何路径信息。 size:文件大小,使用bytes描述,是一个只读的64位整数。 type:文件的MIME type,只读字符串,当类型不能确定时为`""。` ### 通过click\(\)方法使用隐藏的file input元素 {#通过click()方法使用隐藏的file_input元素} ``` <input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)"> <a href="#" id="fileSelect">Select some files</a> <script> var fileSelect = document.getElementById("fileSelect"), fileElem = document.getElementById("fileElem"); fileSelect.addEventListener("click", function (e) { if (fileElem) { fileElem.click(); } e.preventDefault(); // prevent navigation to "#" }, false); </script> ``` ### 使用label元素来触发一个隐藏的input元素对应的事件 {#使用label元素来触发一个隐藏的input元素对应的事件} ``` <input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)"> <label for="fileElem">Select some files</label> ``` ### 显示用户选择的图片的缩略图 {#例子:显示用户选择的图片的缩略图} ``` function handleFiles(files) { for (var i = 0; i < files.length; i++) { var file = files[i]; var imageType = /^image\//; if (!imageType.test(file.type)) { continue; } var img = document.createElement("img"); img.classList.add("obj"); img.file = file; preview.appendChild(img); // Assuming that "preview" is the div output where the content will be displayed. var reader = new FileReader(); reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img); reader.readAsDataURL(file); } } ``` 这里我们循环处理用户选择的文件,看每个文件的type属性是不是image(通过正则表达式来匹配MIME类型字符串模式"`image/*`")。 对每个文件而言,如果它是图片,我们就创建一个img元素。可以使用css来创建一个漂亮的边框或阴影来显示图片的具体大小,这儿就不具体做了。 为了在DOM树中更容易地找到他们,每个图片元素都被添加了一个名为obj的class。我们还给每个图片添加了file属性使它具有[`File`](https://developer.mozilla.org/zh-CN/docs/Web/API/File);这样做可以让我们拿到稍后需要实际上传的图片。我们在预览页中使用[`Node.appendChild()`](https://developer.mozilla.org/zh-CN/docs/Web/API/Node/appendChild)来添加新的缩略图。 接下来,我们创建了[`FileReader`](https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader)来处理异步的图片加载并把他赋给图片元素。在创建一个新的`FileReader`对象后,我们新建了它的`onload`函数,然后调用`readAsDataURL`函数在后台开始读取文件的操作。当整个图片文件被全部加载完后,他们被转换成了一个被传递到onload回调函数的`data:`URL。我们再执行常规操作将img元素的src属性设置为刚刚加载完毕的URL,使得图像可以显示在用户屏幕上的缩略图中。 ### 使用URLs对象显示图片 > **1、 URL.createObjectURL\(\) **静态方法会创建一个 [`DOMString`](https://developer.mozilla.org/zh-CN/docs/Web/API/DOMString),它的 URL 表示参数中的对象。这个 URL 的生命周期和创建它的窗口中的 [`document`](https://developer.mozilla.org/zh-CN/docs/Web/API/Document)绑定。 > > 2、在每次调用 `createObjectURL()`方法时,都会创建一个新的 URL 对象,即使你已经用相同的对象作为参数创建过。当不再需要这些 URL 对象时,每个对象必须通过调用 [`URL.revokeObjectURL()`](https://developer.mozilla.org/zh-CN/docs/Web/API/URL/revokeObjectURL) 方法来释放。浏览器会在文档退出的时候自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。 ``` <input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)"> <a href="#" id="fileSelect">Select some files</a> <div id="fileList"> <p>No files selected!</p> </div> ``` ``` window.URL = window.URL || window.webkitURL; var fileSelect = document.getElementById("fileSelect"), fileElem = document.getElementById("fileElem"), fileList = document.getElementById("fileList"); fileSelect.addEventListener("click", function (e) { if (fileElem) { fileElem.click(); } e.preventDefault(); // prevent navigation to "#" }, false); function handleFiles(files) { if (!files.length) { fileList.innerHTML = "<p>No files selected!</p>"; } else { fileList.innerHTML = ""; var list = document.createElement("ul"); fileList.appendChild(list); for (var i = 0; i < files.length; i++) { var li = document.createElement("li"); list.appendChild(li); var img = document.createElement("img"); img.src = window.URL.createObjectURL(files[i]); img.height = 60; img.onload = function() { window.URL.revokeObjectURL(this.src); } li.appendChild(img); var info = document.createElement("span"); info.innerHTML = files[i].name + ": " + files[i].size + " bytes"; li.appendChild(info); } } } ```