2

I would like to build a progress bar with 3 colors red, yellow, and green. Each color is linked to "not done" for red, "done with difficulties" for yellow, and "successfully done" for green.

I already built my radio button and my progress bar.

I would like that at the begin every red radio button are checked, so the progress bar would be 100% red and 0% both for yellow and green. Then when you check the yellow or green button, the progress bar update and win a percentage in one of those two colors based on the number of exercises.

I know that the problem comes from my function on click, which doesn't fit very well the radio button. As new to coding, my brain is fully melting, and I don't know how to go forward.

Here is my code for the progress bar with the radio button:

function checkRed() {
  const checkBoxes = document.querySelectorAll(".red:checked");
  const progress = document.querySelector(".progress-inner-red");
  const checklistProgressInterval = 100 / checkBoxes.length;
  let width = 0;

  for (let i = 0; i < checkBoxes.length; i++) {
    if (checkBoxes[i].checked) {
      width += checklistProgressInterval;
    }
  }

  progress.style.width = `${width}%`;
}

function checkYellow() {
  const checkBoxes = document.querySelectorAll(".yellow:checked");
  const progress = document.querySelector(".progress-inner-yellow");
  const checklistProgressInterval = 100 / checkBoxes.length;
  let width = 0;

  for (let i = 0; i < checkBoxes.length; i++) {
    if (checkBoxes[i].checked) {
      width += checklistProgressInterval;
    }
  }

  progress.style.width = `${width}%`;

  if (checkBoxes == 0) {
    progress.style.width = `${0}%`;
  }

}

function checkGreen() {
  const checkBoxes = document.querySelectorAll(".turq:checked");
  const progress = document.querySelector(".progress-inner-green");
  const checklistProgressInterval = 100 / checkBoxes.length;
  let width = 0;

  for (let i = 0; i < checkBoxes.length; i++) {
    if (checkBoxes[i].checked) {
      width += checklistProgressInterval;
    }
  }

  progress.style.width = `${width}%`;


}
body {
  margin: 20px 50px;
}

h1 {
  font-size: 1.5em;
}

p {
  margin: 0;
}

input[type="radio"] {
  margin-top: 7px;
  vertical-align: middle;
}

label {
  display: inline-block;
  font: Arial;
}


/* Main Colors
===============================*/

.turq {
  background: #16A085;
}

.turq:checked {
  background: #1ABC9C;
}

.turq:hover {
  background: #1ABC9C;
}

.yellow {
  background: #eeca5a;
}

.yellow:checked {
  background: #ffdc70;
}

.yellow:hover {
  background: #ffdc70;
}

.red {
  background: #C0392B;
}

.red:checked {
  background: #E74C3C;
}

.red:hover {
  background: #E74C3C;
}

.radio {
  position: relative;
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 30px;
  height: 30px;
  margin: 5px;
  margin-top: 0px -webkit-border-radius:100px;
  -moz-border-radius: 100px;
  border-radius: 100px;
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
}

.radio:checked {
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
}

.radio:checked:after {
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
  background: transparent;
}

.radio:after {
  position: absolute;
  top: 5px;
  left: 5px;
  width: 20px;
  height: 20px;
  background: #fff;
  content: "";
  -webkit-border-radius: 100px;
  -moz-border-radius: 100px;
  border-radius: 100px;
}

.progress {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  height: 1rem;
  overflow: hidden;
  font-size: .75rem;
  background-color: #e9ecef;
  border-radius: .25rem;
}


}
.progress-bar {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-orient: vertical;
  -webkit-box-direction: normal;
  -ms-flex-direction: column;
  flex-direction: column;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
  color: #fff;
  text-align: center;
  background-color: #E74C3C;
  transition: width .6s ease;
}
.progress-inner-red {
  background-color: #E74C3C;
  width: 0%;
  height: 100%;
}
.progress-inner-yellow {
  background-color: #ffdc70;
  width: 0%;
  height: 100%;
}
.progress-inner-green {
  background-color: #16A085;
  width: 0%;
  height: 100%;
}
<div class="progress">
  <div class="progress-bar progress-inner-red" role="progressbar" style="width: 100%" aria-valuemin="0" aria-valuemax="100"></div>
  <div class="progress-bar progress-inner-yellow" role="progressbar" style="width: 0%" aria-valuemin="0" aria-valuemax="100"></div>
  <div class="progress-bar progress-inner-green" role="progressbar" style="width: 0%" aria-valuemin="0" aria-valuemax="100"></div>
