【问题标题】:Save HTML locally with Javascript使用 Javascript 在本地保存 HTML
【发布时间】:2014-11-27 19:31:11
【问题描述】:

我知道,出于安全原因,客户端 Javascript 无法将数据写入本地文件系统。

使用 Javascript 在本地保存数据的唯一方法似乎是使用 cookie,localStorage,或者允许用户下载文件(使用 “保存...” 对话框或浏览器的默认下载文件夹)。

但是否有可能,在非常特殊的情况下,本地使用file:///D:/test/index.html 之类的 URL(而不是通过互联网)访问文件来写入数据 本地? (没有任何服务器语言,甚至根本没有任何服务器:只是本地浏览 HTML 文件)

例如,是否可以通过点击此处的保存

  <div contenteditable="true" style="height:200px;">Content editable - edit me and save!</div>
  <button>Save</button>

...这样的 HTML 文件(通过file:///D:/test/index.html 访问)被其新内容覆盖? (即,当按下 SAVE 时,应该更新 local HTML 文件。



TL;DR:在本地访问 HTML 页面时,是否可以通过 Javascript 保存文件?

注意:我希望能够静默保存,而不是提出一个下载/保存对话框,用户必须在其中选择下载位置,然后“你确定要覆盖”等。


编辑:为什么要问这个问题?我正在做一个浏览器内记事本,我可以在没有任何服务器(没有 Apache,没有 PHP)的情况下在本地运行。我需要能够轻松保存,而不必处理对话框“你想在哪里下载文件?”并且必须始终重新浏览到同一文件夹以覆盖当前正在编辑的文件。我想要一个简单的 UX,就像在任何记事本程序中一样:CTRL+S 完成,当前文件被保存! (例如:每次执行“保存”时,MS Word 都不会要求浏览您要保存文件的位置:CTRL+S,完成!)

【问题讨论】:

  • 我相信您能做到这一点的唯一方法是将内容提交给一些服务器端处理(例如 PHP、Python 等)并让本地脚本更新文件。
  • @JacobEwing 并且没有服务器端处理?我的意思是 local 浏览 HTML 文件,即浏览器打开 c:\myproject\index.html 而没有 Apache 服务器/没有 PHP 服务器
  • 如果您在浏览器中执行脚本(甚至从本地机器加载),则无法告诉它修改本地文件(阻止此操作的是浏览器,而不是服务器)。您也许可以使用其他软件执行它,在这种情况下,我不知道还有哪些限制。 stackoverflow.com/questions/2941411/… 有更多信息可能会有所帮助。
  • @Basj: re: node-webkit,它产生的东西不需要在端点上安装 nore-webkit 本身就可以运行。 HTA 已准备好为 Windows 提供,您可以使用 FSO 读取/写入文件,只要文件具有 标记即可。还有 chrome 打包的应用程序和 firefox 应用程序。和科尔多瓦。
  • @kzaiwo 我刚开始赏金,看看是否有新的方法。

标签: javascript html serialization


【解决方案1】:

你可以只使用 Blob 函数:

function save() {
  var htmlContent = ["your-content-here"];
  var bl = new Blob(htmlContent, {type: "text/html"});
  var a = document.createElement("a");
  a.href = URL.createObjectURL(bl);
  a.download = "your-download-name-here.html";
  a.hidden = true;
  document.body.appendChild(a);
  a.innerHTML = "something random - nobody will see this, it doesn't matter what you put here";
  a.click();
}

您的文件将保存。

【讨论】:

  • 另存为怎么样?我喜欢这个解决方案,它很整洁。谢谢。
  • 最好添加一个htmlContent示例。 var htmlContent = [ "&lt;head&gt;&lt;meta charset='utf-8'&gt;&lt;title&gt;Test&lt;/title&gt;&lt;/head&gt;", "&lt;style&gt;.container{max-width: 940px;margin: 0 auto;}&lt;/style&gt;", "&lt;body&gt;&lt;div class="container"&gt;'Content Here'&lt;/div&gt;&lt;/body&gt;" ];
  • 仍然要求我确认下载。
【解决方案2】:

标准答案,来自the W3C File API Standard

用户代理应该提供一个向脚本公开的 API,以公开上述功能。每当与文件系统发生交互时,UI 都会通知用户,使用户能够完全取消或中止事务。用户会收到任何文件选择的通知,并且可以取消这些选择。在没有用户干预的情况下,不会静默调用这些 API。

基本上,由于安全设置,每当您下载文件时,浏览器都会确保用户确实想要保存文件。浏览器并不能真正区分您计算机上的 JavaScript 和 Web 服务器上的 JavaScript。唯一的区别是浏览器如何访问文件,因此将页面存储在本地不会有任何区别。

解决方法: 但是,您可以只将 &lt;div&gt; 的 innerHTML 存储在 cookie 中。当用户回来时,您可以从 cookie 中将其加载回来。虽然它并不完全将文件保存到用户的计算机,但它应该具有与覆盖文件相同的效果。当用户返回时,他们将看到他们上次输入的内容。缺点是,如果用户清除他们的网站数据,他们的信息将会丢失。由于忽略用户清除本地存储的请求也是一个安全问题,因此确实没有办法解决。

但是,您也可以执行以下操作:

  • 使用 Java 小程序
  • 使用其他类型的小程序
  • 创建桌面(非基于 Web)应用程序
  • 请记住在清除网站数据时保存文件。您可以创建一个提醒,在您退出页面时弹出并提醒您,甚至为您打开保存窗口。

使用 cookie:可以在本地页面上使用 JavaScript cookie。只需将其放入文件并在浏览器中打开即可:

<!DOCTYPE html>
<html>

<head>
</head>

<body>
  <p id="timesVisited"></p>
  <script type="text/javascript">
    var timesVisited = parseInt(document.cookie.split("=")[1]);
    if (isNaN(timesVisited)) timesVisited = 0;
    timesVisited++;
    document.cookie = "timesVisited=" + timesVisited;
    document.getElementById("timesVisited").innerHTML = "You ran this snippet " + timesVisited + " times.";
  </script>
</body>

</html>

【讨论】:

  • 好的,谢谢,我就是这么想的(因为安全原因不可能)。如果可能的话,我会制作一个简单的 HTML 本地记事本供个人使用:gget.it/59b78rl1/NFBF33C.HTML(当然还有更多功能,感谢 Javascript)。由于无法一键保存,所以这个项目根本不可能:(
  • @Basj 不过,您仍然可以使用 cookie 实现相同的效果。您只需要小心不要清除其网络数据,否则您会丢失笔记。也可能有大小限制,但对于简单的记事本来说可能不是什么大问题。
  • @Basj 你也可以使用本地存储。 (更多可用空间)
  • 到目前为止,我确实使用localStorage。但是,如果我清理历史记录,丢失所有数据,并且必须记住导出记事本(带有一些下载文件功能),这使得它实际上无趣和无用......不幸的是......
  • @Basj 还有很多其他选项,我会将它们添加到我的答案中。
【解决方案3】:

Chromium 的文件系统访问 API(2019 年推出)

有一个相对较新的非标准File System Access API(不要与早期的File and Directory Entries APIFile System API 混淆)。看起来它是在 2019/2020 年在 Chromium/Chrome 中引入的,并且在 Firefox 或 Safari 中不支持。

使用此 API 时,本地打开的页面可以打开/保存其他本地文件并使用页面中文件的数据。它确实需要初始权限才能保存,但是当用户在页面上时,特定文件的后续保存会“静默”进行。用户还可以授予特定目录的权限,随后对该目录的读取和写入不需要批准。用户关闭网页的所有选项卡并重新打开页面后,需要再次批准。

您可以在https://web.dev/file-system-access/ 阅读有关此新 API 的更多信息。它旨在用于制作更强大的 Web 应用程序。

需要注意的几点:

  • 默认情况下,它需要一个安全的上下文才能运行。在 https、localhost 或通过 file:// 运行它应该可以工作。

  • 您可以通过使用DataTransferItem.getAsFileSystemHandle拖放文件来获取文件句柄

  • 最初读取或保存文件需要用户批准,并且只能通过用户交互来启动。之后,后续的读取和保存都不需要批准,直到站点再次打开。

  • 文件句柄可以保存在页面中(因此,如果您正在编辑 'path/to/file.html' 并重新加载页面,它将能够引用文件)。它们似乎不能被字符串化,因此通过 IndexedDB 之类的东西存储(有关更多信息,请参阅this answer)。使用存储的句柄进行读/写需要用户交互和用户批准。

这里有一些简单的例子。它们似乎没有在跨域 iframe 中运行,因此您可能需要将它们保存为 html 文件并在 Chrome/Chromium 中打开它们。

通过拖放打开和保存(无外部库):

<body>
<div><button id="open">Open</button><button id="save">Save</button></div>
<textarea id="editor" rows=10 cols=40></textarea>
<script>
let openButton = document.getElementById('open');
let saveButton = document.getElementById('save');
let editor = document.getElementById('editor');
let fileHandle;
async function openFile() {
  try {
    [fileHandle] = await window.showOpenFilePicker();
    await restoreFromFile(fileHandle);
  } catch (e) {
    // might be user canceled
  }
}
async function restoreFromFile() {
  let file = await fileHandle.getFile();
  let text = await file.text();
  editor.value = text;
}
async function saveFile() {
  var saveValue = editor.value;
  if (!fileHandle) {
    try {
      fileHandle = await window.showSaveFilePicker();
    } catch (e) {
      // might be user canceled
    }
  }
  if (!fileHandle || !await verifyPermissions(fileHandle)) {
    return;
  }
  let writableStream = await fileHandle.createWritable();
  await writableStream.write(saveValue);
  await writableStream.close();
}

async function verifyPermissions(handle) {
  if (await handle.queryPermission({ mode: 'readwrite' }) === 'granted') {
    return true;
  }
  if (await handle.requestPermission({ mode: 'readwrite' }) === 'granted') {
    return true;
  }
  return false;
}
document.body.addEventListener('dragover', function (e) {
  e.preventDefault();
});
document.body.addEventListener('drop', async function (e) {
  e.preventDefault();
  for (const item of e.dataTransfer.items) {
    if (item.kind === 'file') {
      let entry = await item.getAsFileSystemHandle();
      if (entry.kind === 'file') {
        fileHandle = entry;
        restoreFromFile();
      } else if (entry.kind === 'directory') {
        // handle directory
      }
    }
  }
});
openButton.addEventListener('click', openFile);
saveButton.addEventListener('click', saveFile);
</script>
</body>

使用idb-keyval 存储和检索文件句柄:

存储文件句柄可能很棘手,因为它们不能被取消字符串化,though apparently they can be used with IndexedDB and mostly with history.state。对于这个例子,我们将使用idb-keyval 来访问 IndexedDB 以存储文件句柄。要查看它是否正常工作,请打开或保存文件,然后重新加载页面并按“恢复”按钮。这个例子使用了来自https://stackoverflow.com/a/65938910/的一些代码。

<body>
<script src="https://unpkg.com/idb-keyval@6.1.0/dist/umd.js"></script>
<div><button id="restore" style="display:none">Restore</button><button id="open">Open</button><button id="save">Save</button></div>
<textarea id="editor" rows=10 cols=40></textarea>
<script>
let restoreButton = document.getElementById('restore');
let openButton = document.getElementById('open');
let saveButton = document.getElementById('save');
let editor = document.getElementById('editor');
let fileHandle;
async function openFile() {
  try {
    [fileHandle] = await window.showOpenFilePicker();
    await restoreFromFile(fileHandle);
  } catch (e) {
    // might be user canceled
  }
}
async function restoreFromFile() {
  let file = await fileHandle.getFile();
  let text = await file.text();
  await idbKeyval.set('file', fileHandle);
  editor.value = text;  
  restoreButton.style.display = 'none';
}
async function saveFile() {
  var saveValue = editor.value;
  if (!fileHandle) {
    try {
      fileHandle = await window.showSaveFilePicker();
      await idbKeyval.set('file', fileHandle);
    } catch (e) {
      // might be user canceled
    }
  }
  if (!fileHandle || !await verifyPermissions(fileHandle)) {
    return;
  }
  let writableStream = await fileHandle.createWritable();
  await writableStream.write(saveValue);
  await writableStream.close();
  restoreButton.style.display = 'none';
}

async function verifyPermissions(handle) {
  if (await handle.queryPermission({ mode: 'readwrite' }) === 'granted') {
    return true;
  }
  if (await handle.requestPermission({ mode: 'readwrite' }) === 'granted') {
    return true;
  }
  return false;
}
async function init() {
  var previousFileHandle = await idbKeyval.get('file');
  if (previousFileHandle) {
    restoreButton.style.display = 'inline-block';
    restoreButton.addEventListener('click', async function (e) {
      if (await verifyPermissions(previousFileHandle)) {
        fileHandle = previousFileHandle;
        await restoreFromFile();
      }
    });
  }
  document.body.addEventListener('dragover', function (e) {
    e.preventDefault();
  });
  document.body.addEventListener('drop', async function (e) {
    e.preventDefault();
    for (const item of e.dataTransfer.items) {
      console.log(item);
      if (item.kind === 'file') {
        let entry = await item.getAsFileSystemHandle();
        if (entry.kind === 'file') {
          fileHandle = entry;
          restoreFromFile();
        } else if (entry.kind === 'directory') {
          // handle directory
        }
      }
    }
  });
  openButton.addEventListener('click', openFile);
  saveButton.addEventListener('click', saveFile);
}
init();
</script>
</body>

附加说明

Firefox 和 Safari 支持似乎不太可能,至少在短期内是这样。见https://github.com/mozilla/standards-positions/issues/154https://lists.webkit.org/pipermail/webkit-dev/2020-August/031362.html

【讨论】:

  • 很好的解决方案@Steve!最后一件事,您知道是否可以通过拖放到浏览器窗口中打开文件?这将使其成为创建浏览器内文本编辑器等的完美解决方案。
  • @Basj,应该可以通过DataTransferItem.getAsFileSystemHandle(也提到here)。当我有机会测试它时,我会更新我的答案。
【解决方案4】:

是的,这是可能的。

在您的示例中,您已经在使用 ContentEditable 并且该属性的大多数教程都有某种 localStorage 示例,即。 http://www.html5tuts.co.uk/demos/localstorage/

在页面加载时,脚本应检查 localStorage 的数据,如果为真,则填充元素。单击保存按钮时,内容的任何更改都可以保存在 localStorage 中(或在链接示例中自动保存,使用模糊和焦点)。此外,您可以使用此 sn-p 检查天气用户在线或离线,并根据状态修改您的逻辑:

// check if online/offline
// http://www.kirupa.com/html5/check_if_internet_connection_exists_in_javascript.htm
function doesConnectionExist() {
    var xhr = new XMLHttpRequest();
    var file = "http://www.yoursite.com/somefile.png";
    var randomNum = Math.round(Math.random() * 10000);

    xhr.open('HEAD', file + "?rand=" + randomNum, false);

    try {
        xhr.send();

        if (xhr.status >= 200 && xhr.status < 304) {
            return true;
        } else {
            return false;
        }
    } catch (e) {
        return false;
    }
}

编辑:更高级的 localStorage 版本是 Mozilla localForage,它允许存储字符串以外的其他类型的数据。

【讨论】:

  • 我已经用localStorage 进行了测试,但是当你清除浏览器历史记录时修改会丢失......所以它不是一个真正的解决方案,我可以在本地运行而无需任何服务器(没有 apache,没有 php)。
  • @Basj 好的,我现在在 kittycat3141 答案上看到了您的 cmets。正如那里所解释的,任何浏览器存储技术(cookies、localStorage 或 appcache)都将离线工作,但如果用户决定清除缓存和浏览数据,则不会持续存在。也许您应该考虑将 3rd 方存储服务集成到您的应用程序中并使用它来存储数据文件? Dropbox (dropbox.com/developers/dropins) 和 Google Drive (developers.google.com/drive/web) 都有相当不错的 API,您可以使用。
【解决方案5】:

您可以保存文件,并使用 FileSystem-API 和 webkit 使其持久化。您必须使用 chrome 浏览器,它不是一种标准技术,但我认为它完全符合您的要求。这是一个很棒的教程来展示如何做到这一点http://www.noupe.com/design/html5-filesystem-api-create-files-store-locally-using-javascript-webkit.html

为了表明它的主题,它开始向您展示如何使文件持久保存...

window.webkitRequestFileSystem(window.PERSISTENT , 1024*1024, SaveDatFileBro);

【讨论】:

  • 有了这个技术和API,在Chrome中打开的file:///D:/test/index.html可以写到file:///D:/test/test.txt吗?
【解决方案6】:

将您的 HTML 内容转换为数据 uri 字符串,并设置为锚元素的 href 属性。不要忘记将文件名指定为download 属性。

这是一个简单的例子:

<a>click to download</a>
<script>
    var anchor = document.querySelector('a');
    anchor.setAttribute('download', 'example.html');
    anchor.setAttribute('href', 'data:text/html;charset=UTF-8,<p>asdf</p>');
</script>

只需在浏览器中尝试,无需服务器。

【讨论】:

  • 感谢@LeoDeng,但我希望能够静默保存,而不是建议用户必须选择下载位置的下载对话框。还有其他想法吗?
  • 这取决于用户的浏览器设置。如果你要绕过它,据我所知这是不可能的。
  • 刚刚解决了编码的一个小问题,所以这里值得一提。就在数据 url 中的 html 之前,添加 escape('\xEF\xBB\xBF'),所以它真的变成了 UTF-8,并且语言特定的字符(在我的例子中是土耳其语)将显示正常。
【解决方案7】:

看看这个:) Download File Using Javascript/jQuery 应该有你需要的一切。如果您仍然需要帮助或者这不是您需要的解决方案,请告诉我 ;)

