React: How to force setState()? - javascript

In this React Javascript Calculator this.setState({ array: displayed}); is supposed to update the array in state, but it doesn't. Is there a way to force it? Any help would be greatly appreciated.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';
class JavascriptCalculator extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 0,
array: [],
operators:['+']
}
this.display = this.display.bind(this);
this.clear = this.clear.bind(this);
this.calculate = this.calculate.bind(this);
}
display(text){
// if display is zero, remove the leading zero from the text.
if(this.state.text == 0){
this.state.text = ''
}
let regex = /[*/+-]/;
// if text is not an operator
if (!regex.test(text)){
let displayed = this.state.text
// disallow consecutive decimal points
if (text == '.' && displayed.slice(-1) == '.'){
return;
}
// start by adding text
displayed = this.state.text += text;
// disallow multiple decimal points in a number
// if attempt at more than one decimal point remove last one.
let array = displayed.split('');
let count = 0;
for (let i = 0; i < array.length; i++){
if (array[i] == '.'){
count++;
}
}
// one decimal point is allowed per operator.
// thus to allow the first decimal point,
// this.state.operators must be initialized
// to length of 1.
if(count > this.state.operators.length){
array.pop();
}
displayed = array.join('');
this.setState({ text: displayed});
}
// if text is an operator
if (regex.test(text)){
// add the text to the array
// so that repeated decimal points are prevented
let array = this.state.operators;
array.push(text);
this.setState({ operators: array});
// add the text to the text
let displayed = this.state.text += text;
this.setState({ text: displayed});
}
// if text ends in equals sign, run the calculate function.
if (text == '='){
let displayed = this.state.text.split('');
console.log(displayed);
this.setState({ array: displayed});
//this.state.array = displayed;
this.calculate();
}
}
calculate(){
let regex = /[*/+-]/;
let text = '';
let length = this.state.array.length;
let operators = [];
//console.log(this.state.array);
// capture numbers longer than one digit by adding them to a string
// and adding a comma in place of the operators, so the string
// can be split into an array at the operators.
for (let i = 0; i < length; i++){
// put numbers into a string
if (this.state.array[i].match(/[\d.]/)) {
text+=this.state.array[i];
}
// add commas to string in place of operators
if (this.state.array[i].match(regex)){
text+=',';
// add operators to their own array
operators.push(this.state.array[i]);
}
if (this.state.array[i] == '='){
break;
}
}
//console.log(operators);
// create the numbers array
let numbers = text.split(',');
//console.log(numbers);
// initialize answer with first number
let answer = numbers[0];
let func = undefined;
// Start with second number
for (let i = 1; i < numbers.length; i++){
func = returnFunc(operators.shift());
console.log(func);
answer = func(answer, numbers[i]);
}
this.display(answer);
function returnFunc(val) {
switch (val) {
case '+':
return function sum(a,b) { return Number(a)+Number(b)};
case '-':
return function subtract(a,b) { return Number(a)-Number(b)};
case '*':
return function multiply(a,b) { return Number(a)*Number(b)};
case '/':
return function divide(a,b) { return Number(a)/Number(b)};
default:
throw new Error("Called with unknown operator " + val);
}
}
}
clear(id){
this.setState({ text: id });
this.setState({ array: [] });
this.setState({ operators: [] });
}
render() {
return (
<div id="javascript-calculator">
<h1 id="title">Javascript Calculator</h1>
<div id="display">{this.state.text}</div>
<hr/>
<div>
<button id="clear" onClick={e => this.clear("0")}> clear </button>
<button id="equals" onClick={e => this.display("=")}> = </button>
<button id="zero" onClick={e => this.display("0")}> 0 </button>
<button id="one" onClick={e => this.display("1")}> 1 </button>
<button id="two" onClick={e => this.display("2")}> 2 </button>
<button id="three" onClick={e => this.display("3")}> 3 </button>
<button id="four" onClick={e => this.display("4")}> 4 </button>
<button id="five" onClick={e => this.display("5")}> 5 </button>
<button id="six" onClick={e => this.display("6")}> 6 </button>
<button id="seven" onClick={e => this.display("7")}> 7 </button>
<button id="eight" onClick={e => this.display("8")}> 8 </button>
<button id="nine" onClick={e => this.display("9")}> 9 </button>
<button id="add" onClick={e => this.display("+")}> + </button>
<button id="subtract" onClick={e => this.display("-")}> - </button>
<button id="multiply" onClick={e => this.display("*")}> * </button>
<button id="divide" onClick={e => this.display("/")}> / </button>
<button id="decimal" onClick={e => this.display(".")}> . </button>
</div>
</div>
);
}
}
ReactDOM.render(<JavascriptCalculator />, document.getElementById("app"));
index.html
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Javascript Calculator</title>
<style>
</style>
</head>
<body>
<main>
<div id="app"></app>
</main>
</body>
</html>

