{"id":9052,"date":"2022-10-08T18:02:00","date_gmt":"2022-10-08T17:02:00","guid":{"rendered":"https:\/\/stevepedwards.today\/DebianAdmin\/?p=9052"},"modified":"2023-10-28T23:48:54","modified_gmt":"2023-10-28T22:48:54","slug":"js-html-and-css-todo-list-saves-list-to-json-file","status":"publish","type":"post","link":"https:\/\/stevepedwards.today\/DebianAdmin\/js-html-and-css-todo-list-saves-list-to-json-file\/","title":{"rendered":"JS, HTML and CSS Todo List &#8211; Saves List to JSON File"},"content":{"rendered":"<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_9052\" class=\"pvc_stats all  \" data-element-id=\"9052\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/stevepedwards.today\/DebianAdmin\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-9053\" src=\"https:\/\/stevepedwards.today\/DebianAdmin\/wp-content\/uploads\/2022\/10\/TodoPage-1024x551.png\" alt=\"\" width=\"840\" height=\"452\" \/><\/p>\n<p>This Portfolio Project was built on exercises in Kyle Cook's WEBDev JS and CSS courses:<\/p>\n<p><a href=\"https:\/\/www.youtube.com\/c\/WebDevSimplified\">https:\/\/www.youtube.com\/c\/WebDevSimplified<\/a><\/p>\n<p>He also has a Calculator and Clock project, with code, that was used and modified a little to fit inside the main page using Iframes.<\/p>\n<p><a href=\"https:\/\/youtu.be\/Ki0XXrlKlHY\">Clock<\/a><\/p>\n<p><a href=\"https:\/\/youtu.be\/j59qQ7YWLxw\">Calc<\/a><\/p>\n<p>The basic Todo List HTML and CSS from Kyle's courses - (without the complete JS for saving the list permanently to a JSON array file of K:V pairs, that was written for me (as I've just started learning JS, HTML and CSS) by Joe Moore of <a href=\"https:\/\/www.databasejoe.com\">www.databasejoe.com<\/a> ) - is:<\/p>\n<pre class=\"lang:default decode:true\">&lt;!DOCTYPE html&gt;\r\n&lt;html lang=\"en\"&gt;\r\n&lt;head&gt;\r\n&lt;title&gt;Simple List&lt;\/title&gt;\r\n&lt;link rel=\"stylesheet\" href=\"styles.css\"&gt;\r\n&lt;script src=\"script.js\" defer&gt;&lt;\/script&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n&lt;div id=\"list\"&gt;\r\n\r\n&lt;\/div&gt;\r\n\r\n&lt;form id=\"new-item-form\"&gt;\r\n&lt;label for=\"item-input\"&gt;New Item&lt;\/label&gt;\r\n&lt;input type=\"text\" id=\"item-input\"&gt;\r\n&lt;button type=\"submit\"&gt;Add Item&lt;\/button&gt;\r\n&lt;\/form&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<pre class=\"lang:default decode:true \">\/\/ 1. Select all elements\r\nconst form = document.querySelector(\"#new-item-form\")\r\nconst list = document.querySelector(\"#list\")\r\nconst input = document.querySelector(\"#item-input\")\r\n\r\n\/\/ 2. When I submit the form add a new element\r\nform.addEventListener(\"submit\", e =&gt; {\r\n  e.preventDefault()\r\n\r\n  \/\/ 1. Create a new item\r\n  const item = document.createElement(\"div\")\r\n  item.innerText = input.value\r\n  item.classList.add(\"list-item\")\r\n\r\n  \/\/ 2. Add that item to the list\r\n  list.appendChild(item)\r\n\r\n  \/\/ 3. Clear input\r\n  input.value = \"\"\r\n\r\n  \/\/ 4. Setup event listener to delete item when clicked\r\n  item.addEventListener(\"click\", () =&gt; {\r\n    item.remove()\r\n  })\r\n})<\/pre>\n<pre class=\"lang:default decode:true \">.list-item {\r\n  cursor: pointer;\r\n  width: min-content;\r\n}\r\n\r\n.list-item:hover {\r\n  color: red;\r\n  text-decoration: line-through;\r\n}<\/pre>\n<p>My additions for saving the List and colorising the page, with the Iframes :<\/p>\n<p>index.html:<\/p>\n<pre class=\"lang:default decode:true \">&lt;!DOCTYPE html&gt;\r\n&lt;html lang=\"en\"&gt;\r\n&lt;head&gt;\r\n  &lt;meta charset=\"utf-8\"&gt;\r\n  &lt;title&gt;Todo List&lt;\/title&gt;\r\n  &lt;script type=\"text\/javascript\"&gt;&lt;\/script&gt;\r\n  &lt;link rel=\"stylesheet\" href=\"todo.css\"&gt;\r\n&lt;script defer=\"\" src=\"todo.js\"&gt;&lt;\/script&gt; \r\n&lt;\/head&gt;\r\n  &lt;body&gt;\r\n    \r\n    &lt;section class=\"section-one\"&gt;\r\n      &lt;div id=\"frame\"&gt;&lt;iframe id=\"scaled-frame\" src=\"Calc\/calc.html\"&gt;&lt;\/iframe&gt;&lt;\/div&gt;\r\n      &lt;h1 class=\"h1\"&gt;To Do List&lt;\/h1&gt;\r\n      &lt;div&gt;&lt;iframe id=\"scaled-frame\" src=\"Clock\/clock.html\"&gt;&lt;\/iframe&gt;&lt;\/div&gt;\r\n    &lt;\/section&gt;\r\n\r\n    &lt;section class=\"section-two\"&gt;\r\n      &lt;form id=\"new-item-form\"&gt;\r\n        &lt;div id=\"list\"&gt;&lt;\/div&gt;\r\n        &lt;label class=\"new-item\" for=\"item-input\"&gt;New Item:&lt;\/label&gt;\r\n        &lt;input id=\"item-input\" type=\"text\"&gt;\r\n        &lt;button class=\"btn\" id=\"list-item\" type=\"submit\"&gt;Add Item To List&lt;\/button&gt;\r\n        &lt;button id=\"save\" type=\"button\"&gt;Save List&lt;\/button&gt;\r\n      &lt;\/form&gt;\r\n    &lt;\/section&gt;\r\n\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;<\/pre>\n<p>CSS:<\/p>\n<pre class=\"lang:default decode:true \">* {\r\n  box-sizing: border-box;\r\n}\r\n\r\nbody {\r\n  font-family: cursive;\r\n  color: white;\r\n  background: linear-gradient(to right, rgb(31, 33, 34) , rgb(106, 110, 110));   \r\n}\r\n\r\n.section-one {\r\n    display: flex;\r\n    justify-content: center;\r\n    align-items: center;\r\n}\r\n\r\n\/* iframes for calc and clock*\/\r\n.frame {\r\n  width: 480px;\r\n  height: 500px;\r\n  padding: 0;\r\n  overflow: hidden;\r\n}\r\n\r\n\/* iframes for calc and clock*\/\r\n#scaled-frame {\r\n  width: 330px;\r\n  height: 320px;\r\n  border: 0px;\r\n}\r\n\r\n\/* ToDo text *\/\r\n.h1 {\r\n  font-size: 4rem;\r\n  justify-content: center;\r\n  padding: 100px;\r\n}\r\n\r\n\/* input form and list add\/save buttons *\/\r\n.section-two {\r\n  font-family: 'Courier New';\r\n  display: flex;\r\n  justify-content: center;\r\n  align-items: center;\r\n}\r\n\r\n#item-input {\r\n  background-color: #CCC;\r\n  font-family: 'Courier New';\r\n  padding: 0;  \r\n}\r\n\r\n.new-item {\r\n  font-family: 'Courier New';\r\n  border: 2px solid black;\r\n  margin: 20px;\r\n  padding: 4px;\r\n  font-size: 1.0rem;\r\n  color: white;\r\n  margin: 10px;\r\n  background-color: grey;\r\n  border-radius: 1000px;\r\n}\r\n\r\n.new-item:hover {\r\n  cursor: pointer;\r\n  background-color: #171717;\r\n}\r\n\r\n\r\n\r\n.list-item {\r\n  font-family: cursive;\r\n  margin-bottom: 10px;\r\n  margin-top: 10px;\r\n  margin-left: 105px;\r\n  color: yellow;\r\n  justify-content: center;\r\n  width: max-content;\r\n}\r\n\r\n.list-item:hover {\r\n  color: red;\r\n  text-decoration: line-through;\r\n  cursor: pointer;\r\n}\r\n\r\n.btn {\r\n  font-family: 'Courier New';\r\n  font-size: 1.0rem;\r\n  color: white;\r\n  padding: 4px;\r\n  margin: 10px;\r\n  background-color: grey;\r\n  border-radius: 1000px;\r\n}\r\n\r\n.btn:hover {\r\n  cursor: pointer;\r\n  background-color: #171717;\r\n}\r\n\r\n#save {\r\n  font-family: 'Courier New', Courier, monospace;\r\n  font-size: 1.0rem;\r\n  display: flex;\r\n  margin-top: 20px;\r\n  border-radius: 10000px;\r\n  font-size: 1.0rem;\r\n  color: white;\r\n  margin: 10px;\r\n  background-color: grey;\r\n  border-radius: 1000px;\r\n}\r\n\r\n#save:hover {\r\n  cursor: pointer;\r\n  background-color: #171717;\r\n}\r\n<\/pre>\n<p>original messy JS:<\/p>\n<pre class=\"lang:default decode:true \">\/\/ Program Plan:\r\n\/\/ 1: Select All Elements\r\nconst form = document.querySelector(\"#new-item-form\");\r\nconst input = document.querySelector(\"#item-input\");\r\nconst list = document.querySelector(\"#list\");\r\nconst items = [];\r\nconst saveLink = document.getElementById(\"save\");\r\nsaveLink.addEventListener(\"click\", () =&gt; {\r\n  const jsonObj = { items: items };\r\n  const jsonString = JSON.stringify(jsonObj);\r\n  saveFileAsDialog(jsonString, \"items.json\");\r\n});\r\n\r\n\/\/ 2: When I submit form, add new item to the list\r\nfetchItemsFromArray();\r\nform.addEventListener(\"submit\", (e) =&gt; {\r\n  e.preventDefault();\r\n\r\n  console.log(input.value);\r\n\r\n  \/\/ 1: Create new item\r\n  writeToItemsJsonFile(input.value);\r\n\r\n  \/\/ const item = document.createElement(\"div\");\r\n  \/\/ item.innerText = input.value;\r\n  \/\/ item.classList.add(\"list-item\");\r\n  \/\/ \/\/ console.log(item) Once checked working, remove log.\r\n\r\n  \/\/ \/\/ 2: Add item to list:\r\n  \/\/ \/\/   Check it works and shows in browser:\r\n  \/\/ list.appendChild(item);\r\n\r\n  \/\/ 3: Clear input after item added by setting input to empty string:\r\n  input.value = \"\";\r\n\r\n  \/\/ 4: Setup event listener to delete clicked item:\r\n});\r\n\r\nfunction writeToItemsJsonFile(value) {\r\n  console.log(\"This is writing to file\" + value);\r\n  \/\/ append to items array\r\n  items.push(value);\r\n  const jsonObj = { items: items };\r\n  const jsonString = JSON.stringify(jsonObj);\r\n\r\n  saveFileAsDialog(jsonString, \"items.json\");\r\n\r\n  \/\/write to live document page\r\n  const el = document.createElement(\"div\");\r\n  el.innerText = value;\r\n  el.classList.add(\"list-item\");\r\n  \/\/ console.log(item) Once checked working, remove log.\r\n\r\n  \/\/ 2: Add item to list:\r\n  \/\/   Check it works and shows in browser:\r\n  list.appendChild(el);\r\n}\r\n\r\nfunction saveFileAsDialog(data, filename) {\r\n  \/\/ data is the string type, that contains the contents of the file.\r\n  \/\/ filename is the default file name, some browsers allow the user to change this during the save dialog.\r\n\r\n  \/\/ Note that we use octet\/stream as the mimetype\r\n  \/\/ this is to prevent some browsers from displaying the\r\n  \/\/ contents in another browser tab instead of downloading the file\r\n  var blob = new Blob([data], { type: \"octet\/stream\" });\r\n\r\n  \/\/BLOB = Binary Large Object\r\n  if (window.navigator.msSaveBlob) {\r\n    window.navigator.msSaveBlob(blob, filename);\r\n  } else {\r\n    \/\/Everything else\r\n    var url = window.URL.createObjectURL(blob);\r\n    var a = document.createElement(\"a\");\r\n    document.body.appendChild(a);\r\n    a.href = url;\r\n    a.download = filename;\r\n\r\n    setTimeout(() =&gt; {\r\n      \/\/setTimeout hack is required for older versions of Safari\r\n\r\n      a.click();\r\n\r\n      \/\/Cleanup\r\n      window.URL.revokeObjectURL(url);\r\n      document.body.removeChild(a);\r\n    }, 1);\r\n  }\r\n}\r\n\r\nfunction fetchItemsFromArray() {\r\n  console.log(\"fetch items array\");\r\n  fetch(\"items.json\")\r\n    .then((response) =&gt; response.json())\r\n    .then((json) =&gt; itemsloop(json));\r\n}\r\n\r\nfunction itemsloop(json) {\r\n  json.items.forEach((item) =&gt; htmlStyle(item));\r\n}\r\n\r\nfunction htmlStyle(item) {\r\n  createJsonObject(item);\r\n\r\n  const el = document.createElement(\"div\");\r\n  el.innerText = item;\r\n  el.classList.add(\"list-item\");\r\n  \/\/ console.log(item) Once checked working, remove log.\r\n\r\n  \/\/ 2: Add item to list:\r\n  \/\/   Check it works and shows in browser:\r\n  list.appendChild(el);\r\n\r\n  el.addEventListener(\"click\", (e) =&gt; {\r\n    el.remove(); \/\/ Remove from DOM list\r\n\r\n    \/\/ Remove item from array\r\n    const itemToRemove = e.target.innerHTML;\r\n    items.pop(itemToRemove);\r\n  });\r\n}\r\n\r\nfunction createJsonObject(itemToAdd) {\r\n  items.push(itemToAdd);\r\n}\r\n<\/pre>\n<p>The 70's calculator font is \"calculator.ttf\" from the web.<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_9052\" class=\"pvc_stats all  \" data-element-id=\"9052\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/stevepedwards.today\/DebianAdmin\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p>This Portfolio Project was built on exercises in Kyle Cook's WEBDev JS and CSS courses: https:\/\/www.youtube.com\/c\/WebDevSimplified He also has a Calculator and Clock project, with code, that was used and modified a little to fit inside the main page using Iframes. Clock Calc The basic Todo List HTML and CSS from Kyle's courses - (without <a href=\"https:\/\/stevepedwards.today\/DebianAdmin\/js-html-and-css-todo-list-saves-list-to-json-file\/\" class=\"more-link\">...<span class=\"screen-reader-text\">\u00a0 JS, HTML and CSS Todo List &#8211; Saves List to JSON File<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-9052","post","type-post","status-publish","format-standard","hentry","category-post"],"a3_pvc":{"activated":true,"total_views":3,"today_views":0},"_links":{"self":[{"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/posts\/9052","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/comments?post=9052"}],"version-history":[{"count":12,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/posts\/9052\/revisions"}],"predecessor-version":[{"id":10081,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/posts\/9052\/revisions\/10081"}],"wp:attachment":[{"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/media?parent=9052"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/categories?post=9052"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/stevepedwards.today\/DebianAdmin\/wp-json\/wp\/v2\/tags?post=9052"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}