【讨论】:

  • 谢谢,但这并不是我所需要的。我正在寻找没有显示下载对话框的东西。具体原因可以看我上个cmets on kittycat3141的回答。
【解决方案8】:

是的,它可能的。举例证明:

TiddlyFox: 允许通过插件修改本地文件。 (source code) (extension page):

TiddlyFox 是 Mozilla Firefox 的扩展,它支持 TiddlyWiki 将更改直接保存到文件系统。

Todo.html: 将编辑内容保存到自身的 HTML 文件。目前,它仅适用于 Internet Explorer,您必须在首次打开文件时确认一些安全对话框。 (source code) (functional demo)。

确认 todo.html 的步骤实际上将更改保存到本地:

  1. todo.html保存到本地硬盘
  2. 使用 Internet Explorer 打开。接受所有安全对话框。
  3. 键入命令todo add TEST(todo.html 模拟todo.txt-CLI 的命令行界面)
  4. 检查 todo.html 文件是否添加了“TEST”

警告:没有跨平台方法。我不确定这些方法还会存在多久。当我第一次开始我的 todo.html 项目时,有一个名为 twFile 的 jQuery 插件,它允许使用四种不同的方法(ActiveX、Mozilla XUL、Java applet、Java Live Connect)跨浏览器加载/保存本地文件。除了 ActiveX,出于安全考虑,浏览器都不允许使用所有这些方法。

