Improving repetition in my code (2 examples)

My problem is that when I try to make an app with a lot of buttons, I end up with a ton of global variables, a ton of DOM selectors and an equal amount of click functions that while simillar, I don’t really understand how I could go about making a single function or how to approach it, surelly there has to be a much better way. I’ll post 2 examples, I achieve the result I want, but my code is not pretty, at all:

let count = 0;
const output = document.getElementById("output");
const gameResult = document.getElementById("gameResult");
const numbers = document.querySelectorAll(".each-number");
const numArray = Array.from(numbers);
const binaries = document.querySelectorAll(".binary-number");
const randomizer = document.getElementById("randomizer");
const oneHundredTwentyEight = document.getElementById("128");
const sixtyFour = document.getElementById("64");
const thirtyTwo = document.getElementById("32");
const sixteen = document.getElementById("16");
const eight = document.getElementById("8");
const four = document.getElementById("4");
const two = document.getElementById("2");
const one = document.getElementById("1");

oneHundredTwentyEight.addEventListener("click", function() {
  document.getElementById("binary-128").textContent = "1";
  addMyValue(128);
})
sixtyFour.addEventListener("click", function() {
  document.getElementById("binary-64").textContent = "1";
  addMyValue(64);
})
thirtyTwo.addEventListener("click", function() {
  document.getElementById("binary-32").textContent = "1";
  addMyValue(32);
})
sixteen.addEventListener("click", function() {
  document.getElementById("binary-16").textContent = "1";
  addMyValue(16);
})
eight.addEventListener("click", function() {
  document.getElementById("binary-8").textContent = "1";
  addMyValue(8);
})
four.addEventListener("click", function() {
  document.getElementById("binary-4").textContent = "1";
  addMyValue(4);
})
two.addEventListener("click", function() {
  document.getElementById("binary-2").textContent = "1";
  addMyValue(2);
})
one.addEventListener("click", function() {
  document.getElementById("binary-1").textContent = "1";
  addMyValue(1);
})

for (let i = 0; i < numArray.length; i++) {
  numArray[i].addEventListener("click", function()  {
    this.classList.add("light");
  })
}

function getRandom() {
  return Math.floor(Math.random() * (128 - 1 + 1)) + 1;
}

randomizer.addEventListener("click", () => {
  for (let i = 0; i < numArray.length; i++) {
    numArray[i].classList.remove("light");
  }
  
  for (let i = 0; i < binaries.length; i++) {
    binaries[i].textContent = "0";
  }

  gameResult.textContent = "";
  count = 0;
  output.textContent = getRandom();
})

const addMyValue = (num) => {
  count += num;
  console.log(parseInt(output.textContent));

  if (count > parseInt(output.textContent)) {
    gameResult.textContent = "Wrong value, you went over it."
    count = 0;
    output.textContent = "";
  } else if (count === parseInt(output.textContent)) {
    gameResult.textContent = "You got it right!";
    output.textContent = "";
  }
}

Another example of this:

var outputArr = [];
var firstValue;
var secondValue;
var resetValues;
var totalNumber = document.getElementById("totalNumber");
var buttons = document.getElementsByClassName("col-xs-3");

var one = document.getElementById("one");
var two = document.getElementById("two");
var three = document.getElementById("three");
var four = document.getElementById("four");
var five = document.getElementById("five");
var six = document.getElementById("six");
var seven = document.getElementById("seven");
var eight = document.getElementById("eight");
var Nine = document.getElementById("nine");
var divide = document.getElementById("divide");
var multiply = document.getElementById("multiply");
var subtract = document.getElementById("subtract");
var comma = document.getElementById("comma");
var add = document.getElementById("add");
var equals = document.getElementById("equals");
var C = document.getElementById("C");
var back = document.getElementById("back");