setState is async. It won't work how you're using it. In your case, you should just pass the displayed array to the calculate function.

Related

Javascript calculator stringing multiple operators

I'm working on The Odin Project curriculum at the moment, and the calculator is the final project of the foundations. I have a mostly working app, but I just can't seem to be able to chain multiple operators without having to press the equals button in between. To clarify, I should be able to do 5+5+5 etc. At the moment however, it does nothing after the first 5+5. I have been stuck on this for quite a while, and am getting frustrated.
Any help is greatly appreciated.
html:
<!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>Calculator</title>
</head>
<body>
<div class="calculator">
<div class="output">
<div class="previous"></div>
<div class="operator-display"></div>
<div class="current"></div>
</div>
<button class="span-two clear">Clear</button>
<button class="span-two delete">Delete</button>
<button class="number">1</button>
<button class="number">2</button>
<button class="number">3</button>
<button class="operator">+</button>
<button class="number">4</button>
<button class="number">5</button>
<button class="number">6</button>
<button class="operator">-</button>
<button class="number">7</button>
<button class="number">8</button>
<button class="number">9</button>
<button class="operator">*</button>
<button class="decimal">.</button>
<button class="number">0</button>
<button class="equals">=</button>
<button class="operator">/</button>
</div>
</body>
<script src="script.js"></script>
</html>
javascript:
const numberBtns = document.querySelectorAll(".number");
const operationBtns = document.querySelectorAll(".operator");
const equalsBtn = document.querySelector(".equals");
const clearBtn = document.querySelector(".clear");
const deleteBtn = document.querySelector(".delete");
const previous = document.querySelector(".previous");
const current = document.querySelector(".current");
const operatorDisplay = document.querySelector(".operator-display");
let decimalBtn = document.querySelector(".decimal");
let firstNumber = 0;
let secondNumber = 0;
let result;
//function to clear output screen
function clearOutput(){
previous.innerText = "";
current.innerText = "";
operatorDisplay.innerText = "";
}
//calls function to clear screen on a click
clearBtn.addEventListener("click", ()=>{
clearOutput();
})
//add number to screen on click
numberBtns.forEach(button => {
button.addEventListener('click', ()=>{
current.innerText += button.innerText;
})
})
decimalBtn.addEventListener('click', ()=>{
addDecimal();
})
//when pressing an operator button, the current operand is moved to the previous operand //place
operationBtns.forEach(button =>{
button.addEventListener('click', ()=>{
if (operatorDisplay.innerText != "") {
operate();
}
else{
previous.innerText = current.innerText;
firstNumber = previous.innerText;
current.innerText = "";
operatorDisplay.innerText = button.innerText;
}
})
})
//calculates result based on chosen operator
equalsBtn.addEventListener('click', ()=>{
secondNumber = current.innerText;
operate();
//Display error message if user tries to divide by 0
if (secondNumber === "0" && operatorDisplay.innerText === "/") {
errorMessage();
}
//pressing equals button does nothing if either operand is empty
if (current.innerText != "" && previous.innerText != "") {
displayResult();
}
})
//deletes last number of the current operand on click
deleteBtn.addEventListener('click', ()=>{
current.innerText = current.innerText.slice(0, -1);
})
//displays result
function displayResult(){
clearOutput();
//rounds the result in case of a ridiculous number of decimals
current.innerText = Math.round(result * 10000) / 10000;
}
function operate(){
if (operatorDisplay.innerText === "+") {
result = parseFloat(firstNumber) + parseFloat(secondNumber);
}
if (operatorDisplay.innerText === "-") {
result = parseFloat(firstNumber) - parseFloat(secondNumber);
}
if (operatorDisplay.innerText === "*") {
result = parseFloat(firstNumber) * parseFloat(secondNumber);
}
if (operatorDisplay.innerText === "/") {
result = parseFloat(firstNumber) / parseFloat(secondNumber);
}
}
function errorMessage(){
clearOutput();
result = "Division by 0 impossible";
}
//adds decimal point
function addDecimal(){
if (!current.innerText.includes(".")) {
current.innerText += ".";
}
}

