JavaScript-30-Day-15
Tags: javascript, github
Published: 2021-06-15 18:26:54

## <center>Local Storage and Event Delegation </center>

## [demo](https://cenacrharsh.github.io/JS-30-DAY-15/)

![ss](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ohyafwii9n1zf1emtex6.png)

On day-15 we will learn how to make our state persistent by using local storage and learn how to use event delegation.

We have a simple list by default where we can add items and also they also have checkboxes which we can check, but the data and state of checked/unchecked boxes will stay as long as we don't refresh the page, after which every thing will be reset, and we'll work to make the this change persistent.

This is the default HTML we have:

```html
<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>
```

We'll start by selecting the unordered list and the form element.

```javascript
const addItems = document.querySelector(".add-items");
      const itemsList = document.querySelector(".plates");
      const items =[];
```

We'll add a `submit` eventlistener to form element

```javascript
addItems.addEventListener("submit", addItem);
```

**Why?** `submit`, this is because one can submit by using the keyboard as well hence to cover all our bases we use a `submit` event listener.

Now inside the `addItem` function:

```javascript
function addItem(e) {
        e.preventDefault();
        const text = this.querySelector("[name=item]").value;
        const item = {
          text,
          done: false,
        };
        items.push(item);
        populateList(items, itemsList);
        localStorage.setItem("items", JSON.stringify(items));
        this.reset();
      }
```

First of all we use `.preventDefault()` method as by default the form is going to reload the page as soon as data is entered (generally to send data to server) and to prevent page from reloading we use the `preventDefault()` method.

Now we need to take the input user gives inside the box and put it into an object using `const text = this.querySelector("[name=item]").value`. The `querySelector` gives us the input element which has a property called `.value` which gives the text user has typed inside the input field.

You might be thinking why this and not document for query selector. The `this` here contains the form and we are searching for the one with attribute `name=item`. This technique is helpful when we are working with multiple forms.

After we put the text into an object using 
```javascript
const item = {
          text,
          done: false,
        };
```

The `done` is for whether the item is checked and we will make it true when we click the checkbox.

Now we will push the object into `items` array using `items.push(item)`

Now we call `populateList(items, itemsList)` where we recreate the list every time user gives an input. The work of `populateList` function is to basically generate the HTML for the page.

`this.reset()` clears the input field after adding it to our list.

Here is the populateList() function:

```javascript
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("");
      }
```

Here we basically get the text and create a list item according to it and pass it to the list we already have.

> The `plates = []` prevents the code from breaking if we forget to supply the arguments.

> The `id="item${i}" ` and `for="item${i}"` is same and that is how we link the label and input, when you click the label the input checks itself.

Now after all this we can add whatever we type to our list but it is not peristent and so we use local storage.

**Local Storage** is simply a key-value store and we can only use strings to store data in local storage

> If you try to put something other than a string into Local Storage, it will convert it and store it as string.

So we use `JSON.stringify` to convert our objects/arrays into JSON string, and the method to set data in the storage is `setItem`.
```javascript
localStorage.setItem("items", JSON.stringify(items));
```

So we'll modify the `const items =[]` started with to
`const items = JSON.parse(localStorage.getItem("items")) || []`

Here `getItem` is used to take data our of local storage.

Now on page load it tries to load data from local storage and if it fails then fallback value is `[]` there.

Now we will work to make the toggling persistent, so that if we check an item it remains checked after refresh, but the problem is we can't listen for click events on list items because if we add new list items listeners won't work on them because input was created after we listened for them and hence they don't have eventListeners attached to them.

The whole idea of **Event Delegation** is rather than listening for click/change on checkbox directly , what we do is we look for something which is already going to be on the page at the time of listening.

Looking at our html the <ul> or unordered list with class=plates does exist on the page.

Hence we will listen for a click on the plates and then we wil find out **Did they actually mean to click on one of the input inside of it?**

If we click different items the target will be different and so we use `e.target` and if it is not `input`.

Now we will go to items array and find out what is the status of each items whether checked/uncheced and accordingly toggle it's value and set it into local storage.

Here is the function: 

```javascript
itemsList.addEventListener("click", toggleDone);

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);
      } 
```

Here we use the `data-index` attribute we gave each element so that we know the position of each element inside our array and we use that index to manipulate the elements.

### So in short everytime we make a change, we mirror that change to local storage and then we rerender the entire list.

Here is the complete javascript:

```javascript
const addItems = document.querySelector(".add-items");
      const itemsList = document.querySelector(".plates");
      const items = JSON.parse(localStorage.getItem("items")) || [];

      function addItem(e) {
        e.preventDefault();
        const text = this.querySelector("[name=item]").value;
        const item = {
          text,
          done: false,
        };
        console.log(item);
        items.push(item);
        populateList(items, itemsList);
        localStorage.setItem("items", JSON.stringify(items));
        this.reset();
      }

      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("");
      }

      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);
      }

      addItems.addEventListener("submit", addItem);
      itemsList.addEventListener("click", toggleDone);
      populateList(items, itemsList);
```

and with this our project for the day was completed.

GitHub repo:
{% github cenacrharsh/JS-30-DAY-15 no-readme %}

###Blog on Day-14 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-14-3p0i %}

###Blog on Day-13 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-13-337i %}

###Blog on Day-12 of javascript30
{% link https://dev.to/cenacr007_harsh/javascript-30-day-12-2k45 %}

[Follow me on Twitter](https://twitter.com/cenacr007_harsh)
[Follow me on Linkedin](https://www.linkedin.com/in/kumarharsh22/)

**DEV Profile**

{% user cenacr007_harsh %}

You can also do the challenge at [javascript30](https://javascript30.com/)

Thanks @wesbos  , [WesBos](https://github.com/wesbos) to share this with us! 😊💖

**Please comment and let me know your views**
##Thank You!