Angular update object in array on ng-click? - javascript

On the snippet below each cell contains the name of a shape that are taken from a given sequence. I'm trying to update the shape name of the selected cell on ng-click="fadeName(card)", however, when clicking it does update all cells with the same shape name.For instance, if you click row1,col1 which is by default square, all other squares will be updated, I only want to update the selected one. How can I update only the selected cell value ?
// constant variables
var constants = new (function() {
var rows = 10;
var columns = 10;
this.getRows = function() { return rows; };
this.getColumns = function() { return columns; };
})();
// Global Variables
var shapeSequence =
[
{id: '1', name:'square'},
{id: '2', name:'triangle'},
{id: '3', name:'circle'},
{id: '4', name:'oval'},
{id: '5', name:'pentagon'},
{id: '6', name:'hexagon'},
{id: '7', name:'decagon'},
]
// this function creates deck of cards that returns an object of cards
function createDeck() {
var rows = constants.getRows();
var cols = constants.getColumns();
var key = createColors();
var deck = {};
deck.rows = [];
// create each row
for(var i = 0; i < rows; i++) {
var row = {};
row.cards = [];
// creat each card in the row
for (var j = 0; j < cols; j++) {
var card = {};
card.item = key.shift();
row.cards.push(card);
}
deck.rows.push(row);
}
return deck;
}
function createColors() {
var coloredCards = [];
var rows = constants.getRows();
var cols = constants.getColumns();
var cells = rows * cols;
for (var n = 0; n < cells; n++) {
var thisCard = shapeSequence[n % shapeSequence.length];
coloredCards.splice(n, 0, thisCard);
}
return coloredCards;
}
var app = angular.module('cards', ['ngAnimate']);
app.controller("CardController", function($scope) {
$scope.deck = createDeck();
$scope.fadeName = function(card) {
card.item.name = 'black';
}
});
.card_container {
position: relative;
width: 50px;
height: 50px;
text-align: center;
vertical-align: middle;
line-height: 50px;
z-index: 1;
font-size: 1em;
border:solid 1px;
border-color:black;
}
.card_container {
-moz-perspective: 1000;
-webkit-perspective: 1000;
perspective: 1000;
}
.card {
width: 100%;
height: 100%;
cursor: pointer;
}
table {
margin: 0px auto;
}
.cntr {
margin: 15px auto;
}
<html ng-app="cards">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-animate.min.js" type="text/javascript"></script>
</head>
<body>
<div class="cntr" ng-controller="CardController">
<table >
<tr ng-repeat="row in deck.rows">
<td ng-repeat="card in row.cards">
<div class="card_container " >
<div class="card " ng-click="fadeName(card)" ng-mouseenter="hover = true" ng-mouseleave="hover = false" >
<p ng-if="hover"> {{card.item.name}} </p>
</div>
</div>
</td>
</tr>
</table>
</div>
</html>

While pushing to row
do
card = JSON.parse(JSON.stringify(card));
row.cards.push(card);

You added reference from shapeSequence into your cells where you updated one cells data that will reflect everywhere where you used the same reference. So I only created clone while creating colors in 'createColors()'. clone() is also added.
// constant variables
var constants = new (function() {
var rows = 10;
var columns = 10;
this.getRows = function() { return rows; };
this.getColumns = function() { return columns; };
})();
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
// Global Variables
var shapeSequence =
[
{id: '1', name:'square'},
{id: '2', name:'triangle'},
{id: '3', name:'circle'},
{id: '4', name:'oval'},
{id: '5', name:'pentagon'},
{id: '6', name:'hexagon'},
{id: '7', name:'decagon'},
]
// this function creates deck of cards that returns an object of cards
function createDeck() {
var rows = constants.getRows();
var cols = constants.getColumns();
var key = createColors();
var deck = {};
deck.rows = [];
// create each row
for(var i = 0; i < rows; i++) {
var row = {};
row.cards = [];
// creat each card in the row
for (var j = 0; j < cols; j++) {
var card = {};
card.item = key.shift();
row.cards.push(card);
}
deck.rows.push(row);
}
return deck;
}
function createColors() {
var coloredCards = [];
var rows = constants.getRows();
var cols = constants.getColumns();
var cells = rows * cols;
for (var n = 0; n < cells; n++) {
var thisCard = shapeSequence[n % shapeSequence.length];
coloredCards.splice(n, 0, clone(thisCard));
}
return coloredCards;
}
var app = angular.module('cards', ['ngAnimate']);
app.controller("CardController", function($scope) {
$scope.deck = createDeck();
$scope.fadeName = function(card) {
card.item.name = 'black';
}
});
.card_container {
position: relative;
width: 50px;
height: 50px;
text-align: center;
vertical-align: middle;
line-height: 50px;
z-index: 1;
font-size: 1em;
border:solid 1px;
border-color:black;
}
.card_container {
-moz-perspective: 1000;
-webkit-perspective: 1000;
perspective: 1000;
}
.card {
width: 100%;
height: 100%;
cursor: pointer;
}
table {
margin: 0px auto;
}
.cntr {
margin: 15px auto;
}
<html ng-app="cards">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular-animate.min.js" type="text/javascript"></script>
</head>
<body>
<div class="cntr" ng-controller="CardController">
<table >
<tr ng-repeat="row in deck.rows">
<td ng-repeat="card in row.cards">
<div class="card_container " >
<div class="card " ng-click="fadeName(card)" ng-mouseenter="hover = true" ng-mouseleave="hover = false" >
<p ng-if="hover"> {{card.item.name}} </p>
</div>
</div>
</td>
</tr>
</table>
</div>
</html>