React: How to totally and completely clear state

In this React Javascript Calculator, decimal points are able to be added to numbers, but only after a complete browser refresh, and not after clicking the clear button which triggers the clear() function. How can state be completely and totally reset in the clear() function so that state becomes as if a browser refresh had been done. Any help would be greatly appreciated.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './style.css';
const initState = {
text: '0',
operators:['+']
}
class JavascriptCalculator extends React.Component {
constructor(props) {
super(props);
this.state = initState;
this.display = this.display.bind(this);
this.clear = this.clear.bind(this);
this.calculate = this.calculate.bind(this);
}
display(text){
// if display is zero, remove the leading zero from the text.
if(this.state.text == 0){
this.state.text = '';
}
let regex = /[*/+-]/;
// if text is not an operator
if (!regex.test(text)){
let displayed = this.state.text
// start by adding text
displayed += text;
// disallow multiple decimal points in a number
// if attempt at more than one decimal point remove last one.
let array = displayed.split('');
let count = 0;
for (let i = 0; i < array.length; i++){
if (array[i] === '.'){
count++;
}
}
// one decimal point is allowed per operator.
// thus to allow the first decimal point,
// this.state.operators must be initialized
// to length of 1.
if(count > this.state.operators.length){
array.pop();
}
displayed = array.join('');
this.setState({ text: displayed});
}
// if text is an operator
if (regex.test(text)){
// add the text to the array
// so that repeated decimal points are prevented
let array = this.state.operators;
array.push(text);
this.setState({ operators: array});
let displayed = this.state.text
displayed += text;
this.setState({ text: displayed});
}
// if text is equals sign, run the calculate function.
if (text === '='){
let displayed = this.state.text.split('');
displayed.push('=');
console.log(displayed);
this.calculate(displayed);
}
}
calculate(displayed){
let regex = /[*/+-]/;
let text = '';
let length = displayed.length;
let operators = [];
//console.log(this.state.array);
// capture numbers longer than one digit by adding them to a string
// and adding a comma in place of the operators, so the string
// can be split into an array at the operators.
for (let i = 0; i < length; i++){
// put numbers into a string
if (displayed[i].match(/[\d.]/)) {
text+=displayed[i];
}
// add commas to string in place of operators
if (displayed[i].match(regex)){
text+=',';
// add operators to their own array
operators.push(displayed[i]);
}
if (displayed[i] === '='){
break;
}
}
//console.log(operators);
// create the numbers array
let numbers = text.split(',');
//console.log(numbers);
// initialize answer with first number
let answer = numbers[0];
let func = undefined;
// Start with second number
for (let i = 1; i < numbers.length; i++){
func = returnFunc(operators.shift());
answer = func(answer, numbers[i]);
this.setState({text: answer})
}
function returnFunc(val) {
switch (val) {
case '+':
return function sum(a,b) { return Number(a)+Number(b)};
case '-':
return function subtract(a,b) { return Number(a)-Number(b)};
case '*':
return function multiply(a,b) { return Number(a)*Number(b)};
case '/':
return function divide(a,b) { return Number(a)/Number(b)};
default:
throw new Error("Called with unknown operator " + val);
}
}
}
clear(){
this.setState({text:'0', operators:'+'});
}
render() {
return (
<div id="javascript-calculator">
<h1 id="title">Javascript Calculator</h1>
<div id="display">{this.state.text}</div>
<hr/>
<div>
<button id="clear" onClick={e => this.clear()}> clear </button>
<button id="equals" onClick={e => this.display("=")}> = </button>
<button id="zero" onClick={e => this.display("0")}> 0 </button>
<button id="one" onClick={e => this.display("1")}> 1 </button>
<button id="two" onClick={e => this.display("2")}> 2 </button>
<button id="three" onClick={e => this.display("3")}> 3 </button>
<button id="four" onClick={e => this.display("4")}> 4 </button>
<button id="five" onClick={e => this.display("5")}> 5 </button>
<button id="six" onClick={e => this.display("6")}> 6 </button>
<button id="seven" onClick={e => this.display("7")}> 7 </button>
<button id="eight" onClick={e => this.display("8")}> 8 </button>
<button id="nine" onClick={e => this.display("9")}> 9 </button>
<button id="add" onClick={e => this.display("+")}> + </button>
<button id="subtract" onClick={e => this.display("-")}> - </button>
<button id="multiply" onClick={e => this.display("*")}> * </button>
<button id="divide" onClick={e => this.display("/")}> / </button>
<button id="decimal" onClick={e => this.display(".")}> . </button>
</div>
</div>
);
}
}
ReactDOM.render(<JavascriptCalculator />, document.getElementById("app"));
index.js
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Javascript Calculator</title>
<style>
</style>
</head>
<body>
<main>
<div id="app"></app>
</main>
</body>
</html>
Create a variable containing the initial state, then use it to replace the current state when you want to clear, like the below example:
const initState = {
text: '0',
array: [],
operators:['+']
}
...
constructor(props) {
super(props);
this.state = initState;
this.display = this.display.bind(this);
this.clear = this.clear.bind(this);
this.calculate = this.calculate.bind(this);
}
...
clear(){
this.setState(initState);
}

