I am remaking Wordle for a fun project to get my brain going. I have run into an issue though where squares are getting their background color changed when they are not supposed to.
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>replit</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="l1" class="letterBox"></div>
<div id="l2" class="letterBox"></div>
<div id="l3" class="letterBox"></div>
<div id="l4" class="letterBox"></div>
<div id="l5" class="letterBox"></div>
<script src="script.js"></script>
</body>
</html>
js:
var letter = 0
var id
const word = ["h","e","l","l","o"]
var guess = []
window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
var key = event.key
letter+=1
id = "l".concat(letter)
document.getElementById(id).innerHTML = key
guess.push(key)
event.preventDefault();
if(letter == 5){
for(i in word){
b=parseInt(i)+1-0
letter = word[i]
for(x in guess){
gulet = guess[x]
if(gulet==letter){
id = "l"+b
document.getElementById(id).style.background = "yellow"
}
}
}
}
}, true);
css:
html, body {
width: 100%;
height: 100%;
}
#element1 {display:inline-block;margin-right:10px;}
.letterBox {
display: inline-block;
text-align: center;
font-size: 40px;
height: 50px;
width: 50px;
background-color: #ffffff;
border: 2px solid black;
border-radius: 7px;
var letter = 0
var id
const word = ["h","e","l","l","o"]
var guess = []
window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
var key = event.key
letter+=1
id = "l".concat(letter)
document.getElementById(id).innerHTML = key
guess.push(key)
event.preventDefault();
if(letter == 5){
for(i in word){
b=parseInt(i)+1-0
letter = word[i]
for(x in guess){
gulet = guess[x]
if(gulet==letter){
id = "l"+b
document.getElementById(id).style.background = "yellow"
}
}
}
}
}, true);
html, body {
width: 100%;
height: 100%;
}
#element1 {display:inline-block;margin-right:10px;}
.letterBox {
display: inline-block;
text-align: center;
font-size: 40px;
height: 50px;
width: 50px;
background-color: #ffffff;
border: 2px solid black;
border-radius: 7px;
<div id="l1" class="letterBox"></div>
<div id="l2" class="letterBox"></div>
<div id="l3" class="letterBox"></div>
<div id="l4" class="letterBox"></div>
<div id="l5" class="letterBox"></div>
The constant 'word' is what the letters are being compared to.
Someone removed this part so I am adding it back. An example of a word that breaks it is 'halaa' and 'haala'
I researched this problem and I have not found anyone with this same problem, so I do not know where to even start.
There are quite some mistakes in your code, I'll try to address them one by one:
Watch out ids with leading numbers
No need for the letter variable, we can use guess.length for the same result
id = "l".concat(letter) can just ben 'l' + n' (but not needed)
b=parseInt(i)+1-0 can be: parseInt(i) + 1, since the - 0 doesn't do anything
if(gulet==letter){ compares an char vs a int, won't work as expected
Fixing the above, simplifying the code, gives us something like:
const word = ["h","e","l","l","o"]
var guess = []
window.addEventListener("keydown", (event) => {
if (event.defaultPrevented) {
return; // Do nothing if the event was already processed
}
event.preventDefault();
var key = event.key
var id = "l" + (guess.length + 1);
document.getElementById(id).innerHTML = key
guess.push(key)
if (guess.length == 5){
for (let i in guess){
if (guess[i] == word[i]){
id = 'l' + (+i + 1)
document.getElementById(id).style.background = "yellow" ;
}
}
}
}, true);
html, body { width: 100%; height: 100%; }
.letterBox { display: inline-block; text-align: center; font-size: 40px; height: 50px; width: 50px; background-color: #ffffff; border: 2px solid black; border-radius: 7px; }
#element1 {display:inline-block;margin-right:10px;}
<div id="l1" class="letterBox"></div>
<div id="l2" class="letterBox"></div>
<div id="l3" class="letterBox"></div>
<div id="l4" class="letterBox"></div>
<div id="l5" class="letterBox"></div>
I changed this code snippet for you & I hope it works
if(letter === 5){
let idx = 0;
for(let i in word){
if (word[i] === guess[i]) {
document.getElementById(`l${idx}`).style.background = "yellow";
}
idx++;
}
}
--- UPDATED QUESTION ---
Thanks for all the answers. I wrote the JS code to delete the parent div when clicking its corresponding button in my JS PRACTICE!!!
However, the same JS code does not work in my real JS project where all the parent div are created dynamically. The complete code can be found below.
There is no error but the JS code just does not work. Any ideas?
BELOW IS THE SIMPLIFIED **REAL JS PROJECT ** COMPLETE CODE
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload Imgs</title>
<style type="text/css">
.container {
width: 100%;
}
.display-area {
width: 100%;
height: auto;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
img {
max-width: 100%;
}
.image-preview {
width: 80%;
min-height: 300px;
border: 2px dashed #dddddd;
display: block;
/*default text*/
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: #cccccc;
}
.newbtns {
border: 0;
background: lightgrey;
text-shadow: 1px 1px 1px white;
border: 1px solid #999;
position: absolute;
display: block;
}
</style>
</head>
<body>
<div class="container">
<div id='inputFiles'><input type="file" class="file" name="image_uploads" accept="image/png, image/jpeg, image/jpg"
multiple></div>
<div class="display-area" id='imgDisplay'>
</div>
<div id="defaultContent">
<p>No images</p>
</div>
<button type="button" value="Reload page" onclick="window.location.reload()">Reload Page</button>
</div>
</div>
</body>
<script>
var input = document.querySelector('input');
var uploadBox = document.getElementById('uploadBox');
var defaultContent = document.getElementById('defaultContent');
var imgDisplay = document.getElementById('imgDisplay')
//upload & preview
input.addEventListener('change', function () {
var imgFiles = input.files;
defaultContent.style.display = 'none';
for (var i = 0; i < imgFiles.length; i++) {
var imgDiv = document.createElement('div');
imgDiv.className = 'imgBox';
imgDiv.id = 'box' + i;
imgDiv.style.width = "20%";
var images = document.createElement('img');
images.src = URL.createObjectURL(imgFiles[i]);
var newbtn = document.createElement("button");
newbtn.type = "button";
newbtn.className = "newbtns";
newbtn.innerHTML = "X";
newbtn.style.color = "orange";
newbtn.style.background = 'red';
newbtn.id = 'newbtn' + i;
imgDiv.appendChild(newbtn);
imgDiv.appendChild(images);
imgDisplay.appendChild(imgDiv);
}
});
allButtons = document.getElementsByTagName('button');
for (var n = 0; n < allButtons.length; n++) {
if (allButtons[n].getAttribute('id') === 'newbtn' + n) {
allButtons[n].onclick = function () {
this.parentNode.parentNode.removeChild(this.parentNode);
}
} else { };
}
</script>
</html>
you can do something like this:
const buttonOne = document.getElementById('btn1');
const buttonTwo = document.getElementById('btn2');
buttonOne.addEventListener("click", () => deleteElementAndThisChildNodes('box1'))
buttonTwo.addEventListener("click", () => deleteElementAndThisChildNodes('box2'))
function deleteElementAndThisChildNodes(parentId) {
document.getElementById(parentId).remove()
}
To each of your button elements add onclick="DeleteParent(this)" then outside of your dynamic divs include the following:
<script type="text/javascript">
function DeleteParent(button){
button.parentElement.remove();
}
</script>
You can do this:
const display = document.getElementById("imgdisplayarea");
display.addEventListener("click", e => {
if(e.target.tagName === 'BUTTON'){
//if an element within a display div for a button, remove your father
e.target.parentNode.remove();
}
});
Here is a very simple example that works exactly how you want it (based on your question):
function disable() {
document.getElementById("demo").style.display = "none";
}
<div id="demo">
<button onclick="disable()">
Click Me
</button>
<h3>
This is part of the div
</h3>
</div>
I am trying to remove item from the array by selecting a cell to the left of the button, I have tried using .previous selector but it doesn't work.
var films = [];
function test2(e) {
$("button").one("click", function(r) {
r.preventDefault();
var movie = $(e).attr("name");
if (films.includes(movie)) {
alert("This movie is in your basket")
} else {
films.push(movie);
var r = films.length;
$("#table1").empty();
for (var i = 0; i < r; i += 1) {
$("#table1").append("<tr><td>" + films[i] + "</td><td>" + "<button onclick='newtest2(this)'>Remove</button>" + "</td></tr>");
}
}
})
};
function newtest2(e) {
$(e).parents('tr').remove();
}
.cart {
float: right;
position: relative;
margin-right: 15%;
margin-top: 5%;
}
.cart2 {
background-color: white;
border-radius: 19px;
-moz-border-radius: 19px;
-webkit-border-radius: 19px;
padding: 2% 3%;
border: 2px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="cart">
<div class="cart2">
<h1>Cart: </h1>
<table id="table1">
</table>
</div>
</div>
<button name="Black Mirror" id="BM" id="carttest" onclick="test2(this)" value="Black Mirror" class="button1">Book now (Black Mirror)</button>
<br>
<button name="Star Wars" id="SW" id="carttest" onclick="test2(this)" value="Star Wars" class="button1">Book now(Star Wars)</button>
I also have this bug that I have to click the button twice to fire the function.
Edit:
Whenever the user clicks Order button, it runs the loop that creates the table with a button "remove" and adds the item to 'Films[]' Array. I want the remove button to remove the selected row but I also want it to remove the item from the array.
Well it needs a lot of fixes in your code.
First. Identifiers must be unique for each element.
2nd. When you push in array, you'll have to remove it too.
Here is what was missing in your code
var films = [];
$(".button1").click(function(e){
var movie = $(this).attr("name");
if (films.includes(movie)) {
alert("This movie is in your basket")
} else {
films.push(movie);
var r = films.length;
$("#table1").empty();
for (var i = 0; i < r; i += 1) {
$("#table1").append("<tr><td>" + films[i] + "</td><td>" + "<button onclick='newtest2(this)'>Remove</button>" + "</td></tr>");
}
}
})
function newtest2(e) {
$(e).parents('tr').remove();
films.splice(films.indexOf($(e).parent().prev().text()), 1)
}
.cart {
float: right;
position: relative;
margin-right: 15%;
margin-top: 5%;
}
.cart2 {
background-color: white;
border-radius: 19px;
-moz-border-radius: 19px;
-webkit-border-radius: 19px;
padding: 2% 3%;
border: 2px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="cart">
<div class="cart2">
<h1>Cart: </h1>
<table id="table1">
</table>
</div>
</div>
<button name="Black Mirror" id="BM" value="Black Mirror" class="button1">Book now (Black Mirror)</button>
<br>
<button name="Star Wars" id="SW" value="Star Wars" class="button1">Book now(Star Wars)</button>
Don't need prevent preventDefault() for a click
var films = [];
function test2(e) {
//Don't need preventDefault()
var movie = $(e).attr("name");
if (films.includes(movie)) {
alert("This movie is in your basket")
} else {
films.push(movie);
var r = films.length;
$("#table1").empty();
for (var i = 0; i < r; i += 1) {
$("#table1").append("<tr><td>" + films[i] + "</td><td>" + "<button onclick='newtest2(this)'>Remove</button>" + "</td></tr>");
}
}
};
function newtest2(e) {
const
//parents of the clicked button
parents = $(e).parents('tr'),
//children of the parents
children = parents.children(),
//text content from first child
text = children[0].textContent,
//position of the text in array
pos = $.inArray(text, films)
//if position is not -1 (not found)
if (pos !== -1 ) {
//use position to remove item from the array
films.splice(pos, 1)
}
console.log(`films:`,films)
//remove the tr element from the view
$(e).parents('tr').remove();
}
The muasif80's code works just fine too.
I have created a calculator I use for counting carbs for my 7 y/o type 1 diabetic but as I add more values into my array the page is getting too long.
I'm looking for a way to start with a single select for the food name then select the weight and it calculates the carbs. Then have a button to dynamically add another row to the form in order to select a new food item and calculate the results of any further additions.
This is my functional code base:
<html><head>
<meta name = "viewport" content = "initial-scale = 1.0, user-scalable = no">
<title>Carb Calculator</title>
<style>
#container{width: 200px; margin: 0 auto;}
label { font-size:20px; display: inline-block; width: 45%; text-align: right;}
input[type="text"][disabled] {width: 12%; background-color: white; color: black; font-weight: bolder;}
input[type="button"] {}
select {width: 15%}
</style></head>
<body>
<script language="javascript" type="text/javascript">
var myArray = [['Banana',0.1428571429], ['Blackberry',0.1], ['Blueberry',0.1418918919], ['Carrots',0.09836065574], ['Cantaloupe',0.08], ['Cherry Tomato',0.05882352941], ['Cucumber',0.03653846154], ['Green apple',0.1373626374], ['Honeydew',0.09], ['Pear',0.15], ['Raspberry',0.12], ['Plum',0.11], ['Strawberry',0.075], ['Watermelon',0.075]];
function reset(){
var t=0;
for (var i=0; i<myArray.length; i++) {
var v = "val"+i;
document.calc.elements[v].value=0;
}
}
function calculate(){
var t=0;
var tt=0;
for (var i=0; i<myArray.length; i++) {
var v = "val"+i;
var a = "answer"+i;
if(isNaN(parseInt(document.calc.elements[v].value))) {
//document.calc.elements[a].value="";
} else {
tt=(parseInt(document.calc.elements[v].value))* myArray[i][1];
document.calc.elements[a].value=tt.toFixed(1);
t+=tt;
}
}
document.calc.answerTot.value=(t).toFixed(1)
}
document.write("<form name=\"calc\" action=\"post\">");
for (var i=0; i<myArray.length; i++) {
var vv = "val"+i;
var aa = "answer"+i;
document.write("<label>"+myArray[i][0]+":</label> <select name=\""+ vv +"\" onchange=\"calculate()\" >");
for (var j=0; j<301; j++) {
document.write("<option value=" +j+ ">" +j+ "</option>");
}
document.write("</select><input type=text name=" +aa+ " size=5 placeholder=\"Carbs\" disabled><br>");
}
document.write("<br><label for=\"answerTot\">Total carbs: </label> <input type=text name=answerTot size=5 disabled></br></br> <div style=\"text-align:center\"> <input type=button value=Calculate onClick=\"calculate()\"></br></br><input type=button value=Reset onClick=\"reset()\"></div>");
</script></body></html>
Hello there and welcome to StackOverflow!
Your code required a bit of rework and questions like that (more like a task than a question) aren't usually very successful around here.
Having said that, I wrote something that will hopefully help you, the nice thing about it being that if you want to add new types of food you'll only need to add them in the array and the js will take care of it.
Careful with the approximation to 1 digit though, you might lose some carbs in the calculation. Also, please check your carbs, see if I haven't modified anything by mistake.
var myArray = [
['Food', 0],
['Banana', 0.1428571429],
['Blackberry', 0.1],
['Blueberry', 0.1418918919],
['Carrots', 0.09836065574],
['Cantaloupe', 0.08],
['Cherry Tomato', 0.05882352941],
['Cucumber', 0.03653846154],
['Green apple', 0.1373626374],
['Honeydew', 0.09],
['Pear', 0.15],
['Raspberry', 0.12],
['Plum', 0.11],
['Strawberry', 0.075],
['Watermelon', 0.075]
];
function reset() {
var inputs = document.getElementById("calculatorForm").getElementsByTagName("input");
for (var i = 0; i < inputs.length; i++) {
inputs[i].value = "";
}
document.getElementById("answerTot").value = "";
}
function calculate() {
var t = 0;
var tt = 0;
var inputs = document.getElementById("calculatorForm").getElementsByTagName("input");
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].disabled == false) {
if (parseInt(inputs[i].value) > 0) {
tt = (parseInt(inputs[i].value) * getValue(inputs[i].previousSibling.value));
t = +t + +tt;
inputs[i].nextSibling.value = tt.toFixed(1);
}
}
}
console.log(t.toFixed(1));
document.getElementById("answerTot").value = (t).toFixed(1)
}
function add() {
document.getElementById("calculatorForm");
var o = document.createElement("option");
var sel = document.createElement("select");
var inp = document.createElement("input");
var close = document.createElement("span");
var entry = document.createElement("div");
var carbs = document.createElement("input");
carbs.disabled = true;
carbs.className = "result";
entry.className = "entry";
close.className = "remove";
close.innerHTML = "Remove";
for (i = 0; i < myArray.length; i++) {
o = document.createElement("option");
o.value = myArray[i][0];
o.innerHTML = myArray[i][0];
sel.appendChild(o);
}
close.addEventListener("click", function() {
this.parentElement.remove();
calculate();
})
entry.appendChild(sel);
entry.appendChild(inp);
entry.appendChild(carbs);
entry.appendChild(close);
document.getElementById("calculatorForm").appendChild(entry);
}
function getValue(food) {
for (var i = 0; i < myArray.length; i++) {
if (myArray[i][0] == food) return myArray[i][1];
}
}
function getIndex(food) {
for (var i = 0; i < myArray.length; i++) {
if (myArray[i][0] == food) return i;
}
}
window.onload = function() {
add();
}
* {
box-sizing: border-box;
}
#container {
width: 200px;
margin: 0 auto;
}
input[type="text"][disabled] {
outline: none;
border: 1px solid gray;
background-color: white;
color: black;
font-weight: bolder;
}
input[type="button"] {}
select {
width: 15%
}
#calculatorForm {
width: 300px;
margin: auto;
text-align: center;
}
#calculatorForm .entry > * {
width: 140px;
height: 20px;
margin: 5px;
}
#calculatorForm .entry > span {
font-size: 11px;
line-height: 20px;
cursor: pointer;
}
#calculatorForm .entry > .result {
width: 240px;
}
.control {
width: 300px;
margin: auto;
text-align: center;
}
.control>label {
font-size: 11px;
width: 290px;
display: block;
margin: auto;
text-align: left;
margin-bottom: -5px;
}
.control>input {
display: block;
width: 290px;
padding: 5px;
margin: 5px auto;
}
<form name="calc" action="post" id="calculatorForm">
</form>
<div style="text-align:center" class="control">
<label for="answerTot">Total carbs </label>
<input type=text id=answerTot size=5 disabled>
<input type=button value="Add Food" onClick="add()">
<input type=button value=Calculate onClick="calculate()">
<input type=button value=Reset onClick="reset()">
</div>
If anyone wants to see the result, this is what I ended up with. I added a couple event listeners and made the weight value a select rather than an input. Thanks again #Paul for the assistance this is exactly what I was trying to accomplish!
<html>
<head>
<meta name = "viewport" content = "initial-scale = 1.0, user-scalable = no">
<title>Carb Calculator</title>
<style>
{
box-sizing: border-box;
}
#container {
width: 200px;
margin: 0 auto;
}
input[type="text"][disabled] {
outline: none;
border: 1px solid gray;
background-color: white;
color: black;
font-weight: bolder;
}
input[type="button"] {}
select {
width: 15%
}
#calculatorForm {
width: 300px;
margin: auto;
text-align: center;
}
#calculatorForm .entry > * {
width: 145px;
height: 20px;
margin: 2px;
}
#calculatorForm .entry > span {
font-size: 11px;
line-height: 20px;
cursor: pointer;
}
#calculatorForm .entry > .result {
width: 50px;
}
.control {
width: 300px;
margin: auto;
text-align: center;
}
.control>label {
font-size: 11px;
width: 290px;
display: block;
margin: auto;
text-align: left;
margin-bottom: -5px;
}
.control>input {
display: block;
width: 290px;
padding: 5px;
margin: 5px auto;
}
</style>
</head>
<body>
<script language="javascript" type="text/javascript">
var myArray = [
['Food', 0],
['Banana', 0.1428571429],
['Blackberry', 0.1],
['Blueberry', 0.1418918919],
['Carrots', 0.09836065574],
['Cantaloupe', 0.08],
['Cherry Tomato', 0.05882352941],
['Cucumber', 0.03653846154],
['Green apple', 0.1373626374],
['Honeydew', 0.09],
['Pear', 0.15],
['Raspberry', 0.12],
['Plum', 0.11],
['Strawberry', 0.075],
['Watermelon', 0.075]
];
function reset() {
var inputs = document.getElementById("calculatorForm").getElementsByClassName("result");
for (var i = 0; i < inputs.length; i++) {
inputs[i].value = 0;
}
document.getElementById("answerTot").value = "";
}
function calculate() {
var t = 0;
var tt = 0;
var inputs = document.getElementById("calculatorForm").getElementsByClassName("result");
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].disabled == false) {
if (parseInt(inputs[i].value) >= 0) {
tt = (parseInt(inputs[i].value) * getValue(inputs[i].previousSibling.value));
t = +t + +tt;
inputs[i].nextSibling.value = tt.toFixed(1);
}
}
}
console.log(t.toFixed(1));
document.getElementById("answerTot").value = (t).toFixed(1)
}
function add() {
document.getElementById("calculatorForm");
var o = document.createElement("option");
var sel = document.createElement("select");
var inpu = document.createElement("select");
var close = document.createElement("span");
var entry = document.createElement("div");
var carbs = document.createElement("input");
carbs.disabled = true;
carbs.className = "result";
inpu.className = "result";
entry.className = "entry";
close.className = "remove";
close.innerHTML = "Remove";
for (i = 0; i < myArray.length; i++) {
o = document.createElement("option");
o.value = myArray[i][0];
o.innerHTML = myArray[i][0];
sel.appendChild(o);
}
for (var j=0; j<301; j++){
inpu.options[inpu.options.length]=new Option(j,j)
}
close.addEventListener("click", function() {
this.parentElement.remove();
calculate();
})
inpu.addEventListener("change", function() {
calculate();
})
sel.addEventListener("change", function() {
calculate();
})
entry.appendChild(sel);
entry.appendChild(inpu);
entry.appendChild(carbs);
entry.appendChild(close);
document.getElementById("calculatorForm").appendChild(entry);
}
function getValue(food) {
for (var i = 0; i < myArray.length; i++) {
if (myArray[i][0] == food) return myArray[i][1];
}
}
function getIndex(food) {
for (var i = 0; i < myArray.length; i++) {
if (myArray[i][0] == food) return i;
}
}
window.onload = function() {
add();
}
</script>
<form name="calc" action="post" id="calculatorForm"></form>
<div style="text-align:center" class="control"><br>
<label for="answerTot">Total carbs </label>
<input type=text id=answerTot size=5 disabled>
<input type=button value="Add Food" onClick="add()">
<input type=button value=Calculate onClick="calculate()">
<input type=button value=Reset onClick="reset()">
</div>
</body></html>
Here is a small application I came up with that could also suit your needs.
It consists of five files that should be located in the same directory.
Best of health for your child!
Features include:
adding and removing of new food types
adding and removing food to/from multiple lists
changing, increasing and decreasing count of fooditems in lists
manage (add,remove,rename) multiple lists
save and load data in your browser locally
export and import data through a text area to save your data externally.
layout support for both big screen and handheld devices
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';
}
Technical Notes:
I am using pure Javascript and css. Some styles might not be compatible with older browsers like Internet Explorer prior to Edge.
Saving of data is handled through the browsers local storage.
Import and Export is handled through plain text in "Javascript Object Notation" (JSON).
The Application itself is written as a Javascript Class e.g. Function named MyCarbCalculator which is created and initialized through the bodies onload event and an init() function.
With this example application I'm trying to show how to use Javascript in a structured (pseudo object oriented) way and the power and flexibility of event listeners and unnamed functions e.g. the use of functions as parameters.
Also I am using CSS media descriptors to create a flexible layout that can be customized for both handheld devices and big screens. This is mostly done through the "flexbox" style which allows for very fluid layouts and offers great control over the positioning of elements.