How to create a dynamic progress bar considering all fields of a form?

Asked

Viewed 11,265 times

12

I’m trying to create a progress bar a little different from the ones I found around. This for example, take the values of the fields and plays in the aria-valuenow from the progress bar.

In my case, I want to take the fields that are being filled and go calculating the percentage and updating the bar in real time.

I was able to do it using just one type of selector, but I can’t use more than one. See with just selects:

$("select").one('change', function() {
        var totalSelect = $('select').length;
        console.log(totalSelect);
        var atual = document.getElementById('progress').getAttribute('aria-valuenow');
        console.log(atual);
        if (atual == 0) {
            var percentual = 100 / totalSelect;
        console.log(percentual);
            $('.progress-bar').css('width', percentual + '%').attr('aria-valuenow', percentual);
        }
        else {
            percentual = (100 / totalSelect) + atual;
           $('.progress-bar').css('width', percentual + '%').attr('aria-valuenow', percentual);
    }
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script><link href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>

<form action="#" method="post">
    <div class="container">
        <br>
        <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-3">
            <label for="select1" class="control-label">
                <select id="select1" class="form-control input-md">
                    <option>1</option>
                    <option>2</option>
                </select>
            </label>
            <br><br>
            <label for="select2" class="control-label">
                <select id="select2" class="form-control input-md">
                    <option>1</option>
                    <option>2</option>
                </select>
            </label>
            <br><br>
            </div>
            </div>
    </div>
</form>
    <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-9">
            <div class="row">
                <div class="col-md-9">
                    <div class="progress progress-striped active">
                    <div class="progress-bar" role="progressbar" aria-valuenow="0" id="progress"
                         aria-valuemin="0" aria-valuemax="100">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

In the FIDDLE.

However, I want to do it with all the main form selectors: input, select, textarea, radio and checkbox.

I tried something like this to put with input also, but it didn’t work:

$("select, input").one('change', 'keypress', function() {
}

But it didn’t happen. I created an example below where I left off, with several fields, and to make it easier for those who want to help, I also created a FIDDLE.

$("select").one('change', function() {
        var totalSelect = $('select').length;
        var totalInput = $('input').length;
        var total = totalInput + totalSelect;
        console.log(total);
        var atual = document.getElementById('progress').getAttribute('aria-valuenow');
        console.log(atual);
        if (atual == 0) {
            var percentual = 100 / total;
        console.log(percentual);
            $('.progress-bar').css('width', percentual + '%').attr('aria-valuenow', percentual);
        }
        else {
            percentual = (100 / total) + atual;
           $('.progress-bar').css('width', percentual + '%').attr('aria-valuenow', percentual);
    }
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script><link href="https://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>


<form action="#" method="post">
    <div class="container">
        <br><br>
        <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-3">
            <label for="select1" class="control-label">
                <select id="select1" class="form-control input-md">
                    <option>1</option>
                    <option>2</option>
                </select>
            </label>
            <br><br>
            <label for="select2" class="control-label">
                <select id="select2" class="form-control input-md">
                    <option>1</option>
                    <option>2</option>
                </select>
            </label>
            <br><br>
            </div>
        <div class="col-md-3">
            <label for="input1" class="control-label">
                <input id="input1" type="number" class="form-control">
            </label>
            <br><br>
            <label for="input2" class="control-label">
                <input id="input2" type="text" class="form-control">
            </label>
            <br><br>
</div>
            <div class="col-md-3">
                <label class="radio radio-inline" for="radioSim">
                    <input type="radio" class="radio" id="radioSim" name="radioName1" value="sim">
                    <b>Sim</b></label>
                <label class="radio radio-inline" for="radioNao">
                    <input type="radio" class="radio" id="radioNao" name="radioName1" value="não">
                    <b>Não</b></label>
                <br><br>
                <label class="radio radio-inline" for="radioSim2">
                    <input type="radio" class="radio" id="radioSim2" name="radioName2" value="sim">
                    <b>Sim</b></label>
                <label class="radio radio-inline" for="radioNao2">
                    <input type="radio" class="radio" id="radioNao2" name="radioName2" value="não">
                    <b>Não</b></label>
                    <br><br>
</div>
</div>
    <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-3">
<label class="checkbox" for="Check1">
    <input class="checkbox" type="checkbox" id="Check1"/>Check1
</label>
<label class="checkbox" for="Check2">
    <input class="checkbox" type="checkbox" id="Check2"/>Check2
</label>
<label class="checkbox" for="Check3">
    <input class="checkbox" type="checkbox" id="Check3"/>Check3
</label>
    <br><br>
<label class="checkbox" for="Check1B">
    <input class="checkbox" type="checkbox" id="Check1B"/>Check1B
</label>
<label class="checkbox" for="Check2B">
    <input class="checkbox" type="checkbox" id="Check2B"/>Check2B
</label>
<label class="checkbox" for="Check3B">
    <input class="checkbox" type="checkbox" id="Check3B"/>Check3B
</label>
</div>
        <div class="col-md-3">
<label class="control-label" for="textArea1">TextArea1
    <textarea class="form-control" id="textArea1" cols="80" rows="3"></textarea>
</label>
<br><br>
<label class="control-label" for="textArea2">TextArea2
    <textarea class="form-control" id="textArea2" cols="80" rows="3"></textarea>
</label>
</div>
        </div>
        <div class="row">
            <div class="col-md-2"></div>
            <div class="col-md-7"></div>
    <label class="control-label">
        <input type="submit" value="Enviar"/>
    </label>
            </div>
    </div>
</form>
    <div class="row">
        <div class="col-md-2"></div>
        <div class="col-md-9">
            <div class="row">
                <div class="col-md-9">
                    <div class="progress progress-striped active">
                    <div class="progress-bar" role="progressbar" aria-valuenow="0" id="progress"
                         aria-valuemin="0" aria-valuemax="100">
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

  • 2

    wouldn’t be something like: $("select, input").one('change keypress', function() {

  • 1

    Ah, that’s right! I had already turned off the note, then I saw by cell phone and thought WTF is not possible... kkkk called back and bingo. Thanks! Now I need to think about some details, because when you choose a radio should be x2 (because you passed two fields), and when you choose in checkbox can be up to more than 2... and it seems that for textarea does not work just put input... tomorrow I’ll see better, thanks for now! @Chun

  • Ah, and for the record, I also had to put the parseInt in the variables...

  • hehehe xD yes, you have to add also the textarea as selector and so on. I was thinking of another way to approach that, instead of pointing to the selects, create a class responsible for that, calculate how many elements exist with this class and add 1 value to each element found in the document to then calculate the total value and display the percentage in the progress bar, but I have not yet mentally constructed how to start developing. And then I’m afraid it might not be quite this way that you want to do things :P

  • Yeah, before bed I thought, "Jeez, just add the textarea!" hehe I think it would work with same class right, would only give more work to include a common class to all... and I do not know very well how it would work, because in this case, I understood, each expected behavior is added in the same sequence, select is with the change, textarea and input stays keypress and keypress etc... I think this way will already solve [continue]

  • But there are some details that I’ll still need help with... then I’ll create a fiddle to help you answer :D... thanks! @Chun

  • So I actually realized that I didn’t even need to be using the keypress, this was causing the increases occur in triples when it was in text or textarea. I just thought change functioned only for the selects ... But it’s coming out, I also modified the count, already dividing the radios for 2, I have just not been able to resolve the issue of checkboxes, because in this case it is possible to choose more than one, but it is not necessary. So I’d like when I pick one, I’m going to advance the total of that group of checkboxes, and if I pick another, I’m not going to advance at all...

  • Here is the updated fiddle: https://jsfiddle.net/t8qzd4c1/4/ E @Chun I await your answer, even if only with the solution you have already posted as a comment, but if you can solve this question of checkboxes I would be grateful double! rsrs And look, if you want to present another solution I would love to see... but I wanted to be able to implement this way if only for the challenge (and I use later your solution hehe)... Thanks!

  • 1

    Yeah, that part of selecting several checkboxes but just count as 1 also do not know how to solve, maybe the more experienced can help you in this. I would even add that comment as a response, but since it would be a response of just a few lines, I don’t think it’s worth posting it as an answer. This solution is better than the one I had in mind. But now I’m also interested to know how that part of checkboxes will work :)

  • @gustavox needs to be an answer using Bootstrap?

  • Not @Renan. It might be without bootstrap... but in this case I think it would be relatively simple to adapt your solution (JS mainly) to Bootstrap, or not? Anyway you don’t need to consider boostrap.

  • So @Renan and that other question is why some fields will be hidden and/or optional, so I thought I would apply a class to these options, and when they were modified the account would modify. Because otherwise, when these fields open the full bar before time...

  • 1

    I’m already going to take a look there @gustavox :)

Show 8 more comments

1 answer

8


Progress

The tag <progress> was introduced in HTML5 precisely to display the progress of a task, i.e., it is not necessary to manipulate the property width of an element. Let’s face it, update the value progress is more semantic than modifying the width of an element, no!?

Although it is rendered with the appearance of the operating system, you can style it with CSS normally, as if it were any other element. I’ve even posted an answer with an example. So the first observation is this: If the document is HTML5, be cool and use the tag progress.

Events

On the events of change, I would use the function on() instead of one() because the second will wait for the event only once, after it is shot the Listener will be removed.

If the user, by naivety (or even by Zuera) decides to modify a field in which the event has already been fired, the value of the progress will continue to mark that the field has already been filled and is OK, even putting an incorrect value.

With the function on() you can do a validation and modify the progress value according to the validity of the fields, incrementing or decreasing the current value.

Elements

Use classes to group elements or even data Attributes, so it is easier to define an event for a particular group (e. g .minha-classe-de-agrupamento) than searching by tags select, input, textarea....

So a way to do:

$(function() {

  var $progress         = $('#progress'), // Barra de Progresso.
      $progressElements = $('.progress'), // Elementos que devem ser checados
                                          // para modificar o valor da barra.
      TOTAL             = $progressElements.length; // Total de elementos.

  
  $progressElements.on('blur, change', function() {
    
    // Faz um filtro com o total elementos válidos.
    // Nesse caso, campos que não estejam "em branco" e que não estejam marcados
    // como ':invalid'.
    var valid = $progressElements.filter(function() {
      return ($(this).val() || $(this).prop('checked')) && !$(this).is(':invalid');
    }).length;
    
    // Calcula a porcentagem e altera o valor da barra.
    var percent = (valid * 100) / TOTAL,
        current = $progress.val();
    
    var increase = percent > current;
        
    var transition = setInterval(function(){
      if((increase && current >= percent) ||
        (!increase && current <= percent))
          clearInterval(transition);
      
      var value = $progress.val();
      value = increase ? value+1 : value-1;
      current = value;
      
      $progress.val(current);
    }, 10);    
  });
});
progress, .progress {
  display: inline-block;
  margin: 4px 0;
  width: 100%
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<progress id='progress' max='100' value='0'></progress>

<form>
  <input class='progress' type='text' placeholder='Nome' />
  <input class='progress' type='email' placeholder='Email'/>
  <textarea class='progress' placeholder='Sobre mim...'></textarea>
  <input class='progress' type='radio' name='foo' value='s' required/>
  <input class='progress' type='checkbox' name='bar' value='n' required/>

  <select class='progress'>
    <option selected disabled>País</option>
    <option>Brasil</option>
    <option>Portugal</option>
  </select>
  <button type='submit'>Registrar</button>
</form>

In case I’ve grouped the elements by class .progress, but as stated earlier you can also use an attribute data-* to group all the elements that will be checked when changing the percentage of progress.

  • Legal @Renan. The problem with the tag <progress> is that it is not supported in older browsers, but it has helped a lot. Maybe I’ll even change it to Progress anyway, and leave the bar only for browsers that support HTML5. In the case of checkboxes and Buttons radios, how would you solve it? The checkbox, as is optional by nature, would have to select one from a group, already count as all having been selected, and then if clicking on another checkbox (from the same group) not change anything... +1

  • You can use the attribute required. I edited the answer, that would be this?

Browser other questions tagged

You are not signed in. Login or sign up in order to post.