Home > Software engineering >  How to update progress bar animation onclick?
How to update progress bar animation onclick?

Time:06-27

I am working on a web project and I am trying to create a dynamic form with radio buttons that updates an animated progress-bar.

This is what I have so far: JSFiddle

I have multiple groups of radio buttons that should update the progress-bar onclick and it is working, but the CSS is using set widths rather than just changing it by a certain %, therefore when I click radios from other groups it doesnt animate to-or-from the current %.

For example, if I click option 2a then click option 2b it resets first then goes to the %, and if I click 2b first then click 2a I'd expect the progress-bar to maybe go up by a small %.

You can see when you click option 2a then 1a, its good but its not compatible with multiple groups of radios.

So how can I get it to sum up % amounts rather then it going to-and-from set width amounts?

Also, How do I get the onclick events to work multiple times rather than just once per page refresh?

document.addEventListener('click', option_1_default);
function option_1_default() {
  $(document).ready(function() {
    $("#js1").click(function() {
      $(".progress-value").addClass("progress-value-2a");
    });
  })
};

document.addEventListener('click', option_2_up);
function option_2_up() {
  $(document).ready(function() {
    $("#js2").click(function() {
      $(".progress-value").addClass("progress-value-1a");
    });
  })
};




document.addEventListener('click', option_1b_default);
function option_1b_default() {
  $(document).ready(function() {
    $("#js3").click(function() {
      $(".progress-value").addClass("progress-value-1b");
    });
  })
};

document.addEventListener('click', option_2b_up);
function option_2b_up() {
  $(document).ready(function() {
    $("#js4").click(function() {
      $(".progress-value").addClass("progress-value-2b");
    });
  })
};
body{
  padding:12px;
}

.col-6 label{
  border:1px solid #333;
}
.col-6 input[type=radio]:checked   label{
  border:2px solid blue;
}

.progress {
  background: rgba(255,255,255,0.1);
  justify-content: flex-start;
  border-radius: 100px;
  align-items: center;
  position: relative;
  display: flex;
  height: 10px;
  width: 100%;
  margin-bottom:10px;
}

.progress-value {
  animation: load_speed_default 2s normal forwards;
  box-shadow: 0 10px 40px -10px #fff;
  border-radius: 100px;
  background: #0d6efd;
  height: 30px;
  width: 0;
}
@keyframes load_speed_default {
  0%{width:0;}
  100%{width:50%;}
}




.progress-value-1a {
  animation: load_speed_1a 1s normal forwards;
  box-shadow: 0 10px 40px -10px #fff;
  border-radius: 100px;
  background: #0d6efd;
  height: 30px;
  width: 0;
}
@keyframes load_speed_1a {
  0%{
    width:50%;
    }
  100%{
    width:75%;
  }
}

.progress-value-2a {
  animation: load_speed_2a 1s normal forwards;
  box-shadow: 0 10px 40px -10px #fff;
  border-radius: 100px;
  background: #0d6efd;
  height: 30px;
  width: 0;
}
@keyframes load_speed_2a {
  0%{
    width:75%;
    }
  100%{
    width:50%;
  }
}


.progress-value-1b {
  animation: load_speed_1b 1s normal forwards;
  box-shadow: 0 10px 40px -10px #fff;
  border-radius: 100px;
  background: #0d6efd;
  height: 30px;
  width: 0;
}
@keyframes load_speed_1b {
  0%{
    width:50%;
    }
  100%{
    width:50%;
  }
}

.progress-value-2b {
  animation: load_speed_2b 1s normal forwards;
  box-shadow: 0 10px 40px -10px #fff;
  border-radius: 100px;
  background: #0d6efd;
  height: 30px;
  width: 0;
}
@keyframes load_speed_2b {
  0%{
    width:50%;
    }
  100%{
    width:90%;
  }
}
<head>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
 <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
</head>

<body>



<div >
  Speed
  <div >
    <div ></div>
  </div>
</div>