Related

Trying to get values from multiple inputs

I'm trying to make a very basic expense tracker by building off the foundation of a todo app with vanilla Javascript. I'm having trouble isolating the value of all three input bars and getting them to display on the page. At the moment I'm getting 3 [objectHTMLInputElement] and undefined. I'd just like to know if I'm on the right track or if there's an easier way to isolate multiple input values and get them to display on the page. If somebody could point me in the right direction that'd be awesome. Thanks!
let addButton = document.getElementById('add-btn');
addButton.addEventListener('click', add);
let inputName = document.getElementById('input-name');
let inputDate = document.getElementById('input-date');
let inputAmount = document.getElementById('input-amount');
let inputAll = document.querySelectorAll('.input-all');
let expenses = [
]
function add() {
let inputs = inputAll.value;
if (inputs == '') {
return true;
}
expenses.push(inputs);
displayExpenses();
}
function remove() {
}
function displayExpenses() {
let expensesUl = document.getElementById('expenses-ul');
expensesUl.innerHTML = `${inputName}${inputDate}${inputAmount}`;
for (var i = 0; i < expenses.length; i++) {
let expensesLi = document.createElement('li');
expensesLi.innerHTML = expenses[i];
expensesUl.appendChild(expensesLi);
}
}
* {
padding: 0;
box-sizing: border-box;
}
.headings {
text-align: center;
}
.headings h1 {
font-size: 3rem;
font-family: 'Courier New', Courier, monospace;
}
.headings h2 {
margin-top: -20px;
}
form {
text-align: center;
}
#input-name {
width: 50%;
}
#input-date {
width: 18%;
margin-right: 160px;
}
#input-amount {
width: 18%;
margin-left: 18px;
}
#add-btn {
margin-top: 50px;
margin-left: 800px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Expense Tracker</title>
</head>
<body>
<div class="headings">
<h1>Expense Tracker</h1>
<h2>Add A New Item</h2>
</div>
<form>
<label>Name:</label>
<input class="input-all" id="input-name">
<br>
<br>
<label>Date:</label>
<input class="input-all" id="input-date">
<label>Amount:</label>
<input class="input-all" id="input-amount">
</form>
<button id="add-btn">Add Expense</button>
<ul id="expenses-ul"></ul>
<script src="main.js"></script>
</body>
</html>
Try this
const btn = document.getElementById('btn');
btn.addEventListener('click', function (event) {
const form = document.getElementById('form');
const output = document.getElementById('output');
const data = Object.fromEntries(new FormData(form).entries());
output.innerHTML = JSON.stringify(data, undefined, 2);
});
.wrap{
display: flex;
}
#output{
margin-left:50px;
border-width:3px;
border-style:dashed;
border-color:#FFAC55;
padding:5px;
min-width: 150px;
min-height: 80px;
}
<div class="wrap">
<div>
<form id="form">
<label for="name">Name:</label><br>
<input type="text" id="name" name="name"><br>
<label for="date">Date:</label><br>
<input type="text" id="role" name="role"> <br>
<label for="lname">Amount:</label><br>
<input type="text" id="amount" name="amount"><br><br>
<input id="btn" type="button" value="Print all value">
</form>
</div>
<div>
<pre id="output">
</pre>
</div>
</div>
When using document.querySelectorAll it's return a [NodeList] that consists of all selected elements on the other side there's also document.getElementsByClassName that return [HTMLCollection] - whatever you used you need to loop through to get the value of every selected input
When you passed [HTMLInputElement] as innerHTML of expensesUl it's will return the element object name not the value of this element because you are not selected any property of this object so you can't set an object as innerHTML of html element
if you want the right way of this part it's will be like that
let inputName = document.getElementById('input-name');
let inputDate = document.getElementById('input-date');
let inputAmount = document.getElementById('input-amount');
let expensesUl = document.getElementById('expenses-ul');
//this will give you empty string because they aren't get a value yet
expensesUl.innerHTML = `name = ${inputName.value}, date = ${inputDate.value}, amoute = ${inputAmount.value}`;
but now because we are selected all elements we are not need to select every input one by one anymore we will make a loop so we will loop through inputAll var to get the value of [HTMLInputElement] object
let addButton = document.getElementById('add-btn');
addButton.addEventListener('click', add);
function add() {
let inputAll = document.querySelectorAll('.input-all');
for(var i of inputAll) {
if (i.value == '') {
return "Sorry you need to fill all inputs"
}
}
displayExpenses(inputAll);
}
function displayExpenses(elements) {
let expensesUl = document.getElementById('expenses-ul');
for (var i = 0; i < elements.length; i++) {
let expensesLi = document.createElement('li');
expensesLi.innerHTML = elements[i].value
expensesUl.appendChild(expensesLi);
}
}
at the example above i removed expenses array but if you want to use it to take the value of the inputs you can make it like that
let addButton = document.getElementById('add-btn');
addButton.addEventListener('click', add);
function add() {
let inputAll = document.querySelectorAll('.input-all');
let expenses = []
for(var i of inputAll) {
if (i.value == '') {
return "Sorry you need to fill all inputs"
}
expenses.push(i.value)
}
displayExpenses(expenses);
}
function displayExpenses(values) {
let expensesUl = document.getElementById('expenses-ul');
for (var i = 0; i < values.length; i++) {
let expensesLi = document.createElement('li');
expensesLi.innerHTML = values[i]
expensesUl.appendChild(expensesLi);
}
}
the whole code should to be like that
let addButton = document.getElementById('add-btn');
addButton.addEventListener('click', add);
let inputName = document.getElementById('input-name');
let inputDate = document.getElementById('input-date');
let inputAmount = document.getElementById('input-amount');
let inputAll = document.querySelectorAll('.input-all');
let expenses = []
function add() {
for(var i of inputAll) {
if (i.value == '') {
return true
}
expenses.push(i.value)
}
displayExpenses();
}
function displayExpenses() {
let expensesUl = document.getElementById('expenses-ul');
expensesUl.innerHTML = `${inputName.value}, ${inputDate.value}, ${inputAmount.value}`;
for (var i = 0; i < expenses.length; i++) {
let expensesLi = document.createElement('li');
expensesLi.innerHTML = expenses[i];
expensesUl.appendChild(expensesLi);
}
}
about document.getElementsByClassName, document.querySelectorAll one deferant is that you can use array methods like forEach() with document.querySelectorAll while you can't do that with document.getElementsByClassName

