这是我想出的一个小应用程序,它也可以满足您的需求。
它由应位于同一目录中的五个文件组成。
祝您的孩子身体健康!
功能包括:
- 添加和删除新的食物类型
- 在多个列表中添加和删除食物
- 更改、增加和减少列表中食品的数量
- 管理(添加、删除、重命名)多个列表
- 在本地浏览器中保存和加载数据
- 通过文本区域导出和导入数据以将数据保存在外部。
- 对大屏幕和手持设备的布局支持
myCarbCalculator.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Carb Calculator</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link rel="stylesheet" href="myCarbCalculator.css"></link>
<link rel="stylesheet" media="screen" href="myCarbCalculator-Screen.css"></link>
<link rel="stylesheet" media="handheld" href="myCarbCalculator-Handheld.css"></link>
<script type="text/javascript" src="myCarbCalculator.js"></script>
</head>
<body onload="init();">
<div id="root">
<div id="header">
<button id="btnSaveSettings">save Settings</button>
<button id="btnLoadSettings">load Settings</button>
<button id="btnImportSettings">import Settings</button>
<button id="btnExportSettings">export Settings</button>
<button id="btnResetSettings">reset Settings</button>
</div>
<div id="center">
<div id="content-main">
<div id="c_foodSelector">
<label for="selFoodSelector">select a type of food</label>
<select id="selFoodSelector"></select>
<button id="btnAddSelectedFoodToList">Add Food to List</button>
</div>
<div id="c_foodTable">
<div id="c_foodTableOptions">
<input id="p_foodTableName" value="Your List of Food Items"/>
<button id="btnRenameFoodTable">rename List</button>
<button id="btnNewFoodTable">new List</button>
</div>
<table id="foodTable" class="fill-width"></table>
</div>
</div>
<div id="content-additional">
<div id="c_results">
<label for="inputResultCarbs">Carbs Total</label>
<input id="inputResultCarbs" readonly="readonly"/>
</div/>
<div id="c_foodTableSelection">
<table id="foodTableSelection" class="fill-width"></table>
</div>
<div id="c_output">
<h3>Import/Export</h3>
<textarea id="p_output" class="fill-width"></textarea>
</div/>
</div>
</div>
<div id="footer">
<label for="p_newFoodName">Food Name</label><input id="p_newFoodName"/>
<label for="p_newFoodCarbs">Carb Value</label><input id="p_newFoodCarbs"/>
<button id="btnNewFood">add a new type of food</button>
<button id="btnDeleteSelectedFood">delete Selected type of food</button>
</div>
</div>
</body>
</html>
myCarbCalculator.js
var SAVEID = 'carbCalculatorSettings';
function MyCarbCalculator(){
var self = this;
this.settings = null;
this.initSettings = function(){
self.settings = {
'activeFoodList' : 'default',
'foodData' : {},
'foodList' : {
'default' : {
'label' : 'Your List of Food Items',
'list' : {},
'dateCreated' : '',
'dateChanged' : '',
'notes' : 'This is the default Food List'
}
}
};
};
this.clearContents = function(element){
while (element.firstChild) {
element.removeChild(element.firstChild);
}
};
this.formatDate = function(timestamp){
if(timestamp != ""){
var date = new Date(timestamp);
var monthNames = [
"January", "February", "March",
"April", "May", "June", "July",
"August", "September", "October",
"November", "December"
];
var day = date.getDate();
var monthIndex = date.getMonth();
var year = date.getFullYear();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
return day + ' ' + monthNames[monthIndex] + ' ' + year + ' ' + hours + ':'+ minutes + ':'+ seconds;
}
return "---";
};
//////////////////////////////////////////////////////////////////
// Load and Save Data
//////////////////////////////////////////////////////////////////
this.resetSettings = function(){
self.initSettings();
self.updateView();
};
this.saveSettings = function(){
localStorage.setItem(SAVEID,JSON.stringify(self.settings));
};
this.loadSettings = function(){
var saveData = localStorage.getItem(SAVEID);
if(saveData != null && saveData.length > 0){
self.settings = JSON.parse(localStorage.getItem(SAVEID));
self.updateView();
}
else{
self.initSettings();
}
};
this.importSettings = function(){
localStorage.setItem(SAVEID,document.getElementById('p_output').value);
self.loadSettings();
};
this.exportSettings = function(){
document.getElementById('p_output').value = localStorage.getItem(SAVEID);
};
//////////////////////////////////////////////////////////////////
// Manage Food Data
//////////////////////////////////////////////////////////////////
this.updateView = function(){
self.updateFoodSelector();
self.updateFoodTable();
self.updateFoodTableSelection();
self.updateResult();
};
//////////////////////////////////////////////////////////////////
// Manage Food Data
//////////////////////////////////////////////////////////////////
this.newFood = function(){
var name = document.getElementById('p_newFoodName').value;
var carbs = document.getElementById('p_newFoodCarbs').value;
var id = Date.now();
self.settings.foodData[id] = {'name':name,'carbs':carbs};
self.updateFoodSelector();
};
this.removeSelectedFoodData = function(){
var foodSelector = document.getElementById('selFoodSelector');
var foodDataId = foodSelector.options[foodSelector.selectedIndex].value;
delete self.settings.foodData[foodDataId];
self.updateFoodSelector();
}
this.updateFoodSelector = function(){
var foodSelector = document.getElementById('selFoodSelector');
self.clearContents(foodSelector);
for(id in self.settings.foodData){
var food = self.settings.foodData[id];
var item = document.createElement("option");
item.value = id;
item.innerHTML = food.name + " (" + food.carbs + ")";
foodSelector.appendChild(item);
}
};
//////////////////////////////////////////////////////////////////
// Manage current Food Table
//////////////////////////////////////////////////////////////////
this.addSelectedFoodToTable = function(){
var activeFoodListId = self.settings.activeFoodList;
var foodList = self.settings.foodList[activeFoodListId];
var foodSelector = document.getElementById('selFoodSelector');
var selectedFoodId = foodSelector.options[foodSelector.selectedIndex].value;
var foodData = self.settings.foodData[selectedFoodId];
var foodItem = {'name':foodData.name,'carbs':foodData.carbs,'count':1};
foodList.list[Date.now()] = foodItem;
foodList.dateChanged = Date.now();
self.updateView();
};
this.updateFoodCount = function(id,value){
var activeFoodListId = self.settings.activeFoodList;
var foodList = self.settings.foodList[activeFoodListId];
foodList.list[id].count = value;
foodList.dateChanged = Date.now();
self.updateView();
};
this.removeFoodItem = function(id){
var activeFoodListId = self.settings.activeFoodList;
var foodList = self.settings.foodList[activeFoodListId];
delete foodList.list[id];
foodList.dateChanged = Date.now();
self.updateView();
};
this.updateFoodTable = function(){
var activeFoodListId = self.settings.activeFoodList;
var foodList = self.settings.foodList[activeFoodListId];
// update the List Name
var foodTableNameElement = document.getElementById('p_foodTableName');
foodTableNameElement.value = foodList.label;
// update the List itself
var foodTable = document.getElementById('foodTable');
self.clearContents(foodTable);
// create the Table Header
var row = document.createElement("tr");
foodTable.innerHTML =
"<tr><th>Name</th><th>Carbs/unit</th><th></th><th>Count</th><th></th></tr>";
for(id in foodList.list){
// create a table structure
var row = document.createElement("tr");
var elm1 = document.createElement("td");
var elm2 = document.createElement("td");
var elm3 = document.createElement("td");
var elm4 = document.createElement("td");
var elm5 = document.createElement("td");
var elm6 = document.createElement("td");
row.appendChild(elm1);
row.appendChild(elm2);
row.appendChild(elm3);
row.appendChild(elm4);
row.appendChild(elm5);
row.appendChild(elm6);
foodTable.appendChild(row);
// create input fields
var food = foodList.list[id];
var inputFoodId = document.createElement("input");
inputFoodId.id = "food-id-" + id;
inputFoodId.type = "hidden";
inputFoodId.value = id;
var inputFoodName = document.createElement("input");
inputFoodName.id = "food-name-" + id;
inputFoodName.setAttribute("readonly","readonly");
inputFoodName.value = food.name;
var inputFoodCarbs = document.createElement("input");
inputFoodCarbs.id = "food-carbs-" + id;
inputFoodCarbs.setAttribute("readonly","readonly");
inputFoodCarbs.style.width = "3em";
inputFoodCarbs.value = food.carbs;
var inputFoodCount = document.createElement("input");
inputFoodCount.id = "food-count-" + id;
inputFoodCount.setAttribute("data-id",id);
inputFoodCount.style.width = "3em";
inputFoodCount.value = food.count;
inputFoodCount.addEventListener("change",function(event){
var inputFoodCount = event.currentTarget;
var id = inputFoodCount.getAttribute("data-id");
var count = inputFoodCount.value;
self.updateFoodCount(id,count);
});
var btnDeleteFoodItem = document.createElement("button");
btnDeleteFoodItem.innerHTML = "remove";
btnDeleteFoodItem.setAttribute("data-id",id);
btnDeleteFoodItem.addEventListener("click",function(event){
var btnDeleteFoodItem = event.currentTarget;
var id = btnDeleteFoodItem.getAttribute("data-id");
self.removeFoodItem(id);
});
var btnCountUp = document.createElement("button");
btnCountUp.innerHTML = "+";
btnCountUp.setAttribute("data-id",id);
btnCountUp.addEventListener("click",function(event){
var id = event.currentTarget.getAttribute("data-id");
var inputFoodCount = document.getElementById("food-count-"+id);
inputFoodCount.value = ++ inputFoodCount.value;
self.updateFoodCount(id,inputFoodCount.value);
});
var btnCountDown = document.createElement("button");
btnCountDown.innerHTML = "-";
btnCountDown.setAttribute("data-id",id);
btnCountDown.addEventListener("click",function(event){
var id = event.currentTarget.getAttribute("data-id");
var inputFoodCount = document.getElementById("food-count-"+id);
inputFoodCount.value = -- inputFoodCount.value;
self.updateFoodCount(id,inputFoodCount.value);
});
// append input fields to the table row
elm1.appendChild(inputFoodId); // this one is invisible anyway
elm1.appendChild(inputFoodName);
elm2.appendChild(inputFoodCarbs);
elm3.appendChild(btnCountDown);
elm4.appendChild(inputFoodCount);
elm5.appendChild(btnCountUp);
elm6.appendChild(btnDeleteFoodItem);
}
};
//////////////////////////////////////////////////////////////////
// Calculate Results
//////////////////////////////////////////////////////////////////
this.calculateCarbsForList = function(listId){
var foodListData = self.settings.foodList[listId].list;
var total = 0;
for(id in foodListData){
var item = foodListData[id];
total = total + (item.carbs * item.count);
}
return total;
};
this.updateResult = function(){
var activeFoodListId = self.settings.activeFoodList;
var foodList = self.settings.foodList[activeFoodListId];
var inputResultCarbs = document.getElementById("inputResultCarbs");
inputResultCarbs.value = self.calculateCarbsForList(activeFoodListId);
};
//////////////////////////////////////////////////////////////////
// Food Table Handling
//////////////////////////////////////////////////////////////////
this.renameFoodTable = function(){
var activeTableId = self.settings.activeFoodList;
var foodList = self.settings.foodList[activeTableId];
var newName = document.getElementById('p_foodTableName').value;
foodList.label = newName;
foodList.dateChanged = Date.now();
self.updateView();
};
this.newFoodTable = function(){
var newTableName = document.getElementById('p_foodTableName').value;
var date = Date.now();
self.settings.foodList[date] = {
'label' : newTableName,
'list' : {},
'dateCreated' : date,
'dateChanged' : date,
'notes' : ''
}
self.settings.activeFoodList = date;
self.updateView();
};
this.updateFoodTableSelection = function(){
var foodTableSelection = document.getElementById('foodTableSelection');
self.clearContents(foodTableSelection);
var foodTableLists = self.settings.foodList;
foodTableSelection.innerHTML =
"<tr><th>Name</th><th>last Change</th><th>Carbs</th><th></th><th></th></tr>";
for(var tableId in foodTableLists){
var foodTable = foodTableLists[tableId];
var row = document.createElement("tr");
if(tableId == self.settings.activeFoodList){
row.classList.add("active");
}
var cell1 = document.createElement("td");
var cell2 = document.createElement("td");
var cell3 = document.createElement("td");
var cell4 = document.createElement("td");
var cell5 = document.createElement("td");
cell1.innerHTML = foodTable.label;
cell1.style.cursor = "help";
cell1.title = foodTable.notes;
cell2.innerHTML = self.formatDate(foodTable.dateChanged);
cell2.title = "created: " + self.formatDate(foodTable.dateCreated);
cell3.innerHTML = self.calculateCarbsForList(tableId);
var btnSelectFoodTable = document.createElement("button");
if(tableId == self.settings.activeFoodList)btnSelectFoodTable.disabled = 'disabled';
btnSelectFoodTable.innerHTML = "select";
btnSelectFoodTable.setAttribute("data-tableId",tableId);
btnSelectFoodTable.addEventListener("click",function(event){
var button = event.currentTarget;
self.settings.activeFoodList = button.getAttribute("data-tableId");
self.updateView();
});
cell4.appendChild(btnSelectFoodTable);
var btnDeleteFoodTable = document.createElement("button");
if(tableId == 'default')btnDeleteFoodTable.disabled = 'disabled';
btnDeleteFoodTable.innerHTML = "delete";
btnDeleteFoodTable.setAttribute("data-tableId",tableId);
btnDeleteFoodTable.addEventListener("click",function(event){
var button = event.currentTarget;
var tableId = button.getAttribute("data-tableId");
if(self.settings.activeFoodList = tableId){
self.settings.activeFoodList = "default";
};
delete self.settings.foodList[tableId];
self.updateView();
});
cell5.appendChild(btnDeleteFoodTable);
row.appendChild(cell1);
row.appendChild(cell2);
row.appendChild(cell3);
row.appendChild(cell4);
row.appendChild(cell5);
foodTableSelection.appendChild(row);
}
};
//////////////////////////////////////////////////////////////////
// Add global Events
//////////////////////////////////////////////////////////////////
document.getElementById("btnNewFood").addEventListener("click",this.newFood);
document.getElementById("btnSaveSettings").addEventListener("click",this.saveSettings);
document.getElementById("btnLoadSettings").addEventListener("click",this.loadSettings);
document.getElementById("btnResetSettings").addEventListener("click",this.resetSettings);
document.getElementById("btnImportSettings").addEventListener("click",this.importSettings);
document.getElementById("btnExportSettings").addEventListener("click",this.exportSettings);
document.getElementById("btnDeleteSelectedFood").addEventListener("click",this.removeSelectedFoodData);
document.getElementById("btnAddSelectedFoodToList").addEventListener("click",this.addSelectedFoodToTable);
document.getElementById("btnRenameFoodTable").addEventListener("click",this.renameFoodTable);
document.getElementById("btnNewFoodTable").addEventListener("click",this.newFoodTable);
//////////////////////////////////////////////////////////////////
// Initialize the Data on screen
//////////////////////////////////////////////////////////////////
self.loadSettings();
}
function init(){
var carCalculator = new MyCarbCalculator();
}
myCarbCalculator.css
body,html{
height:100%;
}
/* dont show borders on input fields if read only */
input:-moz-read-only {
border : none;
}
input:read-only {
border : none;
}
/* spacing elements out */
th{
text-align:left;
}
label,input,button,select{
white-space:nowrap;
margin-right: 1em;
}
button{
height:2.5em;
}
#c_results,#c_foodSelector,#c_output,#footer,#header{
padding:1em;
}
/* make result stand out */
#c_results #inputResultCarbs{
font-size: 2em;
color: #882222;
width:4em;
}
#c_results label[for="inputResultCarbs"]{
font-size: 1em;
padding-top:1em;
}
.fill-width{
width:100%;
}
table#foodTableSelection > tr.active{
background-color:yellow;
}
myCarbCalculator-Screen.css
#root{
display:flex;
flex-direction:column;
height:100%;
}
#header{
display:flex;
flex-direction:row;
border-bottom: 1px solid black;
}
#footer{
display:flex;
flex-direction.row;
border-top: 1px solid black;
}
#center{
flex: 1 0;
display:flex;
flex-direction:row;
}
#content-main{
flex: 1 0 auto;
display:flex;
flex-direction: column;
}
#c_foodSelector{
border-bottom: 1px solid black;
}
#c_foodTable{
}
#content-additional{
flex: 0 1 30%;
display:flex;
flex-direction: column;
border-left: 1px solid black;
}
#c_results{
flex: 1 0 auto;
border-bottom: 1px solid black;
}
#c_foodTableSelection{
flex: 1 0 auto;
border-bottom: 1px solid black;
}
#c_output{
flex: 0 1 50%;
}
myCarbCalculator-Handheld.css
#root{
display:flex;
flex-direction:column;
height:100%;
}
#header{
display:flex;
flex-direction:column;
border-top: 1px solid black;
order: 3;
}
#footer{
display:flex;
flex-direction:column;
border-top: 1px solid black;
order: 2;
}
#center{
flex: 1 0;
display:flex;
flex-direction:column;
order: 1;
}
#content-main{
display:flex;
flex-direction: column;
}
#c_foodSelector{
display:flex;
flex-direction:column;
border-bottom: 1px solid black;
}
#c_foodTable{
border-bottom: 1px solid black;
}
#content-additional{
display:flex;
flex-direction: column;
}
#c_results{
flex: 1 0 auto;
border-bottom: 1px solid black;
}
#c_foodTableSelection{
flex: 1 0 auto;
border-bottom: 1px solid black;
}
#c_output{
flex: 0 1 50%;
}
/*******************************************
ADDITONAL STYLES ONLY FOR HANDHELD LAYOUT
*******************************************/
/* spacing out vertically */
label,input,button,select{
margin-bottom: 0.5em;
}
input[id^="food-name"]{
width:4em;
}
#header:before,#footer:before{
text-align:center;
font-size:1em;
font-weight:bold;
margin-bottom:0.5em;
}
#header:before{
content:'Options';
}
#footer:before{
content:'Foodtypes';
}
技术说明:
我正在使用纯 Javascript 和 css。某些样式可能与 Edge 之前的 Internet Explorer 等较旧的浏览器不兼容。
数据的保存是通过浏览器本地存储来处理的。
导入和导出通过“Javascript Object Notation”(JSON)中的纯文本处理。
应用程序本身被编写为 Javascript 类,例如名为 MyCarbCalculator 的函数,通过 body 的 onload 事件和 init() 函数创建和初始化。
通过这个示例应用程序,我试图展示如何以结构化(面向伪对象)的方式使用 Javascript,以及事件侦听器和未命名函数的强大功能和灵活性,例如使用函数作为参数。
我还使用 CSS 媒体描述符来创建灵活的布局,可以为手持设备和大屏幕定制。这主要是通过“flexbox”样式完成的,它允许非常流畅的布局并提供对元素定位的出色控制。