【揭秘】localStorage - 网页的贴心小秘书

183 阅读6分钟

引言

想象一下,当你再次访问一个购物网站时,它竟然还记得你上次浏览的商品!这背后其实藏着一位默默工作的“贴心小秘书”——localStorage。今天,咱们就来揭开它的神秘面纱,并且通过一个更加复杂的例子来了解它是如何工作的。在这个信息爆炸的时代,能有这么一位记忆力超群的小秘书帮我们记住网页的状态,简直不要太幸福!


这次我们要实现的页面如下:

屏幕截图 2024-12-16 230706.png


HTML5 特性介绍

首先,我们先来看看在这个HTML文件中用到了哪些HTML5特性:

  • 文档类型声明 <!DOCTYPE html>:这是告诉浏览器,“嘿,我是HTML5家族的一员哦!”别小看这一行代码,它就像是给浏览器发了一张名片,确保大家都能愉快地交流。
  • 视口设置 <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">:这个标签不仅确保页面在各种设备上都能有良好的视觉效果,还特别指定了用户不能缩放页面,以提供更一致的用户体验。就像给移动设备写的一封信,说:“请把我显示得好看一点,别让我变形了。”
  • 表单增强功能:例如placeholder(输入框里的提示文字)和required(规定必须填写),这些都让用户的体验更加友好,就像有个人在旁边轻声提醒:“这里要填哦”。这样的设计不仅提高了用户交互的效率,还增加了网页的人情味。

代码解析

接下来,让我们用中文口语化地解析这段代码的工作原理以及如何使用localStorage保存数据。以下是完整的HTML和JavaScript代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">
  <title>LocalStorage</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    html {
      box-sizing: border-box;
      background: url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat;
      background-size: cover;
      min-height: 100vh;
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
      font-family: Futura,"Trebuchet MS",Arial,sans-serif;
    }

    *, *:before, *:after {
      box-sizing: inherit; 
    }

    svg {
      fill:white;
      background: rgba(0,0,0,0.1);
      padding: 20px;
      border-radius: 50%;
      width: 200px;
      margin-bottom: 50px;
    }

    .wrapper {
      padding: 20px;
      max-width: 350px;
      background: rgba(255,255,255,0.95);
      box-shadow: 0 0 0 10px rgba(0,0,0,0.1);
    }

    h2 {
      text-align: center;
      margin: 0;
      font-weight: 200;
    }

    .plates {
      margin: 0;
      padding: 0;
      text-align: left;
      list-style: none;
    }

    .plates li {
      border-bottom: 1px solid rgba(0,0,0,0.2);
      padding: 10px 0;
      font-weight: 100;
      display: flex;
    }

    .plates label {
      flex: 1;
      cursor: pointer;
    }

    .plates input {
      display: none;
    }

    .plates input + label:before {
      content: '⬜';
      margin-right: 10px;
    }

    .plates input:checked + label:before {
      content: '🌮';
    }

    .add-items {
      margin-top: 20px;
    }

    .add-items input {
      padding: 10px;
      outline: 0;
      border: 1px solid rgba(0,0,0,0.1);
    }
  </style>
