# I made a bookkeeping book with JS [data can be stored locally] with a detailed explanation of 10000 words

catalogue

1. Judge whether the input content is compliant

3. Modify the history panel style

4. Realize the deletion function

5. Statistical amount

2, Local storage๐ญย

1. Get cache records

2. Add new data to the cache

3. Display history according to local cache

4. Rewrite setMoney method

5. Delete the specified data in the cache

I deployed the bookkeeping project on the code cloud. You can copy the link and open it in the browser to experience: ๐๐๐

http://jie_shao1112.gitee.io/bookkeeping-book

The source address is at the end of the article ๐ฅ

Overall effect:

ย

## 1, Local operation

### 1. Judge whether the input content is compliant

All our operations are carried out around clicking to add a new transaction

Let's first bind the click event to the button of adding a new transaction. Obviously, we need to make a judgment on the input content. We will complete the isAlert function later. The isAlert function returns flash when the content of the input box does not conform to the specification. In this way, a problematic dialog box will pop up to remind the user. Let's make a simple one first, mainly understand the logic first, and then improve it

```var add = document.querySelector('#add');// Get add transaction button
var namein = document.querySelector('#name');// Get name box
var moneyin = document.querySelector('#money');// Get amount box
//1. Determine the content of the input box
}
})```

In the isAlert function, we use a regular expression to determine whether the content entered in the amount box is reasonable. We stipulate that it must start with addition and subtraction, because it is convenient for subsequent operations. Either it is a pure integer. If the decimal point is after the integer and up to two digits

If the input box of transaction name is empty or the content in the amount input box is unreasonable, false will be returned

```function isAlert() {
var reg = /^[\+\-](\d+|\d+\.\d{1,2})\$/;
if(namein.value === "" || reg.test(moneyin.value) === false) {
return false;
}
else{
return true;
}
}```

After entering the name and amount of the transaction, click add new transaction, and the record will be inserted into the history panel. We want to achieve this effect below.

Let's take a look at the html structure of history records. ul is the box of history records. Each record is a li, which is added to ul as a child:

```<ul>
<!-- <li>
<span class="name">investment</span>
<span class="money">-1000</span>
<span class="del">x</span>
</li> -->
</ul>```

Back to the click event of adding a new transaction, we store the obtained data in the form of an object

Because in the history record, the border on the right side of the expenditure is orange and the income is light blue, we need to judge whether it is expenditure or income through the sign, so type is used to store the sign. Here, because the data is a string, we can obtain the sign directly by using the character string segmentation function

```var data = {
name:namein.value,
type:moneyin.value.slice(0,1),
money:moneyin.value.slice(1)
}```

Let's enter the data first, and then click the button to see the console What is the output of log (data)

On the console, we can see that the input data is stored in the way we want. Next, we need a method to add the stored data to the history. We encapsulate an add record function addLi:

```function addLi(data)
{
var ul = document.querySelector('ul');//Get history
var str = `<li>
<span class="name">\${data.name}</span>
<span class="money">\${data.type + data.money}</span>
<span class="del">x</span>
</li>`;
}```

This step is to add li to ul, we call this function in middle note, then we can see if this operation has been successful.

### 3. Modify the history panel style

After clicking Add new transaction, the data normally appears in the history record, but the style is a little ugly. What we want to achieve is that the right border of expenditure record is orange and the right border of income record is light blue. Then we just need to modify the style in the li tag:

`<li style="border-right:4px solid \${data.type==="+" ? "skyblue" : "tomato"}">`

If the value of the type attribute of our data is a plus sign, it's a sky blue border, otherwise it's a tomato color. Here, it's very easy to implement it through a ternary expression.

Let's add a few more records to see the effect:

In this way, the different effects of the border are realized. Let's modify the style of the amount to make it have the same effect as the border

Just copy the ternary expression just now:

`<span class="money" style="color:\${data.type==="+" ? "skyblue" : "tomato"}">\${data.type + data.money}</span>`

In this way, our history record style will be modified

### 4. Realize the deletion function

The deletion function is implemented in the addLi() method. Every time we add a record, we add a click event to the deletion mark of the record. When we click the cross, we delete the history record

Relevant codes:

```var li = ul.children[ul.children.length-1];
ul.removeChild(li);
})```