How to properly link basic calculator results to buttons? (failing to do so)

I'm new to javascript, this week was our second lesson that came with an assignment. Disclaimer: I have a learning disability so sorry ifgot the solution is really obvious.
Alright so in the first lesson we have to create a basic calculator with input fields, this went great, followed along with the teacher and thought I understood it. - Second lesson was making the calculator work with buttons instead.
This is what I came up with:
function insert(num) {
document.form.textview.value = document.form.textview.value + num
}
var result = document.getElementById('result');
result.addEventListener('click', function(number1, number2) {
var a = number1;
var b = number2;
var op = (document.querySelector("#operator").value);
var calculate;
if (op == "add") {
calculate = a + b;
} else if (op == "min") {
calculate = a - b;
} else if (op == "divi") {
calculate = a / b;
} else if (op == "mul") {
calculate = a * b;
}
});
<form name="form">
<input type="text" name="textview">
</form>
<div id="operator">
<br>
<button onclick="insert(7)">7</button>
<button onclick="insert(8)">8</button>
<button onclick="insert(9)">9</button>
<button onclick="insert('/')" id="divi"> / </button>
<br>
<button onclick="insert(4)">4</button>
<button onclick="insert(5)">5</button>
<button onclick="insert(6)">6</button>
<button onclick="insert('*')" id="mul"> * </button>
<br>
<button onclick="insert(1)">1</button>
<button onclick="insert(2)">2</button>
<button onclick="insert(3)">3</button>
<button onclick="insert('-')" id="min"> - </button>
<br>
<button onclick="insert('C')"> C </button>
<button onclick="insert(0)"> 0 </button>
<button onclick="insert('')" id="result"> = </button>
<button onclick="insert('+')" id="add"> + </button>
</div>
Result: All buttons work and input the numbers but it seems to not connect with my function.
First I checked console log for errors but it doesn't give any. Then I asked my teachers and he said we're only allowed to use what we learned in the first 2 lessons, so i'm trying to stick with it but really have no clue what I did wrong here. Any help would be appreciated very muchthanks all!
Here is a sample code that can solve your problem. What is happening in this is.
Whenever you select anything from the keys check if its not a number using isNaN()
If its is not a number the add a space after and before it when inserting into the textbox
document.form.textview.value = document.form.textview.value + ' ' + num + ' ';
When the result button (=) is clicked get the value of the textbox
var operands = equation.split(' ');
Now you will have an array (operands) of 3 items which will have first item as the first number, second item as the operation and the third item as the second number.
Now you can operate accordingly. Below is a working snippet for the above solutions.
function insert(num) {
if (isNaN(num)) {
document.form.textview.value = document.form.textview.value + ' ' + num + ' '; // add a space after and before the operator
} else {
document.form.textview.value = document.form.textview.value + num;
}
}
var result = document.getElementById('result');
result.addEventListener('click', function() {
var equation = document.getElementById('equation').value;
var operands = equation.split(' '); // split the string by space which give you and array of 3 elements
var op = operands[1];
var a = operands[0];
var b = operands[2];
var calculate;
if (op == "+") {
calculate = a + b;
} else if (op == "-") {
calculate = a - b;
} else if (op == "/") {
calculate = a / b;
} else if (op == "*") {
calculate = a * b;
}
console.log('Result is: ' + calculate);
});
<form name="form">
<input type="text" id="equation" name="textview">
</form>
<div id="operator">
<br>
<button onclick="insert(7)">7</button>
<button onclick="insert(8)">8</button>
<button onclick="insert(9)">9</button>
<button onclick="insert('/')" id="divi"> / </button>
<br>
<button onclick="insert(4)">4</button>
<button onclick="insert(5)">5</button>
<button onclick="insert(6)">6</button>
<button onclick="insert('*')" id="mul"> * </button>
<br>
<button onclick="insert(1)">1</button>
<button onclick="insert(2)">2</button>
<button onclick="insert(3)">3</button>
<button onclick="insert('-')" id="min"> - </button>
<br>
<button onclick="insert('C')"> C </button>
<button onclick="insert(0)"> 0 </button>
<button onclick="insert('')" id="result"> = </button>
<button onclick="insert('+')" id="add"> + </button>
</div>
Read more about
.split()
isNaN()
There can be many more solutions to this using different ways. Try and explore. Good luck with your learning.
Hope this helps :)

