Progress bar indicating form completion in jQuery - javascript

I'm working on a progress bar using the progress element that increases as the user completes fields in a lengthy form. I found this SO thread which was helpful in getting some baseline functionality using a data-* attribute, but my form has a "Save Progress" feature that will save any entered field information to the browser's local storage to be retrieved later. Thus, in the event that a user returns to the form with saved data, I'd like the bar to reflect that level of progress.
Here's what is working thus far- sample markup using the data-attribute data-prog for a form field:
<!-- FORM - SAMPLE FIELD -->
<div id="form">
<div class="field-group required">
<label for="mgd_org_name">Organization Name:</label>
<div>
<input id="mgd_org_name" type="text" name="mgd_org_name" placeholder="Please enter your organization's name" required data-prog="6">
</div>
</div>
</form>
<!-- Progress bar -->
<div id="form-progress">
<p>Form Progress</p>
<progress max="100" value="0" id="progress"></progress>
</div>
And the jQuery thus far:
$(function() {
// get all form inputs w/ data-prog values
var inputs = $('#dossier-form').find('input[data-prog], textarea[data-prog]');
var progBar = $('#progress');
var progVal = 0;
$(inputs).change(function() {
if ($(this).val().length > 0) {
progVal += $(this).data('prog');
progBar.attr('value', progVal);
}
});
});
In this way, each field has a data-prog value that will be added to the progVal and reflected in the progress bar level. What I'm having trouble with is loading an initial value for any form fields that might already be filled on load—thus, I would need to find all fields that have a value entered, add the associated data-prog values, and initially load that value into the progress bar. I would also like to accordingly decrease the progress value in the event that a field is cleared.
For the initial load, I've been trying something like the following (not working):
var inputsSaved = $(inputs).val();
if($(inputsSaved).length > 0) {
progVal = $(inputs).data('prog');
console.log('has existing progress');
} else {
progVal = 0;
console.log('does not have existing progress');
}
I'm not quite sure how to find the fields with text entered on load and grab the associated prog values nor how to decrease the value accordingly if a field is cleared. Any assistance here is greatly appreciated!

First, your inputs selector can be replaced by:
var inputs = $('#dossier-form [data-prog]');
Then, $(inputs).change(..., inputs already is a jQuery object. No need to wrap it with $().
You had the progress update function correct. I just placed it in a named function to be able to call it from different places in code (and avoid duplicate code).
I added the localStorage save and retreive...
$(function() {
// get all form inputs w/ data-prog values
var inputs = $('#dossier-form [data-prog]');
var progBar = $('#progress');
var progVal = 0;
// Establish the "dificulty unit" base.
var totalDifficulty = 0;
inputs.each(function(){
totalDifficulty += $(this).data('prog');
});
var difficultyUnit = 100/totalDifficulty;
// Update progressbar
function updateProgress(el){
progVal += parseFloat($(el).data('prog')) * difficultyUnit;
progBar.val(progVal);
}
// LocalStorage on load
inputs.each(function(){
var value = localStorage.getItem(this.id);
if(value !=null && value !=""){
$(this).val(value);
updateProgress(this);
}
}); // End load from LocalStorage
// Blur handler to save user inputs
inputs.on("blur",function(){
// Reset the progress
progVal = 0;
progBar.val(progVal);
// Loop through all inputs for progress
inputs.each(function(){
if ($(this).val().length > 0) {
updateProgress(this);
}
// LocalStorage
localStorage.setItem(this.id,this.value);
});
}); // End on blur
}); // End ready
localStorage isn't allowed in the SO snippets... So have a look at this CodePen.
So you can now give some "difficulty level" without worring about the total obtained by all the data-prog to be 100. Just give an arbitrary number you feel like. ;)

If you always add up the progress from scratch, you can do the same thing in both cases.
$(function() {
// get all form inputs w/ data-prog values
var inputs = $('#dossier-form').find('input[data-prog], textarea[data-prog]');
var progBar = $('#progress');
function update () {
var progVal = 0;
inputs.each( function () {
if ($(this).val().length > 0) {
progVal += Number( $(this).data('prog') );
}
})
progBar.attr('value', progVal);
}
inputs.change( update );
// Fill in fields from localStorage, then:
update();
});

Related

Dynamically check javascript value after database update