Because the records we added are all added after the last li of ul, so the last child of ul is the Li tag we added, and because the span tag with cross is the last child in Li, we can add click events to him.

In this way, our addLi() module has been written temporarily. Let's sort it out:

```function addLi(data)
{
var ul = document.querySelector('ul');//Get history
var str = `<li style="border-right:4px solid \${data.type==="+" ? "skyblue" : "tomato"}">
<span class="name">\${data.name}</span>
<span class="money" style="color:\${data.type==="+" ? "skyblue" : "tomato"}">\${data.type + data.money}</span>
<span class="del">x</span>
</li>`;
var li = ul.children[ul.children.length-1];
ul.removeChild(li);
})
}```

### 5. Statistical amount

Next, let's start counting the amount, and also encapsulate it into a method called setMoney.

Because we want to count the amount of each record, we have to store all the data. We define an array records = [] globally to store data objects.

In the click event of adding a new record, data is the newly created record object. We need to store this object in the array just defined through the push method.

In the setMoney function, records [i] You may not understand the division of money / 1 by 1. Because the attribute of the object is a string, we need to realize the accumulation of amount rather than the superposition of strings, so we need to convert it into numerical type.

```function setMoney() {
var sumzhichu = 0;
var sumshouru = 0;
var shouru = document.querySelector('#shouru');
var zhichu = document.querySelector('#zhichu');
var yue = document.querySelector('#yue');
for(var i = 0;i<records.length;i++) {
if(records[i].type === '+') {
sumshouru += records[i].money/1;
}
else{
sumzhichu += records[i].money/1;
}
}
shouru.innerHTML = sumshouru;
zhichu.innerHTML = sumzhichu;
yue.innerHTML = sumshouru-sumzhichu;
}```

In the above example code, 'Shouru', 'Zhichu' and 'Yue' are the span tags of the obtained amount. You can see the following html structure. We call the setMoney() function in the click event of adding a new transaction, so that every time we add a new transaction, we call the setMoney method again to count the amount, and then re assign the value to the corresponding span tag through innerHTML, so as to realize the dynamic change of expenditure, income and balance

ย

The question is, if we delete a record, how can we add or subtract the corresponding amount?

Before deleting a record, we should also delete the data of this record in the array. Then we can improve this function in the click event of the cross:

```li.children[li.children.length-1].addEventListener('click',function() {
for(var i = 0;i<records.length;i++) {
if(records[i].name === li.children[0].innerHTML) {
records.splice(i,1);
break;
}
}
setMoney();
ul.removeChild(li);
})```

Find the object to be deleted in the array through a for loop, delete the element through the splice method, and call the setMoney function again to realize the dynamic change of the amount after deletion.

After entering the content, we have to clear the Input box. We write the code at the end of the click event of adding the transaction button:

```add.addEventListener('click',function() {
//1. Determine the content of the input box
return;
}
//2. Create li, fill li and add li
//Store the data we enter in a set
var data = {
name:namein.value,
type:moneyin.value.slice(0,1),
money:moneyin.value.slice(1)
}
records.push(data);
setMoney();
namein.value = '';
moneyin.value = '';
});```

## 2, Local storage

We used to store data in the records array, which is a temporary role, because when we refresh the page, the data will be gone. Let's save the data locally:

We have established a constructor for local storage, and we don't need any parameters. Because localstorage itself exists, we only need to judge localstorage If record does not exist, assign it to an empty array.

```function Record() {
if(!localStorage.record) {
localStorage.record = '[]';
}
}```

Why do empty arrays here still have quotes?

Because the local storage can only store strings, the object can be JSON Stringify () is encoded and stored, or through JSON Parse() gets the data after parsing

Why not directly operate it, but encapsulate it?

Because in this way, Record is equivalent to a tool class and an abstract class. It has only operations and no entity content, so the code we write is more independent and extensible. We write the encapsulated methods on the prototype object of Record, so that we can call these methods directly when creating the object.

Here we need to know some knowledge about prototype and prototype chain. This is another blog I wrote about prototype and prototype chain. Students who don't know much about relevant knowledge can have a look:

https://blog.csdn.net/qq_49900295/article/details/123953725?spm=1001.2014.3001.5502https://blog.csdn.net/qq_49900295/article/details/123953725?spm=1001.2014.3001.5502