/**************************************************************
EVENTLISTENERS
***********************************************************/
 zero.addEventListener("click", function(){
    getValue(0);
  });
 one.addEventListener("click", function(){
    getValue(1);
  });
 two.addEventListener("click", function(){
    getValue(2);
  })
 three.addEventListener("click", function(){
    getValue(3);
  })
  four.addEventListener("click", function(){
    getValue(4);
  })
 five.addEventListener("click", function(){
    getValue(5);
  })
 six.addEventListener("click", function(){
    getValue(6);
  })
  seven.addEventListener("click", function(){
    getValue(7);
    
  })
 eight.addEventListener("click", function(){
    getValue(8);
  })
 nine.addEventListener("click", function(){
    getValue(9);
  })
 comma.addEventListener("click", function(){
    getValue(".");

   
 /*****************************************************
 OPERANDS AND SPECIAL KEYS
 ****************************************************/
  })
add.addEventListener("click", function(){
   operation("+");
  })
multiply.addEventListener("click", function(){
  operation("*");
  })
subtract.addEventListener("click", function(){
  operation("-");
 })
divide.addEventListener("click", function(){
  operation("/");
 })
equals.addEventListener("click", function(){
  equalResult();
 })
C.addEventListener("click", function(){
  clear();
 })
 back.addEventListener("click", function(){
   backs();
 })



/* Function getValue() pushes the value of each click into the outputArr and displays in the totalNumber(which is the calculator display) the chain of numbers pressed*/
function getValue(value){
  outputArr.push(value);
  totalNumber.innerHTML += value;
}



/*The operation function is triggered by pressing +, -, *, /, it creates a value variable that gets the numbers inside the outputArr and joins them into a string (removing then the commas and creating a single value), we then empty the outputArr, we display the operand sign in the display and store the value in the firstValue global variable.*/ 
function operation(operand){
   var value = outputArr.join("");
   outputArr = [];
   totalNumber.innerHTML = operand;
 return firstValue = Number(value)
}




/* Function clear (C key) simply resets everything */
function clear (){
  totalNumber.innerHTML = " ";
  outputArr = [];
  return firstValue = 0;
}



/* The back function pops the last value we added and displays the outputArr as a joined string */
function backs (){
  outputArr.pop();
  totalNumber.innerHTML = outputArr.join("");
}



/* The equal result function assigns the value of the outputArr into the secondValue var, it then empties the outputArr and then it turns the string stored in secondValue into a number. Depending on the operand that is present in the display it performs one of the if/else possibilities. After that, the result in the display is stored in the outputArr as a number, also in the firstValue global var we store whatever number is in the display. Basically the result of firstValue and secondValue is stored as a firstValue again, so we re-use it. */
function equalResult(){
  var secondValue = outputArr.join("");
  outputArr = [];
  secondValue = Number(secondValue);
  if (totalNumber.innerHTML.indexOf("+") > -1) {
  totalNumber.innerHTML = firstValue + secondValue;
  } else if (totalNumber.innerHTML.indexOf("*") > -1){
  totalNumber.innerHTML = firstValue * secondValue;
  } else if (totalNumber.innerHTML.indexOf("/") > -1){
  totalNumber.innerHTML = firstValue / secondValue;
  } else if (totalNumber.innerHTML.indexOf("-") > -1){
  totalNumber.innerHTML = firstValue - secondValue;
  }
  outputArr.push(Number(totalNumber.innerHTML))
  console.log(outputArr)
  return firstValue = totalNumber.innerHTML;
  }

Any feedback is appreciated.

use an html data attribute to set the value of each element, then the function can be reused for every button.

give every button with the same funciton a class name and set the event listener to be that class instead of each button.

1 Like

btw, I like the binary game! I’d update it with a “learner” mode that tally’s the total as you click the buttons so students can learn binary. Good job with the UI. The code is fine too, just need to try some suggestions like I posted (im sure others will too) to make it more concise.

Good luck

Just yesterday I was re-making the code for this old thing I made:

And I created spans, which I then hid to actually give evey item a “hidden” extra value that I could reference in my script haha, which I guess is the kind of hack people did 10 years ago. Thank you a lot for mentioning data attributes, I heard about it once and I totally forgot about it and it seems to be exactly what I need.

This was a major “gotcha!”

This monster:

[function setPriceValues(){
  for (let i = 0; i < inventory.length; i++) {
    if ( inventory[i].includes("Banana") ) {
       total += 0.20;
    } else if ( inventory[i].includes("Apple") ) {
       total += 0.35;
    } else if ( inventory[i].includes("Cherries") ) {
       total += 1.50;
    } else if ( inventory[i].includes("Grapes") ) {
       total += 1.0;
    } else if ( inventory[i].includes("Peach") ) {
       total += 0.75;
    } else if ( inventory[i].includes("Tomato") ) {
       total += 0.15;
    } else if ( inventory[i].includes("Pineapple") ) {
       total += 3.20;
    } else if ( inventory[i].includes("Strawberries") ) {
       total += 4.50;
    } else if ( inventory[i].includes("Watermelon") ) {
       total += 2.50;
    } else if ( inventory[i].includes("Melon") ) {
       total += 2.0;
    }
   }
  }

Turned into this beauty:

document.getElementById("total").addEventListener("click", function(){
    newTotal = 0;
    for ( let i = 0; i < total.length; i++) {
      newTotal += parseFloat(total[i]); 
    }
     totalPrice.textContent = newTotal.toFixed(2);
  })

All because I could assign every button it’s own value…Thanks again, it will help a lot.

Ouch too much there to think about finding a better way using plain javascript … i use jquery when working with web … so for me

$('button').click(function() {
  
	var clickBut = $(this).val();

if(/[1-9]/.test(clickBut)){
// rest of code
});

all my num and ac and ce etc are buttons … so just need one click listener listening for a button to be clicked …then the value of which button is clicked is assigned to var clickBut … then i use if statements to find out what button was clicked…
So its easy to see this way is way less work than what you are doing.

now i remember before i started using jquery (which was straight after i learnt a bit of javascript … as i started on khan academy … they did javascript then jquery) i briefly tried add eventlisteners like what you are doing but i remember just using one not loads like you are doing … from what i remember i added the event listener to a div surrounding all the divs i wanted to check for events on … also i remember it took a bit of code but less than what you have. So im sure there is a more convenient way of doing it … hopefully some body will help you.
but as soon as i realized how easy using jquery was for click events and stuff i just coded in jquery when i work on html.
If i can locate a file i have using addeventlistener ill post again

Oh it’s definately way easier with JQuery, but latelly I try to use way more vanilla JS, specially with ES6 here and so much “new” stuff to learn. I think you mean something like this, using event delegation (I think it’s the right term):

div.addEventListener("click", (event) => {
  if (event.target.className == "remove") {
  let li = event.target.parentNode;
  li.remove()

Thanks for the example you have shown, that’s interesting too.

Ye thats pretty close to what i ment … found my example from KhanAcademy so added a way simplified version of it
except in mine you add a function call as well … which you create to do things you need done when a click or a mouseover or other event happens

var theParent = document.querySelector("#outer");

theParent.addEventListener("mouseover", doSomething, false);
// so on mouseover it calls a function you write and do what you want it to do


function doSomething(e) {
 
    if (e.target !== e.currentTarget) {

        if (e.target.id === "scroll"){
         //scroll was a div id
         // so do something with the div 
        }
         e.stopPropagation();
  }

I recommend you to learn jQuery from this tutorial, i know you want to avoid jQuery but this guy also teaches DRY coding and changes the way you think. Learn this and after you are comfortable with it start doing things with plain JavaScript

That’s really funny, as I got this message I was watching the modular JS course from the same channel, I love that channel. I’m actually better with JQuery, but now I was kind of going back to vanilla JS because I want to get better at it. I watched some of that course in the past.

What a coincidence :grinning:

I haven`t reviewed any other tutorials from learncodeacademy but i am sure it is great.

Anyway i don`t know your level of understanding jQuery but i recommend you to check this jQuery tutorial to get proper understanding of jQuery (which is simpler than JavaScript), and than implement that knowledge to master JavaScript

Yea, I’ll watch it again, thanks for the help.

Here’s a nice explanation of getting “DRY” when you have a lot of event handlers. Its explanations use vanilla JS; it’s trivial to adapt to jQuery.

1 Like

Very nice tutorial, made it all even clearer, looks like a nice website too.