</div>

<div>
  <input class="radio red" type="radio" name="radio1" id="radio1.1" onclick="checkRed()" checked>
  <input class="radio yellow" type="radio" name="radio1" id="radio2" onclick="checkYellow()">
  <input class="radio turq" type="radio" name="radio1" id="radio3" onclick="checkGreen()">
  <label for "radio3"> Exercise 1<label>
</div>
    
<div>
  <input class="radio red" type="radio" name="radio2" id="radio4" onclick="checkRed()" checked>
  <input class="radio yellow" type= "radio" name="radio2" id="radio5" onclick="checkYellow()">
  <input class="radio turq" type= "radio" name="radio2" id="radio6" onclick="checkGreen()">
    <label for "radio6" display:"inline-block"> Exercise 2 <label>
</div>
lejlun
  • 4,140
  • 2
  • 15
  • 31

2 Answers2

1

My other answer is a modification of your existing code, but I felt like I should show you a completely different answer. That's here:

function changedCheckBox() {
  let amountOfExercises = document.querySelectorAll("label").length;  
  for (var colour of [ 'red', 'yellow', 'green' ]) {
    let amountChecked = document.querySelectorAll("." + colour + ":checked").length;
    let progress = document.querySelector(".progress-inner-" + colour);
    progress.style.width = (amountChecked / amountOfExercises) * 100 + '%';
  }
}
body { margin: 20px 50px; }
h1 { font-size: 1.5em; }
p { margin: 0; }

input[type="radio"] {
  margin-top: 7px;
  vertical-align: middle;
}

label {
      display: inline-block;
      font: Arial;
}    