I am developing a dynamically generated and self updating form in ASP.NET MVC using javavascript, Jquery, JSON/Ajax calls.
Here is how I set up my view code from the controller. I loop through all available controls from the controller:
<ul>
#using (Html.BeginForm("Index", "WebMan", FormMethod.Post)) {
foreach (var row in Model.controls)
{
<li>
<label>#row.Name</label>
#if (row.ControlType == "STRING" || row.ControlType == COMMENT")
{
<input type="text" name="#row.Name" id="#row.NameID" value="#row.Value" data-original-value="#row.Value" class="form-control data-field" style="width: 300px"/>
}
else if (row.ControlType == "DDL")
{
<select name="#row.Name" id="#row.NameID" class="form-control data-field" value="#row.Value" data-original-value="#row.Value" style="width: 300px">
#foreach (var o in row.Options)
{
<option value="#o.Value">#o.Text</option>
}
</select>
}
</li>
}
<button type="submit">Update</button>
}
</ul>
(notice that I set the value to the value from the database and I also set the “data-original-value” to the value from the database as well)
I am using the “data-original-value” to check to see if the value has changed later.
I also set up a javascript timer that executes every 5 seconds. This timer is meant to “update” the page. (code for timer below)
var interval = setInterval(function () { Update(); }, 10000);
When the timer executes, we loop through each control in the “data-field” class. This allows me to check each dynamically generated control.
Basically, If a user has edited a control, I DO NOT want to update that control, I want to ignore it. I only want to update that specific control if a user has not changed the value. When a user changes the value, the current value != orig value (Data-original-value), so we set the field to yellow and ignore the database update code.
function Update() {
UpdateControls();
}
function UpdateControls() {
$(".data-field").each(function () {
var nameAttr = $(this).attr('name');
var id = $(this).attr('id');
var val = document.getElementById(id).value;
var origVal = $(this).data("original-value");
if (origVal == val) {
//user did not change control, update from database
var url = "/WebMan/UpdateControlsFromDB/";
$.ajax({
url: url,
data: { name: nameAttr },
cache: false,
type: "POST",
success: function (data) {
if (data != null) {
document.getElementById(id).setAttribute("data-original-value", data);
document.getElementById(id).value = data;
}
else {
alert(data);
}
},
error: function (response) {
alert("Issue updating the page controls from database");
}
});
}
else {
document.getElementById(id).style.backgroundColor = "yellow";
//user changed control, do not update control and change color
}
});
}
If no change, this ajax method in my controller is called:
[HttpPost]
public ActionResult UpdateControlsFromDB(string name)
{
var curValue= db.Setup.Where(x => x.Name == name).Select(x =>Value).FirstOrDefault();
return Json(curValue);
}
The code works correctly if a user modifies the field. It senses that the user modifies the code, and changes the field yellow.
The part that does not work correctly, is if the database updates the field. When the database first updates the field, it looks great. We set the “data-original-field” value to the value as well, to tell our code that is should not turn yellow and the user has not modified it.
But after another update, “value” and “original-value” do not match. The code document.getElementById(id).value somehow gets the OLD version of the control. It does not get the current value. So then on next loop, the values don’t match and we stop updating from DB and the control turns yellow.
My issue is that my code senses that the control value changed (database update) and turns it yellow, when I only want to turn the control yellow when a USER has changed the value in the control.
I only want to change the control and prevent updating from DB when the control has been modified by the user.
Thank you for any help.

How to check if a page comes after clicking the back button using jQuery?