### 1. Get cache records

First, write a method to get the cached record content:

```Record.prototype.getRecords = function() {
return JSON.parse(localStorage.record);
}```

We pass the data in the local storage through JSON The parse method can convert a string into an array

### 2. Add new data to the cache

Similarly, create an add data method addData in the prototype object. In this method, we call the getRecords function to get the array, and then push the new data into it through JSON Stringify converts the array into a string and saves it back

```Record.prototype.addData = function(data) {
// Get the array add data update cache
var arr = this.getRecords();
arr.push(data);
localStorage.record = JSON.stringify(arr);
}
```

### 3. Display history according to local cache

Because one of the most important functions of our bookkeeping book is that even if we close the page, the data will not be destroyed when we open it again. Therefore, if we open the bookkeeping book for the first time and finish the transaction, the second opening should be displayed in the history record, so we need to add li to the history record ul according to the local cache.

We create a Record object at the starting position. The next step is to get the history and add the corresponding li. We previously wrote an addLi method, which can be used directly

```var record = new Record();
//2. Get the history and add the corresponding li
if(record.getRecords !=[]) {
for(let i = 0;i<record.getRecords().length;i++) {
}
}```

Because we used the records array before, all related operations should be changed to the operation of localstorage

So our original records Instead of adding objects to the array, push has to add data to the cache. We created a method to add data to the cache in the prototype, and just change it to the addition method in the Record prototype we defined:

` record.addData(data);`

### 4. Rewrite setMoney method

Because there is no record array, our setMoney has to be rewritten. We create two more methods in the prototype to calculate the total revenue and total expenditure (the two codes are similar, and only the code for calculating the total revenue is shown below). The balance can be obtained by subtracting the two. Here is the code for calculating the total income:

```Record.prototype.shouru = function() {
var total = 0;
var arr = this.getRecords();
arr.forEach(function(data) {
if (data.type === "+") {
total += data.money/1;
}
})
}```

We modify the setMoney function:

```function setMoney() {
var shouru = document.querySelector('#shouru');
var zhichu = document.querySelector('#zhichu');
var yue = document.querySelector('#yue');
shouru.innerHTML = record.shouru();
zhichu.innerHTML = record.zhichu();
yue.innerHTML = record.shouru()-record.zhichu();
}```

### 5. Delete the specified data in the cache

Now there is another last problem, that is, there is still the records array we defined before in the deleted record. We need to judge which is the same between the transaction name of the li to be deleted and the name value of the object in the locally stored array, and then pass the corresponding index number as a parameter to the method delData to delete the specified data.

```li.children[li.children.length-1].addEventListener('click',function() {
for(let i = 0;i<record.getRecords().length;i++) {
if(li.children[0].innerHTML == record.getRecords()[i].name) {
record.delData(i);
}
}
setMoney();
ul.removeChild(li);
})```

Then, in the prototype of record, we create another method to delete the specified index number data in the array:

```Record.prototype.delData = function(index) {
var arr = this.getRecords();
arr.splice(index, 1);
localStorage.record = JSON.stringify(arr);
}```

Here, we have achieved the effect of local storage. We first realized the local operation, and then changed the data from array to local storage, so that the data will not be destroyed when refreshing the page

## 3, Pop up window optimization

In this way, we have the last step left. When the input is illegal, we define an alert pop-up window at the beginning. That's too ugly. We want to achieve this effect:

When the input content is compliant, a light blue box pops up and disappears. When it is illegal, an orange box pops up.

How should we use native JS to achieve this effect?

We only need to add a transition height of hide: overflow to the box of hide: overflow. In this way, when we enter the violation, we will make the box have a height, and the box will have the effect of pulling out slowly. Then, add a timer to make the height of the box change to 0 again in a few seconds, and the pop-up window will go back. The effect of pulling out first and then returning is realized.

Relevant codes:

```function errorAlert() {
var error = document.querySelector('#error');
error.style.height = '4rem';
setTimeout(function(){
error.style.height = '0';
},800);
}```

In this way, we can call this function in case of violation and replace the original alert. Similarly, there is a pop-up window when the record is added successfully. The code is similar, but there is no explanation here.