how to save the data and the rows in a table with localStorage in html

What i want to save the rows with the data of this code, i know u can use localStorage but i can't figure out how to do it.do i need to add another button to save the data or can i make it to save when i click "save" button or automatically.
this code has some bugs, like if u try to add the same pic twice it won't work.
it has to be a bit cleaned of but i will fix them later
var r = 1;
var nrr = 1;
var img = 0;
var y;
var nr;
var z;
var i;
var image;
var imgUrl;
var x = document.getElementById("myTable").insertRow(r);
function fileChange(event) {
imgUrl = URL.createObjectURL(event.target.files[0]);
}
var loadFile = function (event) {
image = document.getElementById(di);
image.src = imgUrl;
}
function insRow(id) {
//if (imgUrl !== undefined && document.getElementById("Kodi").value !== "" && document.getElementById("Kodi").value !== "") {
r += 1;
x = document.getElementById(id).insertRow(r);
nr = x.insertCell(0);
y = x.insertCell(1);
z = x.insertCell(2);
i = x.insertCell(3);
y.width = 200;
z.width = 200;
i.width = 100;
nr.width = 50;
z.height = 240;
i.height = 240;
nr.height = 240;
nr.style = "text-align : center;font-size : 20px;";
z.class = "z";
i.class = "i";
nrr += 1;
nr.innerHTML = nrr - 1;
y.append = "img";
x.id = "r" + nrr;
var di = "y" + nrr;
y.innerHTML = "<img id=y1 src=''>";
z.innerHTML = "<p id='zp'></p>"
i.innerHTML = "<p id='ip'></p>"
document.getElementById("y1").id = di;
document.getElementById(di).src = imgUrl;
document.getElementById(di).class = "yy";
imgUrl = undefined;
document.getElementById(di).title = "";
document.getElementById("zp").innerHTML = document.getElementById("Kodi").value;
document.getElementById("ip").innerHTML = document.getElementById("sh").value;
document.getElementById("sh").value = "";
document.getElementById("Kodi").value = "";
// }
}
function delRow() {
if (r != 0) {
document.getElementById("myTable").deleteRow(-1);
r -= 1;
nrr -= 1;
}
}
function save() {
document.getElementById('y1').src = imgUrl;
}
.file {
height: 100px;
}
p {
text-align: top-left;
padding-left: 10px;
font-size: 25px;
}
.kodii {
width: 185px;
}
.shh {
width: 90px;
}
.delete {
position: absolute;
left: 550px;
}
#myTable {
width: 523px;
height: 60px;
}
img {
max-height: 300px;
max-width: 200px;
}
.save {
position: absolute;
left: 500px;
}
div {
width: 700px;
height: 1000px;
overflow-y: auto;
}
<input type="file" accept="image/*" name="image" style="color:transparent; width:90px;" title=""
onchange="fileChange(event)"><label for="price">Kodi:</label>
<input type="label" maxlength="25" id="Kodi" value=""
onchange="if (this.value == '') {border-color: red;}else{border-color : green};" class="kodii" name="price">
<label for="desc">Shtesë:</label>
<input type="text" maxlength="12" id="sh" class="shh" value="" name="desc">
<button type="button" class="save" onclick="insRow('myTable'); loadFile(event)">Save</button>
<input type="button" class="delete" onclick="delRow('myTable')" value="Delete row"></br></br></br></br></br></br>
<div>
<table border="1" class="tablee" style="height: 20px;" id="myTable">
<th width="50px">Nr</th>
<th width="200px" border="2">Image</th>
<th width="200px">Kodi</th>
<th width="100px">Shtesë</th></br>
</table>
</div>