I have a form where I am calculating the total amount using jQuery.
The function I created for that is updateTotal();
and the form action is another page and the action page has this button:
<button class="btn btn-success" onclick="history.go(-1);" type="submit" name="edit">EDIT</button>
so when the user clicks on the EDIT button page goes back to the form again (first page) and all the filled up details are there except the repetitve fields created using jQuery.
The sample form is here in js fiddle
I just want to run this function updateTotal(); if the user comes to the form by clicking the EDIT (basically browse go back) button..
Is there any way to do this in jQuery?
UPDATES FOR FUTURE REFERENCE - I SOLVED IT LIKE THIS
html:
<input type="text" id="amount" name="amount[]" placeholder="Amount" required="required" class="form-control inputChangeVal reqF reqFamount" data-js-input-type="number" />
and the jQuery :
jQuery(document).ready(function() {
var hiddenTot = jQuery('.reqFamount').val() ;
jQuery(".totalAmount").val(hiddenTot);
});
Define a hidden field to store the computed value.
<input type="hidden" id="hiddenTotal" />
Then store the calculated value to the hidden field with Id 'hiddenTotal'.
function updateTotal() {
var price = 0;
$(".inputChangeVal").each(function() {
var t = parseFloat(jQuery(this).val(), 10);
price = price + t;
});
var total = price.toFixed(2);
$(".totalAmount").val(total);
$("#hiddenTotal").val(total);
}
Then when the browse back is triggered the hiddenfield is automatically filled by the browser.
Next check when the document is ready, read the value of hiddenTotal and write to totalAmount.
$(document).ready(function (){
// read value and set value of totalAmount to hiddentotal;
var hiddenTotal = $("#hiddenTotal").val() || 0; //use the hiddenTotal value or if not present set 0 as the default value
$(".totalAmount").val(hiddentotal)
}
Now totalAmount is restored. This even works when you leave the page and return using your browsers history.

limiting local storage input to 1

i have an input which displays data in local storage underneath. At the moment it currently keeps adding whatever has been inputted and displays with an '' in between. This is fine however all I need is for it to Store and display ONE input and display it. If the user
wants to change the input, there will be a button which clears local storage.
Can anybody help with the function for this.
<script>
function storeText(inputID) {
//check to see if the localStorage item already exists
var userInput = localStorage.userInfo;
//set it up for new data to be appended
if(userInput!=null) userInput+=" ";
else userInput="";
//add the new data
userInput += document.getElementById(inputID).value;
//set the localStorage field with the updated data
localStorage.userInfo = userInput;
//write it to the page
document.getElementById("result").innerHTML = localStorage.userInfo;
if (userInput > 1){
alert("Please Clear to add a new goal");
return false;
}
</script>
This is my Input and display
<input id="userText" type="text"/>
<input type="button" value="store" onclick="storeText('userText')"/>
<div id="result">
Result here
</div>
localStorage.clear(); - this will clear local storage entirely
localStorage.removeItem('userInput'); - this removes a specific item - you can then just re-add it.
Just apply this to your buttons onClick event

JavaScript - How to assign user input value to variable inside script

Using widget script--http://socialmention.com/tools/-- from Social Mention. Tried to modify by adding input box to allow user to change social media topic (var smSearchPhrase). I created a function [smSearch()] to retrieve user data (smTopic), assign it to a new variable (var newTopic) and then assign that value to var smSearchPhrase. The assignment does not work.
The function appears to work based on values observed via alerts, however, I cannot figure out how to assign the value from var newTopic to var smSearchPhrase inside the script. I experimented by placing script inside the function, but that didn't work either. Any assistance is appreciated.
If I failed to include all necessary information, please advise. Thanks for any assistance.
HTML:
<form>
<label for="smTopic">Enter topic:</label>
<input type="text" id="smTopic">
<button onclick="smSearch()">Submit</button>
<input type="reset">
</form>
Function: (includes alerts to check values)
function smSearch(){
var newTopic=document.getElementById("smTopic").value;
if(newTopic === ""){
alert("Please enter new social media topic.");
}else{
alert("New topic: " + newTopic);
smSearchPhrase = newTopic;
alert("Value of smSearchPhrase: " + smSearchPhrase);
}
Script: var smSearchPhrase in original has value assigned, e.g. var smSearchPhrase = 'social mention';
<script type="text/javascript">
// search phrase (replace this)
var smSearchPhrase;
// title (optional)
var smTitle = 'Realtime Buzz';
// items per page
var smItemsPerPage = 7;
// show or hide user profile images
var smShowUserImages = true;
// widget font size in pixels
var smFontSize = 11;
// height of the widget
var smWidgetHeight = 800;
// sources (optional, comment out for "all")
//var smSources = ['twitter', 'facebook', 'googleblog', 'identica'];
</script>
<script type="text/javascript" language="javascript" src="http://socialmention.s3.amazonaws.com/buzz_widget/buzz.js"></script>
I think your form is submitting back to the backend, you need to stop the form from doing that by returning false from onsubmit or canceling the event.
So this should work:
<form onsubmit="return smSearch();">
<label for="smTopic">Enter topic:</label>
<input type="text" id="smTopic">
<button>Submit</button>
<input type="reset">
</form>
And return false in your JavaScript:
function smSearch(){
var newTopic=document.getElementById("smTopic").value;
if(newTopic === ""){
alert("Please enter new social media topic.");
}else{
alert("New topic: " + newTopic);
smSearchPhrase = newTopic;
alert("Value of smSearchPhrase: " + smSearchPhrase);
}
return false;
}
Personally I'd use preventDefault() on the event argument (not shown here), but that only works across all browsers when you also include a JavaScript library like jQuery as some versions of IE use a bubble property on the event or something.

Multiple form fields with same 'name' attribute not posting

I'm dealing with some legacy HTML/JavaScript. Some of which I have control over, some of which is generated from a place over which I have no control.
There is a dynamically generated form with hidden fields. The form itself is generated via a Velocity template (Percussion Rhythmyx CMS) and JavaScript inserts additional hidden form fields. The end result is hidden form fields generated with the same 'name' attribute. The data is being POSTed to Java/JSP server-side code about which I know very little.
I know that form fields sharing the same 'name' attribute is valid. For some reason the POSTed data is not being recognized the back end. When I examine the POST string, the same-name-keys all contain no data.
If I manipulate the code in my dev environment such that only a single input field exists for a given name, the data IS POSTed to the back end correctly. The problem is not consistent, sometimes, it works just fine.
Is there something I can do to guarantee that the data will be POSTed? Can anyone think of a reason why it would not be?
I should really update my answer and post code here, because POST requests without
variable strings indicates the problem is on the client side.
How about this:
<script type="text/JavaScript">
function disableBlankValues()
{
var elements = document.getElementById("form1").elements;
for (var i = 0; i < elements.length; i++)
{
if (elements[i].value == "")
elements[i].disabled = true;
}
}
</script>
<form action="page.php" method="POST" onsubmit="disableBlankValues()" id="form1">
<input type="hidden" name="field1" value="This is field 1."/>
<input type="hidden" name="field1" value=""/>
</form>
EDIT
I now realize the actual problem (multiple variables with the same name should be passed to JSP as an array) and my solution is probably not what the OP is looking for, but I'm leaving it here just in case it happens to help someone else who stumbles upon this post.
you could use something like:
var form = document.getElementById('yourformid');
var elements = form.getElementsByName('repeatedName');
var count = 0;
for(var item in elements){
elements[item].name += count++;
}
this way you will get each hiddenfield with the names:
name0
name1
name2
...
I've worked out a brute-force solution. Note that I'm pretty aware this is a hack. But I'm stuck in the position of having to work around other code that I have no control over.
Basically, I've created an ONSUBMIT handler which examines the form for the repeated hidden fields and makes sure they are all populated with the correct data. This seems to guarantee that the POST string contains data regardless of how the form gets rendered and the Java back end appears to be happy with it as well.
I've tested this in the following situations:
Code generates single instances of the hidden fields (which does happen sometimes)
Code generates multiple instances of the hidden fields
Code generates no instances of the hidden fields (which should never happen, but hey...)
My 'else' condition contains a tiny bit of MooTools magic, but it's otherwise straight-forward stuff.
Maybe someone else will find this useful one day...
Thanks for the help!
<form method="post" name="loginform" id="loginform" action="/login" onsubmit="buildDeviceFP(this);">
<script type="text/javascript">
function insertFieldValues( fields, sValue )
{
if ( 'length' in fields )
{
// We got a collection of form fields
for ( var x = 0; x < fields.length; x++ ) {
fields[x].value = sValue;
}
}
else
{
// We got a single form field
fields.value = sValue;
}
}
function buildDeviceFP( oForm )
{
// Get the element collections for Device Fingerprint & Language input fields from the form.
var devicePrintElmts = oForm.elements.deviceprint;
var languageElmts = oForm.elements.language;
// 'devicePrintElmts' & 'languageElmts' *should* always exist. But just in case they don't...
if ( devicePrintElmts) {
insertFieldValues( devicePrintElmts, getFingerprint() );
} else if ( oForm.deviceprint ) {
oForm.deviceprint.value = getFingerprint();
} else {
$('logonbox').adopt(
new Element( 'input', {'type':'hidden', 'name':'deviceprint', 'value':getFingerprint()} )
);
}
if ( languageElmts) {
insertFieldValues( languageElmts, getLanguage() );
} else if ( oForm.language ) {
oForm.language.value = getLanguage();
} else {
$('logonbox').adopt(
new Element( 'input', {'type':'hidden', 'name':'language', 'value':getLanguage()} )
);
}
}
</script>

Categories

Resources