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

12

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

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

I was able to do this using only one type of selector, but I can not use more than one. See with only 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><scriptsrc="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>

No FIDDLE .

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

I tried something like this to put it with input too, but it did not work:

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

But it did not roll. I created an example below where I stopped, 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><scriptsrc="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>
    
asked by anonymous 17.12.2015 / 05:00

1 answer

7

Progress

The tag <progress> was introduced in HTML5 just to show the progress of a task, ie it is not necessary to manipulate the width property of an element. Convince, updating the value of 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, just like any other element. I've even posted a response with an example . So the first remark is this: If the document is HTML5, be cool and use the tag progress .

Events

On change events, I would use the on() function instead of < the second will wait for the event only once, after the event has been triggered, one() > listener will be removed.

If the user naively resolves to modify a field in which the event has already been triggered, the progress value will continue to check that the field has already been filled in and is OK, even if it places an incorrect value. / p>

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

Elements

Use classes to group elements or even data attributes , so it's easier to define an event for a particular group (eg .minha-classe-de-agrupamento ) than searching for 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 grouped the elements by class .progress , but as stated earlier you can also use a data-* attribute to group all elements that will be checked at the time of changing the percentage of progress.

    
22.12.2015 / 00:49