Generate different text in every tile javascript

I have arrays of strings with names and surnames. I created username which consist of random name and random surname and I want to display different username in every generated tile. I tried to splite names and surnames and than add it do array and get random array index but it didn't work. Better solution would be to use my "username". Biggest problem is that I can't modify my getData() function - it always assign the same username to each tile.
function createGrid(x, y) {
for (var cols = 0; cols < x; cols++) {
for (var rows = 0; rows < y; rows++) {
console.log(namesArr)
console.log(x*y)
numberOfTiles = x*y;
var randonIndex = Math.floor(Math.random() * numberOfTiles);
// $(".usernameSpace").html(namesArr[randomIndex]);
//Doesn't work here
$('#container').append("<div class='grid'><div class = 'usernameSpace'></div></div>");
};
};
$('.grid').width(800 / x);
$('.grid').height(800 / x);
};
function refreshGrid() {
var x = $("#colsNumber")[0].value;
var y = $("#rowsNumber")[0].value;
$('.grid').remove();
createGrid(x, y);
};
function getData(count) {
var names = ["Michal ", "Jan ", "Katarzyna ", "Andrzej ", "Jozef ", "Bartek ", "Mikolaj ", "Tomasz ", "Julian ", "Brajan ", "Dzesika "];
var surnames = ["Noga ", "Kowalski ", "Nowak ", "Pazura ", "Duda ", "Komorowski ", "Tomczyk ", "Jozefowicz ", "Lechicki ", "Goldberg "];
result = [];
for (var i = 0; i < count; i++) {
var randomNameIndex = Math.floor(Math.random() * names.length);
var randomSurnameIndex = Math.floor(Math.random() * surnames.length);
var name = names[randomNameIndex];
var surname = surnames[randomSurnameIndex];
result.push({
name: name,
surname: surname
});
}
return result
}
function textDisplay() {
var numberOfTiles = 12;
//for (i = 0; i <= numberOfTiles; i++) {
var data = getData(12);
//var username = "";
////////////////////////////////////////////////////////////////////
username = "";
$.each(data, function (i, { name, surname }) {
username += ` ${name} ${surname}`;
});
////////////////////////////////////////////////////////////////////
// }
namesToDisplay = "";
surnamesToDisplay = "";
$.each(data, function (i, { name }) {
namesToDisplay += `${name}`;
});
$.each(data, function (i, { surname }) {
surnamesToDisplay += `${surname}`;
});
console.log(username)
console.log(namesToDisplay)
console.log(surnamesToDisplay)
//$(".usernameSpace").html(username);
namesArr = namesToDisplay.split(" ");
console.log(namesArr)
$(".usernameSpace").html(namesArr[2]);
}
function AssignUsername(Class, content) {
var container = document.getElementsByClassName(Class);
$(container).html(content);
}
$(document).ready(function () {
$(".startBtn").click(function () {
refreshGrid();
textDisplay();
});
});
#container {
position: relative;
margin:auto;
height:800px;
width:800px;
}
.grid{
outline:5px solid white;
margin:0;
padding:0;
border:none;
background-color: #212121;
display:inline-block;
color: white;
}
.input{
width: 15%;
background-color: #757575;
font-size: 18px;
border: 3px;
height: 4%;
border-radius: 5px;
color: white;
}
html {
font-family: 'Arial';
}
.startBtn{
background-color: #4b4b4b;
border-radius: 10px;
color: white;
}
#textDisplay{
height:800px;
width:800px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<head>
<link rel=stylesheet type="text/css" href="MetroUI 03'2018 basic.css">
<script src="jquery.js"></script>
<script src="MetroUI 03'2018 basic.js"></script>
</head>
<body onload="textDisplay()">
<p>
Give me width of grid:
</p>
<div>
<input class='input' id='colsNumber' type='number'>
</div>
<p>
Give me height of grid:
</p>
<div>
<input class='input' id='rowsNumber' type='number'>
</div>
<button class="startBtn">START!</button>
<div class='nameDisplay'></div>
<div class="Container"></div>
<div id = "container"></div>
</body>
</html>
In your code when you do:
$(".usernameSpace").html(namesArr[2]);
You are assigning all titles with that class to the same HTML values. Instead iterate through your titles and assign it a value based on your data variable:
$(".usernameSpace").each(function(idx){
$(this).html(data[idx].surname);
});
Although I'd recommend storing the names directly in the data attribute of the .usernameSpace elements as you create them. Then you could just use $(this).data("surname") when you iterate through your elements. That way will be more safer as well in the event that data and the number of .usernameSpace elements differs, along with that it will make the code simpler.
Here is a fiddle example of what I described above
function createGrid(x, y) {
for (var cols = 0; cols < x; cols++) {
for (var rows = 0; rows < y; rows++) {
console.log(namesArr)
console.log(x*y)
numberOfTiles = x*y;
var randonIndex = Math.floor(Math.random() * numberOfTiles);
// $(".usernameSpace").html(namesArr[randomIndex]);
//Doesn't work here
$('#container').append("<div class='grid'><div class = 'usernameSpace'></div></div>");
};
};
$('.grid').width(800 / x);
$('.grid').height(800 / x);
};
function refreshGrid() {
var x = $("#colsNumber")[0].value;
var y = $("#rowsNumber")[0].value;
$('.grid').remove();
createGrid(x, y);
};
function getData(count) {
var names = ["Michal ", "Jan ", "Katarzyna ", "Andrzej ", "Jozef ", "Bartek ", "Mikolaj ", "Tomasz ", "Julian ", "Brajan ", "Dzesika "];
var surnames = ["Noga ", "Kowalski ", "Nowak ", "Pazura ", "Duda ", "Komorowski ", "Tomczyk ", "Jozefowicz ", "Lechicki ", "Goldberg "];
result = [];
for (var i = 0; i < count; i++) {
var randomNameIndex = Math.floor(Math.random() * names.length);
var randomSurnameIndex = Math.floor(Math.random() * surnames.length);
var name = names[randomNameIndex];
var surname = surnames[randomSurnameIndex];
result.push({
name: name,
surname: surname
});
}
return result
}
function textDisplay() {
var numberOfTiles = 12;
//for (i = 0; i <= numberOfTiles; i++) {
var data = getData(12);
//var username = "";
////////////////////////////////////////////////////////////////////
username = [];
$.each(data, function (i, { name, surname }) {
username.push(` ${name} ${surname}`);
});
////////////////////////////////////////////////////////////////////
// }
namesToDisplay = "";
surnamesToDisplay = "";
$.each(data, function (i, { name }) {
namesToDisplay += `${name}`;
});
$.each(data, function (i, { surname }) {
surnamesToDisplay += `${surname}`;
});
console.log(username)
console.log(namesToDisplay)
console.log(surnamesToDisplay)
//$(".usernameSpace").html(username);
namesArr = namesToDisplay.split(" ");
console.log(namesArr)
$(".usernameSpace").each(function(idx){
$(this).html(username[idx]);
})
}
function AssignUsername(Class, content) {
var container = document.getElementsByClassName(Class);
$(container).html(content);
}
$(document).ready(function () {
$(".startBtn").click(function () {
refreshGrid();
textDisplay();
});
});
#container {
position: relative;
margin:auto;
height:800px;
width:800px;
}
.grid{
outline:5px solid white;
margin:0;
padding:0;
border:none;
background-color: #212121;
display:inline-block;
color: white;
}
.input{
width: 15%;
background-color: #757575;
font-size: 18px;
border: 3px;
height: 4%;
border-radius: 5px;
color: white;
}
html {
font-family: 'Arial';
}
.startBtn{
background-color: #4b4b4b;
border-radius: 10px;
color: white;
}
#textDisplay{
height:800px;
width:800px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<head>
<link rel=stylesheet type="text/css" href="MetroUI 03'2018 basic.css">
<script src="jquery.js"></script>
<script src="MetroUI 03'2018 basic.js"></script>
</head>
<body onload="textDisplay()">
<p>
Give me width of grid:
</p>
<div>
<input class='input' id='colsNumber' type='number'>
</div>
<p>
Give me height of grid:
</p>
<div>
<input class='input' id='rowsNumber' type='number'>
</div>
<button class="startBtn">START!</button>
<div class='nameDisplay'></div>
<div class="Container"></div>
<div id = "container"></div>
</body>
</html>