<div >
<label>Group 1</label>
  <div >
    <input type="radio" style="display:none;" id="js1" data-price="146.99" value="option1a" name="ONE">
    <label for="js1" onclick="option_1_default()">
      Option 1a (Default 50%)
    </label>
  </div>
  <div >
    <input type="radio" style="display:none;" id="js2" data-price="123.99" value="option2a" name="ONE">
    <label for="js2" onclick="option_2_up()">
      Option 2a (75%)
    </label>
  </div>
  
  <hr style="margin-top:24px;">
  
  <label>Group 2</label>
  
  <div >
    <input type="radio" style="display:none;" id="js3" data-price="116.99" value="option1b" name="TWO">
    <label for="js3" onclick="option_1b_default()">
      Option 1b (Default 50%, but if option 2a selected then stay 75%)
    </label>
  </div>
  <div >
    <input type="radio" style="display:none;" id="js4" data-price="93.99" value="option2b" name="TWO">
    <label for="js4" onclick="option_2b_up()">
      Option 2b (Should increase from group 1 selection)
    </label>
  </div>
</div>
</body>

CodePudding user response:

There's several things which need to be addressed in your code.

Firstly your event handlers are using an odd mix of plain JS and jQeury. Stick with one or the other. As you've already included jQuery.js in the page you may as well stick with that. In addition you're usnig both and unobtrusive event handlers and inline event handlers. Avoid the latter as they are outdated and not good practice.

In addition jQuery 1.4.4 is 12.5 years old at this point and should not be used. The latest version is 3.6.0. Use that instead.

Next, you can massively simplify the animation by using transition on the width of the progress bar value instead of having to build a @keyframe animation in CSS for every possibly width combination. The transition will then be applied to any width value you set the progress bar to.

Similary, you can use a single event handler for all the checkboxes, again simplifying the logic and DRYing it up.

With that said, here's a working example. Note that due to the repeated use of the 75% width, not all radios will appear to change the progress bar.

let $progressValue = $('.progress-value');
let $js1 = $('#js1');
let $js2 = $('#js2');
let $js3 = $('#js3');
let $js4 = $('#js4');

$('.radio').on('change', () => {
  var widthPercent = 0;

  if ($js1.prop('checked') || $js3.prop('checked'))
    widthPercent = 50;
    
  if (($js1.prop('checked') && $js3.prop('checked')) || $js2.prop('checked'))
    widthPercent = 75;
    
  if ($js4.prop('checked'))
    widthPercent = 80; // logic isn't clear here, update as necessary
  
  $progressValue.css('width', widthPercent   '%');
});
body {
  padding: 12px;
}

.col-6 label {
  border: 1px solid #333;
}

.col-6 input[type=radio]:checked label {
  border: 2px solid blue;
}

hr {
  margin-top: 24px;
} 

input[type="radio"] {
  display: none;
}

.progress {
  background: rgba(255, 255, 255, 0.1);
  justify-content: flex-start;
  border-radius: 100px;
  align-items: center;
  position: relative;
  display: flex;
  height: 10px;
  width: 100%;
  margin-bottom: 10px;
}

