【JavaScript】買い出しメモアプリ

localStorageを使用した、簡易メモアプリです。
個人的にスーパーでの食品買い出し用に使いたくて作成。
自分が欲しい機能のみなので、順序入れ替えや個数入力欄などはありません。

![ss_.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618708/b871fd20-1a85-f159-bf32-36dcea4ffe3f.jpeg)

各項目左のチェックボックスは単にテキスト背景をグレー表示させるだけのもので特別な機能はありませんが、私は買い物中の購入済みチェック用に使っています。

----

#### [動作デモ](https://akebi.jp/temp/memo.html) ####

```html:index.html
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta name='viewport' content='initial-scale=1'>
<meta http-equiv='Cache-Control' content='no-store'>
<meta charset='utf-8'>
<title>メモ</title>
<style>
#list {
  margin: 20px 0;
}
button {
  font-size: 18px;
}
[type=text] {
  font-size: 20px;
  margin: 8px;
  width: 220px;
}
[type=checkbox]:checked + input {
  background-color: #ccc;
}
</style>
</head>
<body>
<button id='allDel'>全削除</button>
<div id='list'></div>
<div><input type='text' id='addText'><button id='addBtn'>登録</button></div>
<template><div><input type='checkbox'><input type='text'><button>削除</button></div></template>
<script>
'use strict';

const list = document.getElementById('list');
const addText = document.getElementById('addText');
const template = document.querySelector('template').innerHTML;
const storageKey = 'memo';

const refreshList = () => {
  list.textContent = '';
  (JSON.parse(localStorage.getItem(storageKey)) ?? []).forEach(e => {
    if (e.text === '') return;
    list.insertAdjacentHTML('beforeend', template);
    const parent = list.querySelector('div:last-of-type');
    parent.querySelector('[type=text]').value = e.text;
    parent.querySelector('[type=checkbox]').checked = e.check;
  });
};

document.addEventListener('DOMContentLoaded', refreshList);

const addList = () => {
  if (addText.value.trim() === '') return;
  list.insertAdjacentHTML('beforeend', template);
  list.querySelector('div:last-of-type [type=text]').value = addText.value.trim();
  addText.value = '';
};

document.getElementById('addBtn').addEventListener('click', () => save());
addText.addEventListener('keydown', ev => ev.key === 'Enter' && save());

list.addEventListener('click', ev => {
  const target = ev.target;
  const parent = target.closest('div');
  if (target.tagName === 'BUTTON' && confirm(`${parent.querySelector('[type=text]').value}を削除しますか`)) {
    parent.remove();
    saveStorage();
  }
});

list.addEventListener('keydown', ev => {
  const target = ev.target;
  const parent = target.closest('div');
  if (target.tagName === 'INPUT' && ev.key === 'Enter' && target.value.trim() === '') {
    parent.remove();
    saveStorage();
  }
});

document.getElementById('allDel').addEventListener('click', () => {
  if (confirm('全てのリストを削除しますか')) {
    list.textContent = '';
    saveStorage();
  }
});

const saveStorage = () => {
  const listItems = [...list.querySelectorAll('div')];
  if (!listItems.length) {
    localStorage.removeItem(storageKey);
    return;
  }
  localStorage.setItem(storageKey,
    JSON.stringify(listItems.map(e => ({
      text: e.querySelector('[type=text]').value.trim(),
      check: e.querySelector('[type=checkbox]').checked,
    })))
  );
};

const save = () => {
  addList();
  saveStorage();
};

addEventListener('beforeunload', save);
addEventListener('pagehide', save);
addEventListener('storage', refreshList);
</script>
</body>
</html>
```

----