当前位置: 首页 > 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>…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...