Filter element if checkboxes are check or not JavaScript

I got a problem with my code. I coded a search bar which filter checkboxes by their name and I coded a function which only display the checkboxes selected. But there is a conflict.
Everytime I click on "Selected" everything is passing to a display : none;
So I tried to wrap my search bar into some div but it's not working as I imagined. I know that .parent() is the problem because it impacts the parent of my search bar, but I need it to hide the elements not selected so I don't succeed in solving it.
Here is the snippet to understand what I mean. Update it in your answer to let me see what was wrong.
Hope you guys will be able to help me.
function All() {
$("input").parent().show();
}
function OnlySelecteds() {
$("input:not(:checked)").parent().hide();
}
//array of options
var choices = new Array();
choices[0] = "January";
choices[1] = "February";
choices[2] = "March";
choices[3] = "April";
choices[4] = "May";
choices[5] = "June";
choices[6] = "July";
choices[7] = "August";
choices[8] = "September";
choices[9] = "October";
choices[10] = "November";
choices[11] = "December";
var target = new Array()
target[0] = "9";
target[1] = "8";
target[2] = "11";
var cbh = document.getElementById('checkboxes');
var val = '';
var cap = "";
var j = "";
var t = document.getElementById('t');
// the loop is creating the checkboxes with name, value...
for (var i in choices) {
//Name of checkboxes are their number so I convert the i into a string.
j = i.toString();
val = j;
//cap will be the value/text of choices[i]
var cb = document.createElement('input');
var label = document.createElement("label");
cap = choices[i];
var text = document.createTextNode(cap);
cb.type = 'checkbox';
cbh.appendChild(cb);
cb.name = cap;
cb.value = val;
label.appendChild(cb);
label.appendChild(text);
cbh.appendChild(label);
cb.addEventListener('click', updateText)
if (target.indexOf(i) >= 0) {
cb.checked = true;
}
}
updateText();
function updateText() {
t.value = [null, ...document.querySelectorAll('#checkboxes [type="checkbox"]')].reduce((s, el) => el && el.checked ? s = (s || '') + el.value + '$#' : s || '')
}
function updateCheckboxes(x) {
if ($('#SearchBar').val() == '') {
$('#checkboxes > label').show();
} else {
$('#checkboxes > label').hide();
$('#checkboxes > label:contains(' + $('#SearchBar').val() + ')').show();
}
}
* {
box-sizing: border-box;
}
#data {
padding: 5px;
width: 100vw;
}
.multiselect {
overflow: visible;
padding: 0;
padding-left: 1px;
border: none;
width: 100vw;
white-space: normal;
height: 75px;
}
.checkboxes {
height: 100px;
width: 100px;
border: 1px solid #000;
background-color: white;
margin-left: -1px;
display: inline-block;
}
label {
display: inline-block;
border: 1px grey solid;
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<span onclick="All()">All</span> | <span onclick="OnlySelecteds()">Selected</span> | <input id="SearchBar" placeholder="Search for options.." type="text" oninput="updateCheckboxes(this)" autocomplete="off">
<form>
<div id="data">
<div class="multiselect">
<div id="c_b">
<div id="checkboxes">
</div>
</div>
</div>
</div>
</form>
<textarea id="t" style="display: none;"></textarea>
rather than only hide all your unchecked input, I would prefer to show all input first. that way, even after I searched something, then if I click "selected", it would still show all the selected input
function OnlySelecteds() {
$("input").parent().show();
$("input:not(:checked)").parent('label').hide();
}
demo : https://jsfiddle.net/Lzw1xg2n/1/
Change the function OnlySelecteds() to this:
function OnlySelecteds() {
$("input").closest('label').show();
$("input:not(:checked)").closest('label').hide();
}
function All() {
$("input").parent().show();
}
function OnlySelecteds() {
$("input").closest('label').show();
$("input:not(:checked)").closest('label').hide();
}
//array of options
var choices = new Array();
choices[0] = "January";
choices[1] = "February";
choices[2] = "March";
choices[3] = "April";
choices[4] = "May";
choices[5] = "June";
choices[6] = "July";
choices[7] = "August";
choices[8] = "September";
choices[9] = "October";
choices[10] = "November";
choices[11] = "December";
var target = new Array()
target[0] = "9";
target[1] = "8";
target[2] = "11";
var cbh = document.getElementById('checkboxes');
var val = '';
var cap = "";
var j = "";
var t = document.getElementById('t');
// the loop is creating the checkboxes with name, value...
for (var i in choices) {
//Name of checkboxes are their number so I convert the i into a string.
j = i.toString();
val = j;
//cap will be the value/text of choices[i]
var cb = document.createElement('input');
var label = document.createElement("label");
cap = choices[i];
var text = document.createTextNode(cap);
cb.type = 'checkbox';
cbh.appendChild(cb);
cb.name = cap;
cb.value = val;
label.appendChild(cb);
label.appendChild(text);
cbh.appendChild(label);
cb.addEventListener('click', updateText)
if (target.indexOf(i) >= 0) {
cb.checked = true;
}
}
$.expr[":"].contains = $.expr.createPseudo(function(arg) {
return function( elem ) {
return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
updateText();
function updateText() {
t.value = [null, ...document.querySelectorAll('#checkboxes [type="checkbox"]')].reduce((s, el) => el && el.checked ? s = (s || '') + el.value + '$#' : s || '')
}
function updateCheckboxes(x) {
if ($('#SearchBar').val() == '') {
$('#checkboxes > label').show();
} else {
$('#checkboxes > label').hide();
$('#checkboxes > label:contains(' + $('#SearchBar').val() + ')').show();
}
}
* {
box-sizing: border-box;
}
#data {
padding: 5px;
width: 100vw;
}
.multiselect {
overflow: visible;
padding: 0;
padding-left: 1px;
border: none;
width: 100vw;
white-space: normal;
height: 75px;
}
.checkboxes {
height: 100px;
width: 100px;
border: 1px solid #000;
background-color: white;
margin-left: -1px;
display: inline-block;
}
label {
display: inline-block;
border: 1px grey solid;
padding: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<span onclick="All()">All</span> | <span onclick="OnlySelecteds()">Selected</span> | <input id="SearchBar" placeholder="Search for options.." type="text" oninput="updateCheckboxes(this)" autocomplete="off">
<form>
<div id="data">
<div class="multiselect">
<div id="c_b">
<div id="checkboxes">
</div>
</div>
</div>
</div>
</form>
<textarea id="t" style="display: none;"></textarea>

Javascript calculate function not calculating

I have recently been working on a project that has resulted in countless hours of frustration. The task is to create a Webpage that calculates a user's total cost depending on the different radio buttons / check boxes they select.
So, assuming all my other functions and constants are correct, is there anything wrong with my function or the calling of my function.
HTML
<input type = "button" value = "Submit" onclick="calculate();">
<table>
<tr><td>Workshop Total:</td> <td><div id="divWorkshopTotal"></div></td></tr>
<tr><td>Lodging Total:</td> <td><div id="divLodgingTotal"></div></td></tr>
<tr><td>Discount Amount:</td> <td><div id="divDiscount"></div></td></tr>
<tr><td>Sales Tax Amount:</td> <td><div id="divSalesTaxAmount"></div></td></tr>
<tr><td>Total Due:</td> <td><div id="divTotal"></div></td></tr>
</table>
JavaScript
function $(elementName){
return document.getElementById(elementName);
}
function calculate(){
clearOutput();
if (checkWorkshopSelected() > 3){
$("divWorkshopError").innerHTML = "* Selected workshops exceeds maximum of " + MAXIMUM_WORKSHOPS_SELECTED;
return;
} else if (checkWorkshopSelected() == 0){
$("divWorkshopError").innerHTML = "* No workshop(s) selected";
return;
}
var workshopCost = calculateWorkshopTotalCost();
var lodgingCost = calculateLodgingCost();
var subtotal = workshopCost + lodgingCost;
var discountRate = calculateDiscountRate();
var discountAmount = subtotal * discountRate;
if ($("chkTaxExempt").checked == false){
var salesTaxAmount = (subtotal - discountAmount) * SALES_TAX_RATE;
}
var totalCost = subtotal - discountAmount + salesTaxAmount;
$("divWorkshopTotal").innerHTML = workshopCost;
$("divLodgingTotal").innerHTML = lodgingCost;
$("divDiscount").innerHTML = discountAmount;
$("divSalesTaxAmount").innerHTML = salesTaxAmount;
$("divTotal").innerHTML = totalCost;
}
Please check out the code and only ask small doubts in comments. Otherwise please make a new question.
var SALES_TAX_RATE = 0.1,
MAXIMUM_WORKSHOPS_SELECTED = 3;
var data = {
productA: 10,
productB: 20,
productC: 25,
};
var prod = document.getElementsByClassName('prod'),
tax = $("tax"),
workshopTotal = $("workshopTotal"),
lodgingTotal = $("lodgingTotal"),
discount = $("discount"),
salesTaxAmount = $("salesTaxAmount"),
total = $("total"),
workshopError = $("workshopError"),
out = document.getElementsByClassName('out');
$("inputButton").addEventListener("click", calculate);
clearOutput();
function $(elementName){
return document.getElementById(elementName);
}
function calculate () {
clearOutput();
if (checkWorkshopSelected() >= MAXIMUM_WORKSHOPS_SELECTED) {
workshopError.innerHTML = "* Selected workshops exceeds maximum of " + MAXIMUM_WORKSHOPS_SELECTED;
return;
} else if (checkWorkshopSelected() == 0) {
workshopError.innerHTML = "* No workshop(s) selected";
return;
}
var workshopCost = calculateWorkshopTotalCost();
var lodgingCost = calculateLodgingCost();
var subtotal = workshopCost + lodgingCost;
var discountRate = calculateDiscountRate();
var discountAmount = subtotal * discountRate;
var salesTax = 0;
if (tax.checked == false){
salesTax = (subtotal - discountAmount) * SALES_TAX_RATE;
}
var totalCost = subtotal - discountAmount + salesTax;
outputPrice(workshopTotal, workshopCost);
outputPrice(lodgingTotal, lodgingCost);
outputPrice(discount, discountAmount);
outputPrice(salesTaxAmount, salesTax);
outputPrice(total, totalCost);
}
function clearOutput () {
for (var i=0; i<out.length; i++) {
var o = out[i];
o.innerHTML = "0,00"
}
workshopError.innerHTML = "";
}
function checkWorkshopSelected () {
var s = 0;
for (var i=0; i<prod.length; i++) {
var p = prod[i];
if (p.checked) s += 1;
}
return s;
}
function calculateLodgingCost () {
return 5;
}
function calculateWorkshopTotalCost () {
var t = 0;
for (var i=0; i<prod.length; i++) {
var p = prod[i];
if (p.checked) t += data[p.id];
}
return t;
}
function calculateDiscountRate () {
return 0.05;
}
function outputPrice (e, p) {
e.innerHTML = p.toFixed(2);
}
p {
line-height: 14px;
width: 200px;
}
#inputButton {
height: 32px;
margin-bottom: 10px;
}
#workshopError {
color: red;
float: right;
margin-bottom: 20px;
}
.prod + label {
font-weight: bold;
}
.out {
display: inline-block;
padding: 4px 12px;
margin: -6px 0;
background: #fff;
border: 1px solid #999;
border-radius: 3px;
font-family: monospace;
float: right;
}
.out:before {
content: "$";
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<p><input type="checkbox" id="productA" class="prod"><label>Workshop A - 10</label></p>
<p><input type="checkbox" id="productB" class="prod"><label>Workshop B - 20</label></p>
<p><input type="checkbox" id="productC" class="prod"><label>Workshop C - 25</label></p>
<p><input type="checkbox" id="tax"><label>Tax 10%</label></p>
<p><input id="inputButton" type="button" value="Submit"><label id="workshopError"></label></p>
<p><label>Workshop Total:</label><span class="out" id="workshopTotal"></span></p>
<p><label>Lodging Total:</label><span class="out" id="lodgingTotal"></span></p>
<p><label>Discount Amount:</label><span class="out" id="discount"></span></p>
<p><label>Sales Tax Amount:</label><span class="out" id="salesTaxAmount"></span></p>
<p><label>Total Due:</label><span class="out" id="total"></span></p>
</body>
</html>

Categories

Resources