How to add attribute name and attribute value dynamically in React

I want to set value="a" depending on the condition it meets. Button has no value attribute currently. How do I write this?I want like this <button value="a"> One </button>
const buttonValues = ["a", "b"]
const addingValuesToButtons = () => {
for (var i = 0; i < buttonValues.length; i++) {
if(buttonValues[i] === "a") {
//add attribute name and value to first two buttons
}
if(buttonValues[i] === "b"){
//add attribute name and value to first three buttons
}
};
return(
<div>
<button> One </button>
<button> Two </button>
<button> Three </button>
<button> Four </button>
</div>
)
}
const buttonValues = ["a", "b"]
const addingValuesToButtons = () => {
const buttons = [];
for (var i = 0; i < buttonValues.length; i++) {
if(buttonValues[i] === "a") {
buttons.push({attr: 'foo', name: 'bar'});
}
if(buttonValues[i] === "b"){
buttons.push({attr: 'baz', name: 'bar2'})
}
};
return(
<div>
{buttons.map(button => {
return <button attr={button.attr}>{button.name}</button>;
})}
</div>
)
}
It will go like this:
<button value={this.buttonValues[0] == 'a'?this.buttonValues[0]:null}> One </button>
another netter approach is :
const buttonValues = [{value:"a",name:"One"}, {value:"b",name:"two"}]
this.buttonValues.map(value => {<button value={value.value == 'a'? value.value:null}>{value.name}</button>}) ;

How to beat a JavaScript condition riddle?

