当前位置: 首页 > news >正文

html记账本改写:保存数据 localStorage。

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><title>记账本改写</title><style>table {user-select: none;/* width: 100%; */border-collapse: collapse;}table,th,td {border: 1px solid black;}th,td {padding: 8px;text-align: center;}</style>
</head>
<body><table id="ledgerTable"><thead><tr><th style="width: 50px;"><input type="month" id="monthPicker"></th><th colspan="3">日常开销</th></tr><tr><th>日期</th><th>项目</th><th>支出</th><th>收入</th></tr></thead><tbody></tbody><tfoot><tr><td><strong>总和</strong></td><td id="totalAmount">0</td><td id="totalExpense">0</td><td id="totalIncome">0</td></tr></tfoot></table><script>let currentYear, currentMonth;document.getElementById('monthPicker').addEventListener('change', function () {let selectedDate = new Date(this.value);currentYear = selectedDate.getFullYear();currentMonth = selectedDate.getMonth() + 1; // 月份从0开始,需要加1let daysInMonth = new Date(currentYear, currentMonth, 0).getDate();let tbody = document.querySelector('#ledgerTable tbody');// 清空tbody中的所有行tbody.innerHTML = '';// 为每一天添加行for (let day = 1; day <= daysInMonth; day++) {let newRow = document.createElement('tr');newRow.innerHTML = `<td>${day}</td><td><button class="add-item">+</button></td><td>0</td><td>0</td>`;newRow.querySelector('.add-item').addEventListener('click', addItemHandler);tbody.appendChild(newRow);}// 从localStorage加载数据loadData();// 初始化总和updateTotals();});// 添加项目的事件处理函数function addItemHandler() {const newItem = createItemElement({ name: '', expense: '', income: '' });this.parentNode.insertBefore(newItem, this);updateDailyTotals(this.closest('tr'));updateTotals(); // 更新总和saveData(); // 保存数据}// 创建项目元素的函数function createItemElement(item) {const newItem = document.createElement('div');newItem.innerHTML = `<input type="text" placeholder="项目" value="${item.name}"><select class="type-select"><option value="expense">支出</option><option value="income">收入</option></select><input type="number" placeholder="金额" style="width: 70px;" value="${item.expense || item.income || ''}"><button class="remove-item">-</button>`;// 获取金额输入框和类型选择框const amountInput = newItem.querySelector('input[type="number"]');const typeSelect = newItem.querySelector('.type-select');// 添加事件监听器addEventListeners(amountInput, typeSelect, newItem);// 给“-”按钮添加事件监听newItem.querySelector('.remove-item').addEventListener('click', function () {const row = this.closest('tr');// 弹出确认对话框const confirmDelete = confirm("确定要删除此项目吗?");if (confirmDelete) {this.parentNode.remove();updateDailyTotals(row);updateTotals(); // 更新总和saveData(); // 保存数据}});return newItem;}// 添加事件监听器的函数function addEventListeners(amountInput, typeSelect, newItem) {// 监听金额输入框的变化amountInput.addEventListener('input', function () {if (typeSelect.value === 'expense' && this.value > 0) {this.value = -this.value; // 如果选择“支出”且金额为正数,自动转换为负数}updateDailyTotals(newItem.closest('tr')); // 更新每日总和updateTotals(); // 更新总和saveData(); // 保存数据});// 监听类型选择框的变化typeSelect.addEventListener('change', function () {if (this.value === 'expense') {// 如果选择“支出”,确保金额为负数if (amountInput.value > 0) {amountInput.value = -amountInput.value;}amountInput.min = '-999999';amountInput.max = '0';} else if (this.value === 'income') {// 如果选择“收入”,确保金额为正数if (amountInput.value < 0) {amountInput.value = -amountInput.value;}amountInput.min = '0';amountInput.max = '999999';}updateDailyTotals(newItem.closest('tr')); // 更新每日总和updateTotals(); // 更新总和saveData(); // 保存数据});}// 更新每日总和的函数function updateDailyTotals(row) {const items = row.querySelectorAll('div');let expenseTotal = 0;let incomeTotal = 0;items.forEach(item => {const amountInput = item.querySelector('input[type="number"]');const amount = parseFloat(amountInput.value) || 0;if (amount < 0) {expenseTotal += amount;} else {incomeTotal += amount;}});row.querySelectorAll('td')[2].textContent = expenseTotal;row.querySelectorAll('td')[3].textContent = incomeTotal;}// 更新总和的函数function updateTotals() {const rows = document.querySelectorAll('#ledgerTable tbody tr');let totalExpense = 0;let totalIncome = 0;rows.forEach(row => {const expenseCell = row.querySelectorAll('td')[2];const incomeCell = row.querySelectorAll('td')[3];totalExpense += parseFloat(expenseCell.textContent) || 0;totalIncome += parseFloat(incomeCell.textContent) || 0;});document.getElementById('totalExpense').textContent = totalExpense;document.getElementById('totalIncome').textContent = totalIncome;document.getElementById('totalAmount').textContent = totalIncome + totalExpense;}// 保存数据的函数function saveData() {if (!currentYear || !currentMonth) return;const key = `ledger_${currentYear}_${currentMonth}`;const data = [];const rows = document.querySelectorAll('#ledgerTable tbody tr');rows.forEach(row => {const day = row.querySelector('td').textContent;const items = row.querySelectorAll('div');items.forEach(item => {const name = item.querySelector('input[type="text"]').value;const type = item.querySelector('.type-select').value;const amount = item.querySelector('input[type="number"]').value;data.push({ day, name, type, amount });});});localStorage.setItem(key, JSON.stringify(data));}// 加载数据的函数function loadData() {if (!currentYear || !currentMonth) return;const key = `ledger_${currentYear}_${currentMonth}`;const data = JSON.parse(localStorage.getItem(key)) || [];const rows = document.querySelectorAll('#ledgerTable tbody tr');data.forEach(item => {const row = Array.from(rows).find(row => row.querySelector('td').textContent === item.day);if (row) {const newItem = createItemElement({ name: item.name, expense: item.type === 'expense' ? item.amount : '', income: item.type === 'income' ? item.amount : '' });row.querySelector('td:nth-child(2)').insertBefore(newItem, row.querySelector('.add-item'));}});rows.forEach(row => updateDailyTotals(row));updateTotals();}</script>
</body>
</html>

相关文章:

html记账本改写:保存数据 localStorage。

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>记账本改写</title><style>table {user-select: none;/* width: 100%; */border-collapse: collapse;}table,th,td {border: 1px solid…...

frida检测绕过-libmsaoaidsec.so

libmsaoaidsec.so 部分检测手段 检测机制在native层实现一般在init_proc()函数中触发使用 pthread_create 创建2个检测线程 绕过: nop pthread_create 的调用 eg: 在 bilibil1 - v7.26.1版本中, 在got表导入了pthread_create 绕过: 替换dlsym(xx, "pthread_create "…...

Splasthop 安全远程访问帮助企业对抗 Cobalt Strike 载荷网络攻击

一、背景 根据 FreeBuf&#xff08;标题为&#xff1a;潜藏系统2个月未被发现&#xff0c;新型网络攻击瞄准中国高价值目标&#xff09;和 The Hacker News&#xff08;标题为&#xff1a;New Cyberattack Targets Chinese-Speaking Businesses with Cobalt Strike Payloads&a…...

Rust:Restful API 服务程序开发详述

0. 关于异步程序设计 0.1 对异步机制的理解 运行效率对于后端程序来讲很重要。我曾经以为&#xff0c;多线程机制是后端设计的终极方法&#xff0c;后来才发现&#xff0c;异步机制才是榨干 CPU 运行效率资源的关键所在。 我最初对于异步程序设计有误解&#xff0c;以为多线…...

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 09部署OSPF

本章的目的是帮助网络工程师确定网络的理想 OSPF 配置。本章将回答以下问题 应何时在数据中使用OSPF ?配置 OSPF 的关键设计原则是什么?OSPFv2 和 OSPFv3 之间有什么区别&#xff0c;应如何使用?如何在路由协议栈中配置 OSPF ?如何在服务器上配置 OSPF&#xff0c;例如为容…...

【Visual Studio 报错】未加载 wntdll.pdb(一种可行的解决办法)

调试程序时&#xff0c;会出现下面这个报错 分析原因&#xff1a; 出现未加载 wntdll.pdb 报错大概率是你的指针使用错误 &#xff0c;比如使用野指针、越界访问、或者堆区空间释放方式错误等。 这里以 堆区空间释放方式错误 为例子 1、堆区开辟的数组空间使用 delete 释放 …...

P1332 血色先锋队

[题目通道](血色先锋队 - 洛谷) #include<bits/stdc.h> using namespace std; int n,m,a,b,xa[114514],ya[114514],xb[114514],yb[114514],maxx[114514]; int main() {cin>>n>>m>>a>>b;for(int i1;i<a;i)cin>>xa[i]>>ya[i];for(…...

HarmonyOS】ArkTS学习之基于TextTimer的简易计时器的elapsedTime最小时间单位问题

本文旨在纪录自己对TextTimer使用过程的疑惑问题 我在查看教程时候&#xff0c;发现很多博客在onTimer(event: (utc: number, elapsedTime: number) > void) 这里提到elapsedTime&#xff1a;计时器经过的时间&#xff0c;单位为毫秒。我不清楚是否为版本问题。 在我查看ver…...

函数指针学习

认识函数指针&#xff1a; 函数指针的用处&#xff1a; 回调函数&#xff08;以函数指针为参数的参数&#xff09; 定义带标签的结构体 typedef struct mode { int data; } Node; 标签&#xff08;mode&#xff09;: mode 是结构体的标签名。在这种定义中&#xff0c;mo…...

『功能项目』武器的切换实例【34】

本章项目成果展示 我们打开上一篇33战士的A键连击的项目&#xff0c; 本章要做的事情是按键盘E键切换职业时切换手中的武器 首先在资源商店下载免费的武器模型 创建一个空物体 命名为WeaponPos 将武器预制体拖拽至WeaponPos &#xff08;注意调整空物体位置就可以后续文章会更…...

github中action作用和讲解

1&#xff0c;简介 GitHub Actions 是 GitHub 的一个自动化功能&#xff0c;它允许你在 GitHub 仓库中自动执行软件开发工作流程。你可以使用 GitHub Actions 来执行各种任务&#xff0c;比如&#xff1a; 自动测试&#xff1a;每当代码被推送到仓库时&#xff0c;自动运行测试…...

数据库管理-第238期 23ai:全球分布式数据库-架构与组件(20240904)

数据库管理238期 2024-09-04 数据库管理-第238期 23ai&#xff1a;全球分布式数据库-架构与组件&#xff08;20240904&#xff09;1 架构图2 分片数据库与分片3 Shard Catalog4 Shard Director5 Global Service6 管理界面总结 数据库管理-第238期 23ai&#xff1a;全球分布式数…...

GIT | git提交注释自动添加信息头

GIT | git提交注释自动添加信息头 时间&#xff1a;2024年9月6日10:20:11 文章目录 GIT | git提交注释自动添加信息头1.操作2.commit-msg文件 1.操作 2.commit-msg文件 #!/bin/sh # # An example hook script to check the commit log message. # Called by "git commit&q…...

React 全屏问题解决方案

1、全屏下弹窗被遮挡的问题 参考&#xff1a;https://www.jianshu.com/p/b22d1ad9533e 原因&#xff1a; 需要全屏的节点部分被传入 screenfull 中&#xff0c;弹窗的层级永远低于全屏&#xff0c;所以被遮挡。 解决方法&#xff1a; 方式1&#xff1a;把整个 body 全屏&…...

Java JVM 垃圾回收算法详解

Java 虚拟机&#xff08;JVM&#xff09;是运行 Java 应用程序的核心&#xff0c;它的垃圾回收&#xff08;Garbage Collection, GC&#xff09;机制是 JVM 中非常重要的一个部分。垃圾回收的主要任务是自动管理内存&#xff0c;回收那些不再被使用的对象&#xff0c;从而释放内…...

hadoop dfs web页面访问增加鉴权

前言 装好了Hadoop&#xff0c;通过浏览器访问&#xff0c;发现竟然不需要鉴权就能访问&#xff0c;且暴露了很多服务器层文件路径信息&#xff0c;基于多年积累的安全意识&#xff0c;必须得配置些鉴权信息&#xff0c;就有了该文&#xff0c;仅做学习记录&#xff0c;下次自…...

LCP 485. 最大连续 1 的个数[lleetcode -11]

从今天起&#xff0c;我们的算法开始研究搜索&#xff0c;首先就是DFS深度优先搜索&#xff08;depth-first seach&#xff0c;DFS&#xff09;在搜索到一个新的节点时&#xff0c;立即对该新节点进行遍 历&#xff1b;因此遍历需要用先入后出的栈来实现&#xff0c;也可以通过…...

关于宏任务的说法已经过时

关于宏任务w3c的最新解释&#xff0c;&#xff08;mdn已经搜不到宏任务队列&#xff09; ● 每个任务都有一个任务类型&#xff0c;用一个类型的任务必须在一个队列&#xff0c;不同类型的任务可以分属不同的队列。在一次事件循环当中&#xff0c;浏览器可以根据实际情况从不同…...

Java箱与泛型

大O的渐进表示法 大 O 的渐进表示法 去掉了那些对结果影响不大的项 &#xff0c;简洁明了的表示出了执行次数。 void func1(int N){ int count 0; for (int i 0; i < N ; i) { for (int j 0; j < N ; j) { count; } } for (int k 0; k < 2 * N ; k) { count; } in…...

QT如何判断一个文件是否存在

在Qt中&#xff0c;判断一个文件是否存在是一个常见的操作&#xff0c;可以通过QFile类和QDir类来实现。不过&#xff0c;对于简单的文件存在性检查&#xff0c;QFile类提供的接口更为直接。下面是一个使用QFile类来判断文件是否存在的例子&#xff1a; #include <QFile>…...

Vim笔记

【指尖飞舞&#xff1a;vscode vim 高效开发&#xff08;系列视频&#xff09;】https://www.bilibili.com/video/BV1z541177Jyp16&vd_source23e4761174881d73295e362ffd706749 Vscode vim插件配置-CSDN博客 g_跳到行尾最后一个非空字符 gd :go to definition ctrl (o): …...

宝塔部署Vue项目解决跨域问题

一、前言 使用宝塔面板部署前端后端项目相比用命令行进行部署要简单许多&#xff0c;宝塔的可视化操作对那些对Linux不熟悉的人很友好。使用宝塔部署SpringBoot后端项目和Vue前端项目的方法如下&#xff1a; 1、视频教程 2、文字教程1 3、文字教程2 以上的教程完全可以按照步骤…...

C++智能指针简述

auto_ptr 设计目的&#xff1a;超出作用域&#xff0c;自动释放内存。所有权传递&#xff1a;拷贝auto_ptr时&#xff0c;原指针失效&#xff0c;释放所有权&#xff1b;新指针获得所有权。总而言之&#xff0c;允许通过拷贝转移所有权&#xff0c;独占所有权。缺陷&#xff1…...

龙芯+FreeRTOS+LVGL实战笔记(新)——05部署主按钮

本专栏是笔者另一个专栏《龙芯+RT-Thread+LVGL实战笔记》的姊妹篇,主要的区别在于实时操作系统的不同,章节的安排和任务的推进保持一致,并对源码做了改进和优化,各位可以先到本人主页下去浏览另一专栏的博客列表(目前已撰写36篇,图1所示),再决定是否订阅。此外,也可以…...

Android Camera系列(二):TextureView+Camera

两岸猿声啼不住&#xff0c;轻舟已过万重山—李白 Android Camera系列&#xff08;一&#xff09;&#xff1a;SurfaceViewCamera Android Camera系列&#xff08;二&#xff09;&#xff1a;TextureViewCamera Android Camera系列&#xff08;三&#xff09;&#xff1a;GLS…...

DFS算法专题(一)——二叉树中的深搜【回溯与剪枝的初步注入】

目录 1、DFS算法简介 2、算法实战应用【leetcode】 2.1 计算布尔二叉树的值 2.1.1 算法原理 2.1.2 算法代码 2.2 求根节点到叶节点数字之和 2.2.1 算法原理 ​2.2.2 算法代码 2.3 二叉树剪枝 2.3.1 算法原理 2.3.2 算法代码 2.4 验证二叉搜索树 2.4.1 算法原理 …...

AWS SES服务 Golang接入教程(排坑版)

因为刚来看的时候 也迷迷糊糊的 所以 先讲概念 再上代码 一 基础设置 这里需要完成两个最基础的设置任务 1 是验证至少一个收件电子邮箱 2 【很关键】是验证发送域。即身份里的域类型的身份。&#xff08;可以理解为配置你的域名邮箱服务器&#xff08;SMPT&#xff09;为亚马…...

Vite + Vue3 +Vant4出现Toast is not a function

今天写前端的时候出现了这个问题搞了我一会 搜集原因: 1:是vant版本的问题&#xff0c;Toast()的方法是vant3版本的写法&#xff0c;而我用的是vant4&#xff0c;vant4中的写法改成了showToast()方法&#xff0c;改正过来 import {showToast} from "vant"; 发现还是…...

【MATLAB】模拟退火算法

模拟退火算法的MATLAB实现 模拟退火算法简介模拟退火算法应用实例关于计算结果 模拟退火算法简介 1982年&#xff0c;Kirkpatrick 将退火思想引入组合优化领域&#xff0c;提出了一种能够有效解决大规模组合优化问题的算法&#xff0c;尤其对 NP 完全问题表现出显著优势。模拟…...

什么是Kubernetes RBAC?

什么是Kubernetes RBAC? 1、什么是RBAC?2、核心组件3、优势💖The Begin💖点点关注,收藏不迷路💖 在Kubernetes集群中,RBAC(基于角色的访问控制)是保障系统安全的关键。它通过角色和绑定管理不同实体对资源的访问权限,具有显著优势: 1、什么是RBAC? RBAC是Kube…...