目录
一、本地操作
1.判断输入内容是否合规
2.实现信息的添加
3.修改历史记录面板样式
4.实现删除功能
5.统计金额
二、本地存储
1.获取缓存记录
2.向缓存中添加新数据
3.根据本地缓存显示历史记录
4.重写setMoney方法
5.在缓存中删除指定数据
三、弹窗优化
我把记账本项目部署在了码云上,大家可以复制链接在浏览器打开体验一下:
http://jie_shao1112.gitee.io/bookkeeping-book
源码地址在文章末尾
整体效果:
我们所有的操作都是围绕点击添加新交易来进行的
那么我们先给添加新交易这个按钮绑定点击事件,那么显然我们要对输入进去的内容进行一个判断,我们待会把isAlert函数补全,isAlert函数就是在输入框内容不符规范时返回flase,这样就弹出一个有问题的对话框提醒用户,我们先做一个简易的,主要先把逻辑搞懂,然后再加以完善
var add = document.querySelector('#add');//获取添加交易按钮
var namein = document.querySelector('#name');//获取姓名框
var moneyin = document.querySelector('#money');//获取金额框
add.addEventListener('click',function() {
//1.判断输入框的内容
if(!isAlert()) {
alert('有问题');
}
})
在isAlert函数里我们用了一个正则表达式来判定金额框里输入的内容是否合理,我们规定必须加减开头,因为这样后续方便进行运算,要么就是纯整数,小数的话小数点在整数后且最多两位
如果交易名称的输入框为空或金额输入框里内容不合理就返回false
function isAlert() {
var reg = /^[\+\-](\d+|\d+\.\d{1,2})$/;
if(namein.value === "" || reg.test(moneyin.value) === false) {
return false;
}
else{
return true;
}
}
当我们输入完交易的名字和金额后点击添加新交易,该条记录就会插入在历史记录面板中,下面我们就要实现这一效果。
我们先看一下历史记录的html结构,ul就是历史记录的盒子,每条记录都是一个li,作为孩子添加到ul中:
回到添加新交易的点击事件中,我们把获取的数据以对象的形式存储起来
因为在历史记录中,支出右边的边框是橙色,收入是淡蓝色,所以我们需要通过正负号判断他是支出还是收入,所以type就用来专门存储正负号,这里因为拿过来的数据是字符串所以直接用字符串分割函数就能把正负号获取
var data = {
name:namein.value,
type:moneyin.value.slice(0,1),
money:moneyin.value.slice(1)
}
我们先输入数据然后点击按钮看看console.log(data)输出的是什么
在控制台中我们可以看到,输入的数据通过我们想要的方式存储起来了,下面我们需要一个方法把存储的数据添加到历史记录中,我们封装一个添加记录函数addLi:
function addLi(data)
{
var ul = document.querySelector('ul');//获取历史记录
var str = `
${data.name}
${data.type + data.money}
x
`;
ul.insertAdjacentHTML('beforeend',str);
}
这一步就是添加li到ul中,我们在点击事件中调用这个函数,然后我们看看这部操作有没有成功:
点击添加新交易后数据正常在历史记录里出现了,就是样式有点丑,我们想实现的是支出记录的右边框为橙色,收入记录的右边框记录为淡蓝色,那我们只需在li标签里修改一下样式就行:
如果我们data数据的type属性值是加号就是天蓝色的边框否则就是tomato色,这里我们通过一个三元表达式实现就非常的简便了。
我们再添加几条记录看看效果出没出来:
这样边框的不同效果就实现了,我们再修改一下金额的样式,让他和边框一个效果
只需要把刚刚的三元表达式再复制一下就行:
${data.type + data.money}
这样我们的历史记录样式就修改完了
删除功能在addLi()方法里实现,我们每添加一条记录的同时,就给这条记录的删除标记添加一个点击事件,当点这个叉号的时候,就删掉该条历史记录
相关代码:
var li = ul.children[ul.children.length-1];
li.children[li.children.length-1].addEventListener('click',function() {
ul.removeChild(li);
})
因为我们添加的记录都是添加到ul的最后一个li的后面,所以ul的最后一个孩子就是我们添加的li标签,又因为带叉号的span标签是li里面的最后一个孩子,所以我们给他添加点击事件就可以啦。
这样我们的addLi()模块就暂时写完了,整理一下:
function addLi(data)
{
var ul = document.querySelector('ul');//获取历史记录
var str = `
${data.name}
${data.type + data.money}
x
`;
ul.insertAdjacentHTML('beforeend',str);
var li = ul.children[ul.children.length-1];
li.children[li.children.length-1].addEventListener('click',function() {
ul.removeChild(li);
})
}
下面我们就开始统计金额,同样把它封装为一个方法叫做setMoney。
因为我们要统计每条记录的金额,那么就得把所有数据存储起来。我们在全局定义一个数组records=[],来存储data对象。
在添加新记录的点击事件中,data是新创建的记录对象,我们需要通过push方法把这个对象存储在刚刚定义的数组中。
在setMoney函数里records[i].money/1这个除1大家可能不太理解,因为对象的属性是一个字符串,所以我们要实现金额的累加而不是字符串的叠加,所以要把它转化为数值型。
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
在上例代码中,'shouru','zhichu','yue' 都是获取的金额的span标签,大家可以看下面的html结构。我们在添加新交易的点击事件里调用setMoney()函数,这样每次添加新交易的时候我们都重新调用setMoney方法统计金额,再通过innerHTML给对应的span标签重新赋值,这样就实现了支出,收入和余额的动态变化
问题来了,那如果我们删除了某一条记录,应该怎么实现对应金额的加减呢?
我们在删除记录之前,也应该把这条记录的数据在数组里删掉,那么我们在叉号的点击事件里完善一下这个功能:
li.children[li.children.length-1].addEventListener('click',function() {
for(var i = 0;i
通过一个for循环在数组中找到要删除的对象通过splice方法删除元素,删除完之后再次调用setMoney函数实现金额的动态变化。
输完内容后还得清空Input框,我们把代码写在添加交易按钮的点击事件最后:
add.addEventListener('click',function() {
//1.判断输入框的内容
if(!isAlert()) {
alert('有问题');
return;
}
//2.创建li,填充li,添加li
//把我们输进去的数据存储在一个集合里
var data = {
name:namein.value,
type:moneyin.value.slice(0,1),
money:moneyin.value.slice(1)
}
addLi(data);
records.push(data);
setMoney();
namein.value = '';
moneyin.value = '';
});
我们之前存储数据的是records数组,这是一个临时角色,因为当我们刷新页面的时候,数据就都没了,下面我们通过本地存储来实现保留数据的效果:
我们建立了一个本地存储的构造函数,什么参数也不需要,因为localstorage本身就是存在的,所以我们只需要判断localstorage.record存不存在,不存在就把他赋值为一个空数组。
function Record() {
if(!localStorage.record) {
localStorage.record = '[]';
}
}
为什么这里的空数组还有带上引号呢?
因为本地存储只能存储字符串,可以将对象JSON.stringify()编码后存储,或者通过JSON.parse()解析后获取数据
为什么不直接操作而是把他封装起来呢?
因为这样Record就相当于是一个工具类,一个抽象类,他只有操作没有实体内容,这样我们写出来的代码独立性与扩展性就更强。我们把封装的方法写在Record的原型对象上,这样我们在创建对象时就可以直接调用这些方法。
这里需要我们了解一些原型与原型链的知识,这是我写的另一篇关于原型与原型链的博客,对相关知识点不太了解的同学可以看一看:
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
首先写一个获取缓存的记录内容方法:
Record.prototype.getRecords = function() {
return JSON.parse(localStorage.record);
}
我们把本地存储里的数据通过JSON.parse方法可以把字符串转化为数组
同样在原型对象中创建一个添加数据方法addData,在这个方法里我们调用getRecords函数拿到数组,然后把新的数据push进去,再通过JSON.stringify将数组转化为字符串再保存回去
Record.prototype.addData = function(data) {
// 拿到数组 添加数据 更新缓存
var arr = this.getRecords();
arr.push(data);
localStorage.record = JSON.stringify(arr);
}
因为我们的记账本最重要的功能之一就是即便关掉了页面,再次打开数据也不会被销毁,所以如果我们第一次打开记账本写完交易后,第二次打开应该在历史记录中显示出来,这样我们就需要根据本地缓存来对历史记录ul进行添加li的操作。
我们在起始位置创建一个Record对象,下一步就是获取历史记录并添加相应的li,我们之前写过一个addLi方法,这个方法是可以直接拿来用的
var record = new Record();
//2.获取历史记录,并添加对应的li
if(record.getRecords !=[]) {
for(let i = 0;i
因为我们之前用的是records数组,相关的操作全部都要改成localstorage的操作
这样我们原来的records.push不是向数组里添加对象嘛,就得改成向缓存中添加数据,我们之前在原型里创建了向缓存添加数据的方法,改成我们定义的Record原型里的添加方法就行:
record.addData(data);
因为已经没有records这个数组了,所以我们的setMoney也得重新写,我们在原型里再创建两个方法用来计算总收入和总支出(二者代码相似下面只展示计算总收入的代码)。余额就用二者相减就能得到,下面是计算总收入的代码:
Record.prototype.shouru = function() {
var total = 0;
var arr = this.getRecords();
arr.forEach(function(data) {
if (data.type === "+") {
total += data.money/1;
}
})
return total;
}
我们把setMoney函数进行修改:
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();
}
现在还有最后一个问题,就是在删除记录里还是有我们之前定义的records数组,我们需要判断要删除的li的交易名和本地存储的数组里的对象的name值哪个一样,然后把对应的索引号作为参数传给删除指定数据的方法delData就行。
li.children[li.children.length-1].addEventListener('click',function() {
for(let i = 0;i
然后我们在record的原型里再创建一个删除数组内指定索引号数据的方法:
Record.prototype.delData = function(index) {
var arr = this.getRecords();
arr.splice(index, 1);
localStorage.record = JSON.stringify(arr);
}
到这里我们的本地存储的效果就都实现了,我们先实现了本地的操作,再把数据从数组改为了本地存储的方式,这样数据就不会在刷新页面时被销毁
这样我们就剩下最后一步了,就是当输入违规的时候,我们在最开始定义的是一个alert弹窗,那样太丑了,我们想实现这样的效果:
当输入的内容合规时弹出一个淡蓝色的框框然后消失,违规的时候弹出一个橙色的框框。
我们应该怎么用原生JS实现这个效果呢?
我们只需要让盒子的高度为0,然后再来个溢出隐藏overflow:hidden,再加一个过渡效果。这样当我们输入违规的时候,就让盒子有一个高度,盒子就有这种缓慢拉出的效果了,然后再加一个定时器在多少秒后让盒子的高度再变为0,这样弹窗就又回去了。就实现了先拉出再返回的效果。
相关代码:
function errorAlert() {
var error = document.querySelector('#error');
error.style.height = '4rem';
setTimeout(function(){
error.style.height = '0';
},800);
}
这样我们在违规的时候调用这个函数就行,替换掉原来的alert,同理,添加记录成功时也有一个这样的弹窗,代码相似这里不过多说明。
相信大家看完本文后都能做出一个属于自己的记账本了,有需要源码的同学就点击这个链接:
https://gitee.com/jie_shao1112/bookkeeping-bookhttps://gitee.com/jie_shao1112/bookkeeping-book
看到这里的同学点个赞支持下好不好