I am using a foreach loop in php to load data from a mysql table. I'm using the data ID's loaded from the data base and applying it to the button values.
The buttons come in two colors, green and white. The buttons represent likes for liking comments or posts.
The total existing number of likes starts at 6 (div id="total")
white buttons
If button 1 has color of white and you click it, total likes (6) will increase by 1. If you click button 1 again, total likes (7) will decrease by 1.
If button 1, button 2, and button three are clicked, total likes (6) increases by 3 ( 1 for each button). If button 1, button 2 and button 3 are clicked again, the total likes (9) will decrease by 3.
The Puzzle
Green buttons
How do I make it so, When a green button is clicked, the total (6) decrease by 1, and if the button is clicked again, it should increase by 1. Unlike white buttons.
If Green button 3, 5 and 6 are clicked, the total (6) should decease by 3. if the same buttons are clicked again, total (6) increases by 3.
Here is my code
var colorcode = "rgb(116, 204, 49)";
var buttonid = str;
var elem = document.getElementById(buttonid);
var theCSSprop = window.getComputedStyle(elem, null).getPropertyValue("background-color");
var initialtotal = parseInt(document.getElementById("total").innerHTML, 10);
var likes = new Array();
function showUser(str) {
////// 1st condition /////
if (theCSSprop == colorcode) {
if (likes[value] == 0 || !likes[value]) {
likes[value] = 1;
} else {
likes[value] = 0;
}
var sum = 0;
for (i = 0; i < likes.length; i++) {
if (likes[i] == 1) {
sum--
}
}
}
////// 2nd condition /////
else {
if (likes[str] == 0 || !likes[str]) {
likes[str] = 1;
} else {
likes[str] = 0;
}
var sum = 0;
for (i = 0; i < likes.length; i++) {
if (likes[i] == 1) {
sum++
}
}
}
var tot = initialtotal + sum;
document.getElementById("total").innerHTML = tot;
}
<div id="total" style="width:100px;padding:50px 0px; background-color:whitesmoke;text-align:center;">6 </div>
<!---------------------------------------------------------------------------------------------------------------------->
<button id="5" value="5" onclick="showUser(this.value)">LIKE </button>
<button id="346" value="346" onclick="showUser(this.value)" style="background-color:rgb(116, 204, 49);">LIKE </button>
<button id="128" value="128" onclick="showUser(this.value)" style="background-color:rgb(116, 204, 49);">LIKE </button>
<button id="687" value="687" onclick="showUser(this.value)">LIKE </button>
<button id="183" value="183" onclick="showUser(this.value)" style="background-color:rgb(116, 204, 49);">LIKE </button>
<button id="555" value="555" onclick="showUser(this.value)">LIKE </button>
<!---------------------------------------------------------------------------------------------------------------------->
Instead of passing this.value to showUser(), just pass this. That way, the function can get the value and the style directly, without having to call getElementById() (you're not passing the ID). Then you need to set theCSSprop inside the function, so it's the property of the current button.
To make green buttons alternate direction from increment to decrement, you need a global variable that remembers what it did the last time the function was called.
Also, you don't need to write if(likes[str] == 0 || !likes[str]), since 0 is faley. Just write if(!likes[str]).
var colorcode = "rgb(116, 204, 49)";
var likes = new Array();
var greenIncr = -1;
function showUser(elem) {
var initialtotal = parseInt(document.getElementById("total").innerHTML, 10);
////// 1st condition /////
var str = elem.value;
var theCSSprop = window.getComputedStyle(elem, null).getPropertyValue("background-color");
if (theCSSprop == colorcode) {
if (!likes[str]) {
likes[str] = 1;
} else {
likes[str] = 0;
}
var sum = 0;
for (i = 0; i < likes.length; i++) {
if (likes[i] == 1) {
sum += greenIncr;
}
}
greenIncr = -greenIncr; // revese the direction of green button
}
////// 2nd condition /////
else {
if (!likes[str]) {
likes[str] = 1;
} else {
likes[str] = 0;
}
var sum = 0;
for (i = 0; i < likes.length; i++) {
if (likes[i] == 1) {
sum++
}
}
}
var tot = initialtotal + sum;
document.getElementById("total").innerHTML = tot;
}
<div id="total" style="width:100px;padding:50px 0px; background-color:whitesmoke;text-align:center;">6 </div>
<!---------------------------------------------------------------------------------------------------------------------->
<button id="5" value="5" onclick="showUser(this)">LIKE </button>
<button id="346" value="346" onclick="showUser(this)" style="background-color:rgb(116, 204, 49);">LIKE </button>
<button id="128" value="128" onclick="showUser(this)" style="background-color:rgb(116, 204, 49);">LIKE </button>
<button id="687" value="687" onclick="showUser(this)">LIKE </button>
<button id="183" value="183" onclick="showUser(this)" style="background-color:rgb(116, 204, 49);">LIKE </button>
<button id="555" value="555" onclick="showUser(this)">LIKE </button>
<!---------------------------------------------------------------------------------------------------------------------->
First naive implementation can look like this
class Counter {
constructor(initial) {
this.initial = initial
this.white = [false, false, false]
this.green = [false, false, false]
}
changeGreen(index) {
this.green[index] = !this.green[index]
}
changeWhite(index) {
this.white[index] = !this.white[index]
}
get total() {
return this.initial + this.white.reduce((total, current) => total + current, 0) + this.green.reduce((total, current) => total - current, 0)
}
}
let counter = new Counter(6)
const render = counter => {
document.querySelector('#total').innerHTML = counter.total
}
render(counter)
;['#first', '#second', '#third'].map((selector, index) => {
document.querySelector(selector).addEventListener('click', e => {
e.target.classList.toggle('pressed')
counter.changeWhite(index)
render(counter)
})
})
;['#fourth', '#fifth', '#sixth'].map((selector, index) => {
document.querySelector(selector).addEventListener('click', e => {
e.target.classList.toggle('pressed')
counter.changeGreen(index)
render(counter)
})
})
.green {
background: #00aa00
}
.pressed {
border-style: inset
}
<div id="total">0</div>
<p>
<button id="first">First</button>
<button id="second">Second</button>
<button id="third">Third</button>
<button id="fourth" class="green">Fourth</button>
<button id="fifth" class="green">Fifth</button>
<button id="sixth" class="green">Sixth</button>
</p>
But after all I've finished with something like
class Counter {
constructor(initial, strategy) {
this.initial = initial;
this.elements = [];
this.strategy = typeof strategy === 'function' ? strategy : () => {}
}
addElement(content, type, next) {
const element = {
content: content,
type: type,
state: false
};
this.elements.push(element);
return next(element, this.elements.length - 1);
}
toggleElementState(index) {
this.elements[index].state = !this.elements[index].state
}
get total() {
return this.strategy(this.initial, this.elements)
}
}
const initialize = () => {
Counter.WHITE = Symbol('white');
Counter.GREEN = Symbol('green');
const counter = new Counter(6, (initial, buttons) => {
return initial +
buttons.filter(button => button.type === Counter.WHITE).reduce((total, current) => total + Number(current.state), 0) +
buttons.filter(button => button.type === Counter.GREEN).reduce((total, current) => total - Number(current.state), 0)
});
const render = counter => {
document.querySelector('#total').innerHTML = counter.total
};
const createButton = (element, index) => {
const button = document.createElement('button');
button.setAttribute('data-id', index);
button.classList.add(element.type === Counter.GREEN ? 'green' : 'none');
button.textContent = element.content;
document.querySelector('#buttons').appendChild(button)
};
const addButton = (type, ...selectors) => {
selectors.forEach(selector => counter.addElement(selector, type, createButton));
};
render(counter);
addButton(Counter.WHITE, '#first', '#second', '#third');
addButton(Counter.GREEN, '#fourth', '#fifth', '#sixth');
addButton(Counter.WHITE, '#first', '#second', '#third');
document.querySelector('#buttons').addEventListener('click', function(e) {
e.target.classList.toggle('pressed');
counter.toggleElementState(parseInt(e.target.dataset.id));
render(counter)
})
};
document.addEventListener('DOMContentLoaded', initialize);
.green {
background: #00aa00
}
.pressed {
border-style: inset
}
<div id="total">0</div>
<p id="buttons">
</p>

Categories

Resources