/* Main Colours
===============================*/
.green {background:#16A085;}
.green:checked {background:#1ABC9C;}
.green:hover {background:#1ABC9C;}

.yellow {background:#eeca5a;}
.yellow:checked {background:#ffdc70;}
.yellow:hover {background:#ffdc70;}

.red {background:#C0392B;}
.red:checked {background:#E74C3C;}
.red:hover {background:#E74C3C;}

.radio {
  position: relative;
  -webkit-appearance: none;
  -moz-appearance: none;
  width: 30px;
  height: 30px;
  margin: 5px;
  margin-top: 0px
  -webkit-border-radius: 100px;
     -moz-border-radius: 100px;
          border-radius: 100px;
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
}

.radio:checked {
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
}

.radio:checked:after {
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
  background: transparent;
}

.radio:after {
  position: absolute;
  top: 5px;
  left: 5px;
  width: 20px;
  height: 20px;
  background: #fff;
  content: "";
  -webkit-border-radius: 100px;
     -moz-border-radius: 100px;
          border-radius: 100px;
}

.progress {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    height: 1rem;
    overflow: hidden;
    font-size: .75rem;
    background-color: #e9ecef;
    border-radius: .25rem;
}}

.progress-bar {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    color: #fff;
    text-align: center;
    background-color: #E74C3C;
    transition: width .6s ease;
    width: 0%;
    height: 100%;
}

.progress-inner-red {
    background-color: #E74C3C;
}

.progress-inner-yellow {
    background-color: #ffdc70;
}

.progress-inner-green {
    background-color: #16A085;
}
<div class="progress">
  <div class="progress-bar progress-inner-red" role="progressbar" style="width: 100%" aria-valuemin="0" aria-valuemax="100"></div>
  <div class="progress-bar progress-inner-yellow" role="progressbar" style="width: 0%" aria-valuemin="0" aria-valuemax="100"></div>
  <div class="progress-bar progress-inner-green" role="progressbar" style="width: 0%" aria-valuemin="0" aria-valuemax="100"></div>
</div>

<div>
  <input class="radio red" type="radio" name="radio1" id="radio1" onchange="changedCheckBox()"checked>
  <input class="radio yellow" type="radio" name="radio1" id="radio2" onchange="changedCheckBox()" >
  <input class="radio green" type="radio" name="radio1" id="radio3" onchange="changedCheckBox()">
  <label for="radio3"> Exercise 1</label>
</div>
    
<div>
  <input class="radio red" type="radio" name="radio2" id="radio4" onchange="changedCheckBox()" checked>
  <input class="radio yellow" type= "radio" name="radio2" id="radio5" onchange="changedCheckBox()">
  <input class="radio green" type= "radio" name="radio2" id="radio6" onchange="changedCheckBox()">
  <label for="radio6"> Exercise 2 </label>
</div>
            
<div>
  <input class="radio red" type="radio" name="radio3" id="radio7" onchange="changedCheckBox()" checked>
  <input class="radio yellow" type= "radio" name="radio3" id="radio8" onchange="changedCheckBox()">
  <input class="radio green" type= "radio" name="radio3" id="radio9" onchange="changedCheckBox()">
  <label for="radio9"> Exercise 3 </label>
</div>

The biggest change is that I've removed the three colour functions and instead made one new function, which determines the amount of exercises and then loops through each colour. For each colour it will determine how many of that colour are checked, then it will set the width of that colour's progress bar accordingly.

This new solution uses a lot less code and consequently it's easier to understand, maintain, fix bugs and add new colours.

Other changes:

  • I've added in an extra exercise. I noticed that the original code doesn't handle that correctly, but this new code does.
  • In the CSS each progress bar had the same width/height applied to it. So I've moved that to the progress bar element. This just reduces the amount of lines.
  • The end tags for the labels were <label> (that's a start tag) so I've changed them to be </label>.
  • The original code had two names for green, so I've unified that to just be green.
  • The labels weren't assigned to an element (the = character was missing), so I've fixed that.
Robson
  • 2,008
  • 2
  • 7
  • 26
0

The code for handling red/yellow/green is working. Each function correctly shows the amount of that colour, when the function is run.

The problem is that when a colour is unchecked(i.e. red) the function for that colour isn't run, so it doesn't get removed from the progress bar.

You can solve this by making two changes.

The first change is to add a function that calls all of the colour functions. That would be this:

function changedCheckBox() {
  checkRed();
  checkYellow();
  checkGreen();
}

The second change is that when you click a checkbox it will run that function, instead of running the colour-specific function. This means that every time a user clicks a checkbox, the correct amount of each colour will be set. To do that you just replace checkGreen() (etc) in the HTML with changedCheckBox().

Here's the result of those two changes:

function checkRed() {
  const checkBoxes = document.querySelectorAll(".red:checked");
  const progress = document.querySelector(".progress-inner-red");
  const checklistProgressInterval = 100 / checkBoxes.length;
  let width = 0;
  
  for(let i = 0; i < checkBoxes.length; i++){
    if(checkBoxes[i].checked){
      width += checklistProgressInterval;
    }
  }
  
    progress.style.width = `${width}%`;
}

function checkYellow() {
  const checkBoxes = document.querySelectorAll(".yellow:checked");
  const progress = document.querySelector(".progress-inner-yellow");
  const checklistProgressInterval = 100 / checkBoxes.length;
  let width = 0;
  
  for(let i = 0; i < checkBoxes.length; i++){
    if(checkBoxes[i].checked){
      width += checklistProgressInterval;
    }
  }
  
    progress.style.width = `${width}%`;
  
  if (checkBoxes==0){
    progress.style.width = `${0}%`;
  }

}

function checkGreen() {
  const checkBoxes = document.querySelectorAll(".turq:checked");
  const progress = document.querySelector(".progress-inner-green");
  const checklistProgressInterval = 100 / checkBoxes.length;
  let width = 0;
  
  for(let i = 0; i < checkBoxes.length; i++){
    if(checkBoxes[i].checked){
      width += checklistProgressInterval;
    }
  }
  
    progress.style.width = `${width}%`;
  
  
}

function changedCheckBox() {
  checkRed();
  checkYellow();
  checkGreen();
}
body { margin: 20px 50px; }
h1 { font-size: 1.5em; }
p { margin: 0; }

input[type="radio"] {
  margin-top: 7px;
  vertical-align: middle;
}

label {
      display: inline-block;
      font: Arial;
    }    

/* Main Colors
===============================*/
.turq {background:#16A085;}
.turq:checked {background:#1ABC9C;}
.turq:hover {background:#1ABC9C;}

.yellow {background:#eeca5a;}
.yellow:checked {background:#ffdc70;}
.yellow:hover {background:#ffdc70;}

.red {background:#C0392B;}
.red:checked {background:#E74C3C;}
.red:hover {background:#E74C3C;}

.radio {
  position:relative;
  -webkit-appearance:none;
  -moz-appearance:none;
  width:30px;
  height:30px;
  margin:5px;
  margin-top:0px
  -webkit-border-radius:100px;
     -moz-border-radius:100px;
          border-radius:100px;
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
}

.radio:checked {
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
}

.radio:checked:after{
  transition: all .3s ease-in-out;
  -moz-transition: all .3s ease-in-out;
  -webkit-transition: all .3s ease-in-out;
  background:transparent;
}

.radio:after {
  position:absolute;
  top:5px;
  left:5px;
  width:20px;
  height:20px;
  background:#fff;
  content:"";
  -webkit-border-radius:100px;
     -moz-border-radius:100px;
          border-radius:100px;
}

.progress{
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    height: 1rem;
    overflow: hidden;
    font-size: .75rem;
    background-color: #e9ecef;
    border-radius: .25rem;
}}

.progress-bar {
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
    -ms-flex-direction: column;
    flex-direction: column;
    -webkit-box-pack: center;
    -ms-flex-pack: center;
    justify-content: center;
    color: #fff;
    text-align: center;
    background-color: #E74C3C;
    transition: width .6s ease;
}

.progress-inner-red{
  background-color: #E74C3C;
  width: 0%;
  height: 100%;
  }

.progress-inner-yellow{
  background-color: #ffdc70;
  width: 0%;
  height: 100%;
  }

.progress-inner-green{
  background-color: #16A085;
  width: 0%;
  height: 100%;
  }
<div class="progress">
  <div class="progress-bar progress-inner-red" role="progressbar" style="width: 100%" aria-valuemin="0" aria-valuemax="100"></div>
  <div class="progress-bar progress-inner-yellow" role="progressbar" style="width: 0%" aria-valuemin="0" aria-valuemax="100"></div>
  <div class="progress-bar progress-inner-green" role="progressbar" style="width: 0%"  aria-valuemin="0" aria-valuemax="100"></div>
</div>

<div>
 <input class="radio red" type="radio" name="radio1" id="radio1.1" onclick="changedCheckBox()"checked>
 <input class="radio yellow" type="radio" name="radio1" id="radio2" onclick="changedCheckBox()" >
 <input class="radio turq" type="radio" name="radio1" id="radio3" onclick="changedCheckBox()">
  <label for "radio3"> Exercise 1<label>
</div>
    
<div>
  <input class="radio red" type="radio" name="radio2" id="radio4" onclick="changedCheckBox()" checked>
  <input class="radio yellow" type= "radio" name="radio2" id="radio5" onclick="changedCheckBox()">
  <input class="radio turq" type= "radio" name="radio2" id="radio6" onclick="changedCheckBox()">
    <label for "radio6" display:"inline-block"> Exercise 2 <label>
</div>
            
      
Robson
  • 2,008
  • 2
  • 7
  • 26
  • 1
    I don't know what to say, as a beginner I'm so happy to see people like you, explaining everything like that was amazing, i learned so much here. I think I'm going to run the shorter function, and yes you are so much right, I need to work on the efficiency of my code and not doing a 2 thousand lines of code ^^ Now I think I will try to do some css to improve the design of the progress bar. Once again, thanks a million. – Prodence King Aug 13 '21 at 13:32
  • You're very welcome! Glad I could help. Definitely don't worry about having lots of code - one of the more enjoyable things about programming is learning all of the ways to achieve things more efficiently. Worth saying as well: your code was 99% of the solution! I barely needed to change anything to get that final 1%. – Robson Aug 13 '21 at 13:57