</head>
<body>
<div class="wrapper">
  <h2>LOCAL TAPAS</h2>
  <p></p>
  <ul class="plates">
    <li>Loading Taps...</li>
  </ul>
  <form class="add-items">
    <input type="text" name="item" placeholder="Item Name" required>
    <input type="submit" value="+ Add Item">
  </form>
  <script>
    const addItems = document.querySelector('.add-items'); // 获取表单元素
    const itemsList = document.querySelector('.plates'); // 获取项目列表容器
    let items = JSON.parse(localStorage.getItem("items")) || [];  // 从localStorage读取数据,默认为空数组

    function addItem(event) {
      event.preventDefault(); // 阻止表单默认提交行为
      const text = (this.querySelector('[name=item]')).value.trim(); // 获取并修剪用户输入文本
      if (!text) return; // 如果输入为空,直接返回不做处理

      // 创建新项对象
      const item = {
        text,
        done: false
      };

      items.push(item); // 将新项添加到数组
      populateList(items, itemsList); // 更新UI
      localStorage.setItem('items', JSON.stringify(items)); // 更新localStorage
      this.reset(); // 重置表单
    }

    function toggleDone(event) {
      const index = event.target.getAttribute('data-index');
      if (index !== null) {
        items[index].done = !items[index].done;
        localStorage.setItem('items', JSON.stringify(items));
        populateList(items, itemsList);
      }
    }

    function removeItem(event) {
      const index = event.target.closest('li').getAttribute('data-index');
      if (index !== null) {
        items.splice(index, 1);
        localStorage.setItem('items', JSON.stringify(items));
        populateList(items, itemsList);
      }
    }

    function populateList(plates, platesList) {
      platesList.innerHTML = plates.map((plate, i) => {
        return `
          <li data-index="${i}">
            <input
              type="checkbox" 
              data-index="${i}" 
              id="item${i}"
              ${plate.done ? 'checked' : ''}
              onchange="toggleDone(event)">
            <label for="item${i}">${plate.text}</label>
            <button onclick="removeItem(event)">Remove</button>
          </li>
        `;
      }).join('');
    }

    addItems.addEventListener('submit', addItem);
    window.onload = () => {
      populateList(items, itemsList); // 页面加载时渲染已有数据
    };
  </script>
</div>
</body>
</html>

解释

在这段代码里,我们创建了一个更复杂但同样直观的待办事项列表应用,用户可以像点菜一样添加新的小吃到列表中,并标记它们是否已经吃过了或者直接移除不想再吃的。所有的操作都会实时同步到localStorage,所以就算刷新页面,之前的选择也不会消失。

  • DOMContentLoaded事件:当整个HTML文档加载完成后触发,确保脚本在所有元素都准备好了之后才开始干活。这就像是等所有人都到齐了,才正式开饭,避免了有人还没到位就开始行动的尴尬。

  • loadItems函数:这个函数就像个勤快的小工,每次页面一加载就会跑去localStorage里找有没有存过的东西,然后把它们展示出来。它负责的是确保你上次做的选择不会被遗忘,下次打开页面还能看到。

  • populateList函数:根据传入的数据生成对应的HTML结构,简单来说就是把数据变成你能看到的项目列表。它就像是厨房里的厨师,把食材变成了美味的菜肴,让你能够享受一顿丰盛的晚餐。

  • addItem函数:这里有个聪明的做法,它阻止了表单默认提交的行为,收集用户输入的新项,把它加到本地存储里,然后再更新界面,最后还帮用户清空了输入框,真贴心啊。这种做法不仅提高了效率,还让用户感觉特别好,好像有一个私人助手在帮你打理一切。

  • toggleDone函数:用来改变某个小吃的状态(比如我已经吃过啦)。每次做完这些动作,它还会记得更新一下localStorage,好让下次打开页面时还能找到这些记录。这就好比是整理书架,把读过的书归类,保持书架的整洁有序。

  • removeItem函数:干脆把这个小吃从列表里踢出去。每当点击删除按钮时,它会查找对应的项目并将其从列表中移除,同时更新localStorage中的记录,确保数据的一致性。

深入探讨

当然,localStorage不仅仅是用来保存一些简单的待办事项列表。实际上,它可以用于保存用户偏好、游戏进度、甚至临时文件等。但需要注意的是,localStorage并不是无限大的,每个域名下的存储空间大约为5MB左右。因此,在使用时应该合理规划,不要滥用。另外,由于localStorage中的数据是以字符串形式存储的,所以在存储复杂对象或数组时,需要使用JSON.stringify进行序列化,取出时再用JSON.parse反序列化。这有点像是把东西装进盒子里,再从盒子里拿出来,只不过这里的盒子是JSON格式的。

结语

通过这篇文章,希望你对localStorage有了更深入的理解,并且学会了如何利用它为自己的项目增添持久化存储的功能。记住,localStorage虽然是个好帮手,但也要注意别把敏感信息交给它保管哦。毕竟,谁都不希望自己的秘密被随便翻阅吧?

现在,轮到你动手试试看啦!如果你有任何问题或者想要分享你的创作,请随时留言评论。祝编程愉快,玩得开心!希望每一位读者都能成为自己网页的小秘书,打造独一无二的用户体验。

20200229174423_bzukt.jpg