【讨论】:

    【解决方案9】:

    如果您对在默认浏览器范围之外运行的代码感到满意,并且对仅支持 Windows 的情况感到满意,那么 HTA 可以轻松满足无提示的静默保存要求。

    下面的代码没有使用很多 HTA 特定的功能,但它仍然使用微软特定的东西,比如ActiveXObject("Scripting.FileSystemObject")

    <html>
    
    <head>
      <title>Simple Notepad</title>
      <meta http-equiv="X-UA-Compatible" content="IE=9">
      <script>
        document.addEventListener('keydown', function (event) {
          if (event.ctrlKey) {
            if (event.key == 's') {
              var FSo = new ActiveXObject("Scripting.FileSystemObject");
              //see https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/opentextfile-method
              var thisFile = FSo.OpenTextFile(window.location.pathname, 2, true, -1);
              thisFile.Write(document.getElementsByTagName("html")[0].outerHTML);
              thisFile.Close();
              // Comment out the below alert to get truly silent saving.
              alert('Saved Successfully');
              if (event.preventDefault) event.preventDefault();
              return false;
            }
          }
        }, false);
      </script>
    
    </head>
    
    <body contentEditable="true">
      <h1>Press <kbd>CTRL + S</kbd> To Save</h1>
    </body>
    
    </html>
    

    这也不是一个非常丰富的编辑体验,但我认为可以通过更多按钮或键盘快捷键来解决。喜欢CTRL + B 以鼓励选定的文本。它目前还没有任何安全检查,但将事件处理程序绑定到beforeunload 应该可以防止因意外关闭程序而导致的任何数据丢失。

    HTA 也有其他缺点。它们不支持 ES6(尽管可以选择转译)。

    虽然它有点过时,但如果你不尝试使用现代网络功能,我想你会同意它非常实用和实用。

    编辑 我忘了提,但 HTA 必须使用 .hta 文件扩展名保存,mshta.exe 才能注册为其文件类型处理程序。这是必需的,以便您可以在 Windows 资源管理器中双击它以轻松打开它。

    另见

    Introduction to HTML Applications on MSDN

    HTML Applications reference on MSDN

    【讨论】:

    • 澄清一下,这不会像浏览器选项卡那样在沙盒/受限环境中运行,因此应该不会提示它尝试使用ActiveXObjectsFileSystemObject 等。
    • HTA 无法通过 MS Edge 呈现。它们使用 MSHTML 渲染引擎(与 IE 相同),因此仅限于 IE 11 的功能。然而,上面的代码将在 IE 5 文档模式下运行,因为它缺少任何 DOCTYPEX-UA-Compatible宣言。 Add appropriate declarations to run in IE 9, 10, or 11 mode.
    • 但问题是寻找一种从网页的沙盒环境访问本地文件系统的方法。 HTA 不会这样做。这只是编写本地应用程序的另一种方法,它恰好使用 HTML/CSS 作为它的 GUI。
    • 是的。对不起。我的错。 HTA 不能使用 MSEdge(即使有注册表黑客)。我现在已经编辑过了。出于某种原因,我认为 mshta.exe 允许选择您要使用的引擎,例如 cscript.exe 可以如何,您可以在命令行中使用 progId,或者修改注册表以修改默认值。
    【解决方案10】:

    我认为在这种情况下澄清服务器和客户端之间的区别很重要。

    客户端/服务器是一种程序关系,其中一个程序(客户端)向另一个程序(服务器)请求服务或资源。

    来源:http://searchnetworking.techtarget.com/definition/client-server

    我不确定您会发现太多高级应用程序没有至少一种服务器/客户端关系。要求在没有任何服务器的情况下实现这一点有点误导,因为任何时候您的程序与另一个程序对话时,它都是客户端/服务器关系,请求者是客户端和来自服务器的响应。即使您在本地工作也是如此。当你想做浏览器范围之外的事情时,你需要一个服务器中的钩子。

    现在,这并不意味着没有服务器端特定语言就无法实现这一目标。例如,this solution 使用 NodeJS 作为服务器。 WinJS 有 WinJS.xhr,它使用XmlHttpRequest 向服务器提供数据。

    AJAX 寻求提供相同类型的功能。这里的重点是,无论你有一个程序还是预先构建了某种钩子,当你发出“save file”之类的命令并且文件实际被保存时,都有一个程序在解析它的另一方,无论是服务器端语言还是其他语言,这意味着如果没有服务器来接收请求,您不可能拥有类似的功能。

    【讨论】:

      【解决方案11】:

      只需使用https://github.com/firebase/firepadSee it in action
      这不需要您的计算机上的服务器,它会伸出手并远程保存数据。

      【讨论】:

        【解决方案12】:

        使用 jsPDF -> https://github.com/MrRio/jsPDF

        <div id="content">
             <h3>Hello, this is a H3 tag</h3>
            <p>a pararaph</p>
        </div>
        <div id="editor"></div>
        <button id="cmd">generate PDF</button>
        

        Javascript

          var doc = new jsPDF();
          var specialElementHandlers = {
              '#editor': function (element, renderer) {
                  return true;
              }
          };
        
          $('#cmd').click(function () {
              doc.fromHTML($('#content').html(), 15, 15, {
                  'width': 170,
                      'elementHandlers': specialElementHandlers
              });
              doc.save('sample-file.pdf');
          });
        

        【讨论】:

          【解决方案13】:

          这是为那些想知道如何使用localStorage 的人提供的示例。

          <div id="divInput" contenteditable="true" style="height:200px;border: 2px solid blue">
            Content editable - edit me and save!
          </div>
          <button onclick="onSave()">Save</button>
          <button onclick="onLoad()">Load</button>
          <script>
            config = {
              localStorageItemName: "demo",
              datetimeFormat: {
                year: 'numeric',
                month: '2-digit',
                day: '2-digit',
                hour: '2-digit',
                hour12: false,
                minute: '2-digit',
                second: '2-digit'
              }
            }
          
            function Now() {
              return new Date().toLocaleString("zh-TW", config.datetimeFormat)
            }
          
            const errMap = {
              IsEmptyError: new Error('is empty'),
              LengthError: new Error('length = 0')
            }
          
            /**
             * @param {string} itemName
             * @return {Object}
             * */
            function getLocalStorageItem(itemName) {
              const dbDataString = localStorage.getItem(itemName)
              if (dbDataString === null) {
                throw errMap.IsEmptyError
              }
              const db = JSON.parse(dbDataString)
              if (Object.keys(db).length === 0) {
                throw errMap.LengthError
              }
              return db
            }
          
            function onSave() {
              const inputValue = document.querySelector(`#divInput`).textContent
              try {
                const db = getLocalStorageItem(config.localStorageItemName)
                db.msg = inputValue
                db.lastModTime = Now()
                localStorage.setItem(config.localStorageItemName, JSON.stringify(db))
                console.log("save OK!")
              } catch (err) {
                switch (err) {
                  case errMap.IsEmptyError:
                    console.info("new localStorageItemName")
                    localStorage.setItem(config.localStorageItemName,
                      JSON.stringify({msg: inputValue, createTime: Now()})
                    )
                    break
                  /*
                  case ...
                    break
                  */
                  default:
                    console.error(err.message)
                }
              }
            }
          
            function onLoad(e) {
              try {
                const db = getLocalStorageItem(config.localStorageItemName)
                console.log("load")
                document.querySelector(`#divInput`).textContent = db.msg
              } catch (err) {
                return
              }
            }
            (()=>{
              window.onload = () => (
                onLoad()
              )
            })()
          </script>

          它是用纯 javascript 编写的,没有依赖关系。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2018-07-24
            • 1970-01-01
            • 2017-09-08
            • 1970-01-01
            • 1970-01-01
            • 2018-10-02
            • 2016-04-04
            相关资源
            最近更新 更多