作者:©Iaine 万一
简介:30 Day ChallengeWes Bos 设计的一个 30 天原生js编程挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。

本项目为第15天的“JS window属性: localStorage ”项目。Have fun with the website! ♪(^∇^*)

网页效果: https://janice143.github.io/localStorage/

项目描述

本项目是一个可添加项目的点菜清单,刷新网页时,菜单信息不会清空。实现该功能的主要技术是JavaScript Window 对象的localStorage属性。

项目重点

  • localStorage

    • localStorage.setItem
    • localStorage.getItem
  • JS取消默认行为

  • reset() 方法

    • 把表单中的元素重置为默认值
  • JSON 的方法

    • JSON.stringify
    • JSON.parse

项目过程

HTML部分

  • 网页logo

    • <svg>标签
  • 菜品清单内容

    • 标题<h2>

    • 菜单项目<ul>

    • 添加菜品表单<form>

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      <div class="wrapper">
              <h2>LOCAL TAPAS</h2>
              <p></p>
              <ul class="plates">
                <li>Loading Tapas...</li>
              </ul>
              <form class="add-items">
                <input type="text" name="item" placeholder="Item Name" required>
                <input type="submit" value="+ Add Item">
              </form>
          </div>

CSS部分

  • 菜品项目添加后默认复选框⬜️没有checked

    1
    2
    3
    4
    .plates input + label:before {
    content: "⬜️";
    margin-right: 10px;
    }
  • 菜品项目checked后方框变成其他图标

    1
    2
    3
    .plates input:checked + label:before {
    content: "🌮";
    }

JS部分

  • form表单中若有type 属性是 “submit”的元素,则具有submit 事件

    1
    2
    3
    4
    function addItem(){
    console.log('hello')
    }
    addItems.addEventListener('submit', addItem);
  • 当点击form中的提交按钮时,会提交表单并且刷新页面(可在控制台中看出闪现hello),这种默认行为可以通过 e.preventDefault来阻止

    1
    2
    3
    function addItem(e){
    e.preventDefault();
    }
  • 下面开始正式编写addItem函数,用来获取form中添加的元素,然后放到items变量中存储起来

    • this.querySelector('[name=item]')选择type为text元素的值(输入框输入的内容)
    • 构造一个对象 item 来存储这个信息
    • 把item push到提前创建的items(所有菜单)中
    1
    2
    3
    4
    5
    6
    7
    const text = (this.querySelector('[name=item]')).value;
    // 构造一个对象 item 来存储这个信息
    item = {
    text, // ES6中对 text: text, 的简写
    done:false // 标记有没有checked
    }
    items.push(item);
    • 执行populateList(items, itemsList)函数,把新添加的菜品显示到页面中
    • 更新localStorage中的items数据
    • 重置输入框的值
    1
    2
    3
    populateList(items, itemsList);
    localStorage.setItem('items', JSON.stringify(items));
    this.reset();
  • 编写populateList函数,实现将items中的信息挂载到DOM树上

  • <input>标签实现的复选框

    • data-index属性标记菜品序号
  • <label>标签记录菜单的文字

1
2
3
4
5
6
7
8
9
10
function populateList(plates = [], platesList) {
platesList.innerHTML = plates.map((plate, i) => {
return `
<li>
<input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''} />
<label for="item${i}">${plate.text}</label>
</li>
`;
}).join('');
}
  • 程序写到这里基本完成,但是仔细观察会发现,如果checked每个菜品,刷新页面后,这个状态会被刷新(不被保留),这是因为我们并没有更新items中done的值

  • 所以还需编写toggleDone函数,通过菜品click事件触发

    • e.target.dataset.index可以获取利用data-index属性标记菜品序号
    • !items[index].done否操作
    • 更新localStorage和HTML页面
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function toggleDone(e) { 
    if (!e.target.matches('input')) return; // skip this unless it's an input
    // console.log(e.target)
    const el = e.target;
    const index = el.dataset.index;
    items[index].done = !items[index].done;
    localStorage.setItem('items', JSON.stringify(items));
    populateList(items, itemsList);
    }
    itemsList.addEventListener('click', toggleDone);

项目补充

HTML <input> 标签的 required 属性

required 属性规定必需在提交之前填写输入字段

JS-preventDefault() 取消默认行为

语法:event.preventDefault()

该方法将通知 Web 浏览器不要执行与事件关联的默认动作(如果存在这样的动作)。

常用情景:

  • 如果 type 属性是 “submit”,在事件传播的任意阶段可以调用任意的事件句柄,通过调用该方法,可以阻止提交表单。
  • a 标签点击时,会跳转url,采用如下方式,可防止链接打开 URL:

常用情景的知识点来源于博客

JSON.parseJSON.stringify

JSON对象在所有现代浏览器中都适用,他有两个非常有用的方法是parse()和stringify().

  • JSON.parse() 把一个JSON字符串转变成JS对象
1
2
3
4
let userStr = '{"name":"Sammy","email":"[email protected]","plan":"Pro"}';
let userObj = JSON.parse(userStr);
console.log(userObj);
// {name: 'Sammy', email: '[email protected]', plan: 'Pro'}
  • JSON.parse() 第二个参数可以是一个自定义函数,具有返回值
1
2
3
4
5
6
7
8
9
let userStr = '{"name":"Sammy","email":"[email protected]","plan":"Pro"}';
let userObj = JSON.parse(userStr, (key, value) => {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value;
});
console.log(userObj);
// {name: 'SAMMY', email: '[email protected]', plan: 'PRO'}
  • JSON.stringify() 把一个JS对象转变成JSON字符串
1
2
3
4
5
6
7
8
let userObj = {
name: "Sammy",
email: "[email protected]",
plan: "Pro"
};
let userStr = JSON.stringify(userObj);
console.log(userStr);
// {"name":"Sammy","email":"[email protected]","plan":"Pro"}
  • JSON.stringify()可以有两个额外参数

    • 一个replacer参数(是一个自定义函数,函数名为replacer)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let userObj = {
    name: "Sammy",
    email: "[email protected]",
    plan: "Pro"
    };
    function replacer(key, value) {
    console.log(typeof value);
    if (key === 'email') {
    return undefined;
    }
    return value;
    }
    let userStrReplacer = JSON.stringify(userObj, replacer);
    console.log(userStrReplacer);
    // {"name":"Sammy","plan":"Pro"}
    • 一个是space参数(是 String 或者 Number 值),用来控制间距
      • 如果是Number,缩进为空格数(1-10)
      • 如果是String,缩进为该字符串
    1
    2
    3
    4
    5
    6
    JSON.stringify({ uno: 1, dos: 2 }, null, '\t');
    // returns the string:
    // '{
    // "uno": 1,
    // "dos": 2
    // }'

map()和forEach()的区别和理解

两个方法都可以实现元素遍历,但是map方法可以用返回值,而forEach方法没有返回值

参考博客

  1. JS-preventDefault() 取消默认行为
  2. How To Use JSON.parse() and JSON.stringify()
  3. Example of using JSON.stringify() with localStorage
  4. map()和forEach()的区别和理解

JS30的第15个项目圆满完成啦,感谢阅读,有问题联系我的邮箱1803105538@qq.com.