.progress-value {
  box-shadow: 0 10px 40px -10px #fff;
  border-radius: 100px;
  background: #0d6efd;
  height: 30px;
  width: 0;
  transition: width 2s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP JcXn/tWtIaxVXM" crossorigin="anonymous"></script>

<div >
  Speed
  <div >
    <div ></div>
  </div>
</div>

<div >
  <label>Group 1</label>
  <div >
    <input type="radio"  id="js1" data-price="146.99" value="option1a" name="ONE" />
    <label for="js1">Option 1a (Default 50%)</label>
  </div>
  <div >
    <input type="radio"  id="js2" data-price="123.99" value="option2a" name="ONE" />
    <label for="js2">Option 2a (75%)</label>
  </div>

  <hr />

  <label>Group 2</label>
  <div >
    <input type="radio"  id="js3" data-price="116.99" value="option1b" name="TWO" />
    <label for="js3">Option 1b (Default 50%, but if option 2a selected then stay 75%)</label>
  </div>
  <div >
    <input type="radio"  id="js4" data-price="93.99" value="option2b" name="TWO" />
    <label for="js4">Option 2b (Should increase from group 1 selection)</label>
  </div>
</div>

CodePudding user response:

Consider the following example.

Fiddle: https://jsfiddle.net/Twisty/2ud0h9Lq/37/

javaScript

$(function() {
  function updateProgress(perc) {
    $(".progress-value").data("percentage", perc).animate({
      width: perc   '%'
    });
  }

  updateProgress(50);

  $("input[type='radio']").change(function() {
    switch ($(this).attr("id")) {
      case "js1":
        updateProgress(50);
        break;
      case "js2":
        updateProgress(75);
        break;
      case "js3":
        updateProgress(50);
        if ($("#js2").is(":checked")) {
          updateProgress(75);
        }
        break;
      case "js4":
        updateProgress(80);
        break;
    }
  });
});

This makes use of jQuery Animate to show the changes to the progress bar. The overall Logic is not clear, yet this gives you conditional options depending on the other radio buttons.

switch() is similar to if() yet gives you a multitude of conditions. See more:

CodePudding user response:

This much simpler then you are making it. First just use a CSS Transition for the animation (you only need one class). Adjust your inputs by adding data-progress="X" where X is the amount of progress contributed by each selection. Your script just needs to listen for the change event and sum all the checked items' data-progress values and use that to set the width.

$(document).ready(function() {
  const $radios = $('input[type="radio"]')

  $radios.change(function() {
    let progress_value = 0
    $('input[type="radio"]:checked').each(function() {
      progress_value  = Number($(this).data('progress'))
    })
    console.log(progress_value)
    $(".progress-value").width(progress_value   '%')
  });

  // trigger change on one to register any pre-checked values
  $($radios[0]).change()
})
body {
  padding: 12px;
}

.col-6 label {
  border: 1px solid #333;
}

.col-6 input[type=radio]:checked label {
  border: 2px solid blue;
}

.progress {
  background: rgba(255, 255, 255, 0.1);
  justify-content: flex-start;
  border-radius: 100px;
  align-items: center;
  position: relative;
  display: flex;
  height: 10px;
  width: 100%;
  margin-bottom: 10px;
}

.progress-value {
  box-shadow: 0 10px 40px -10px #fff;
  border-radius: 100px;
  background: #0d6efd;
  height: 30px;
  width: 0;
  transition: width 2s;
}
<head>
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
</head>

<body>



  <div >
    Speed
    <div >
      <div ></div>
    </div>
  </div>




  <div >
    <label>Group 1</label>
    <div >
      <input type="radio" style="display:none;" id="js1" data-price="146.99" value="option1a" name="ONE" data-progress="50" checked>
      <label for="js1" onclick="">
      Option 1a (Default 50%)
    </label>
    </div>
    <div >
      <input type="radio" style="display:none;" id="js2" data-price="123.99" value="option2a" name="ONE" data-progress="75">
      <label for="js2" onclick="">
      Option 2a (75%)
    </label>
    </div>

    <hr style="margin-top:24px;">

    <label>Group 2</label>

    <div >
      <input type="radio" style="display:none;" id="js3" data-price="116.99" value="option1b" name="TWO" data-progress="0">
      <label for="js3" onclick="">
      Option 1b (Default 50%, but if option 2a selected then stay 75%)
    </label>
    </div>
    <div >
      <input type="radio" style="display:none;" id="js4" data-price="93.99" value="option2b" name="TWO" data-progress="10">
      <label for="js4" onclick="">
      Option 2b (Should increase from group 1 selection)
    </label>
    </div>
  </div>

  <!-- bootstrap.bundle.min.js belongs down here after all the content, just before the closing body tag -->
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>

  • Related