0

I am trying to apply visual cues for the user as they insert data in the form field. Red indicates that the input is not valid, whilst green indicates that the content is valid. The code is functioning, albeit in a glitched manner.

» Issue One: The "red state" flickers on any event.

» Issue Two: If you manually insert correct (number) input in all fields, one by one, and then, in the last field, press backspace, so that the current input field is an empty string, the sum total will return as NaN.

I suspect this is due to the parseInt() JavaScript method, but I need that so that computation can be performed on what is pushed into the arrays.

The specific reason why this is setup this way, is because the on initial load, the fields may already be populated and if so, I want it to display all green boxes and the check mark as a visual queue that, that section is done. This is also the reason why the entire block of code is wrapped in a setInterval() so the page will always be scanning what state should be displayed and apply the proper classes accordingly.

HTML:


Staff Information (for all office locations)
        </center>
    </div><span class="mrQuestionText" style=""><br>
    <br>
    17. A. Indicate the TOTAL number of full time staff your firm has in the following
    positions. DO NOT count a staff member in more than one position. (numeric values
    only, no need for thousands separator)</span>

    <table summary="&lt;hr/&gt;&lt;div style='border:1px solid #888; 
     background:#efefef;margin-top:1em;margin-left:2em;margin-right:2em;padding-top:5px;padding:1em;text-align:left; vertical-align:top; color:#000055;font-weight:normal;'&gt;&lt;center&gt;&lt;b&gt;Staff Information (for all office locations)&lt;/b&gt;&lt;/center&gt;&lt;/div&gt;&lt;br/&gt;&lt;br/&gt;17. A. Indicate the TOTAL number of full time staff your firm has in the following positions. DO NOT count a staff member in more than one position. (numeric values only, no need for thousands separator)&lt;span class='sumcol'&gt;&lt;/span&gt;" class="mrQuestionTable" style="display: inline-block;">
        <tbody>
            <tr>
                <td id="Cell.0.0"></td>
                <td id="Cell.1.0" class="mrGridQuestionText" style=""><span class="mrQuestionText" style=""><span style="clear:none; font-weight:bold; margin:0 
           auto; display:block; text-align:center;
          width:280px;">&nbsp;</span></span>
                </td>
            </tr>
            <tr>
                <td id="Cell.0.1" class="mrGridCategoryText" style=" text-Align: Left; 
 vertical-align: Middle; background-color: #D8D8D8; width: 430px; border-color: 
 black; border-width: 1px; border-left-style: Solid; border-right-style: Solid; 
           border-top-style: Solid; border-bottom-style: Solid;"><span class="mrQuestionText" style=" font-size: 10pt;">1. Principals/Partners</span>
                </td>
                <td id="Cell.1.1" style=" text-Align: Center; vertical-align: Middle; 
 background-color: #D8D8D8; width: auto; border-color: black; border-width: 
 1px; border-left-style: Solid; border-right-style: Solid; border-top-style: 
           Solid; border-bottom-style: Solid;">
                    <input type="text" name="_QP1_QGRQ17A_QPrincipals__Partners_QQ17A" id="_Q33_Q0_Q0" class="mrEdit" autocomplete="on" style="width: 215px; background-color: rgb(229, 242, 251);" maxlength="10" value="">
                </td>
            </tr>
            <tr>
                <td id="Cell.0.2" class="mrGridCategoryText" style=" text-Align: Left; 
 vertical-align: Middle; background-color: #F8F8F8; width: 430px; border-color: 
 black; border-width: 1px; border-left-style: Solid; border-right-style: Solid; 
           border-top-style: Solid; border-bottom-style: Solid;"><span class="mrQuestionText" style=" font-size: 10pt;">2. Project
          Managers/Directors</span>
                </td>
                <td id="Cell.1.2" style=" text-Align: Center; vertical-align: Middle; 
 background-color: #F8F8F8; width: auto; border-color: black; border-width: 
 1px; border-left-style: Solid; border-right-style: Solid; border-top-style: 
           Solid; border-bottom-style: Solid;">
                    <input type="text" name="_QP1_QGRQ17A_QProject__Managers_QQ17A" id="_Q33_Q1_Q0" class="mrEdit" autocomplete="on" style="width: 215px; background-color: rgb(229, 242, 251);" maxlength="10" value="">
                </td>
            </tr>
            <tr>
                <td id="Cell.0.3" class="mrGridCategoryText" style=" text-Align: Left; 
 vertical-align: Middle; background-color: #D8D8D8; width: 430px; border-color: 
 black; border-width: 1px; border-left-style: Solid; border-right-style: Solid; 
           border-top-style: Solid; border-bottom-style: Solid;"><span class="mrQuestionText" style=" font-size: 10pt;">3. Designers</span>
                </td>
                <td id="Cell.1.3" style=" text-Align: Center; vertical-align: Middle; 
 background-color: #D8D8D8; width: auto; border-color: black; border-width: 
 1px; border-left-style: Solid; border-right-style: Solid; border-top-style: 
           Solid; border-bottom-style: Solid;">
                    <input type="text" name="_QP1_QGRQ17A_QDesigners_QQ17A" id="_Q33_Q2_Q0" class="mrEdit" autocomplete="on" style="width: 215px; background-color: rgb(229, 242, 251);" maxlength="10" value="">
                </td>
            </tr>
            <tr>
                <td id="Cell.0.4" class="mrGridCategoryText" style=" text-Align: Left; 
 vertical-align: Middle; background-color: #F8F8F8; width: 430px; border-color: 
 black; border-width: 1px; border-left-style: Solid; border-right-style: Solid; 
           border-top-style: Solid; border-bottom-style: Solid;"><span class="mrQuestionText" style=" font-size: 10pt;">4. Other interior design
          staff</span>
                </td>
                <td id="Cell.1.4" style=" text-Align: Center; vertical-align: Middle; 
 background-color: #F8F8F8; width: auto; border-color: black; border-width: 
 1px; border-left-style: Solid; border-right-style: Solid; border-top-style: 
           Solid; border-bottom-style: Solid;">
                    <input type="text" name="_QP1_QGRQ17A_QOther__design__staff_QQ17A" id="_Q33_Q3_Q0" class="mrEdit" autocomplete="on" style="width: 215px; background-color: rgb(229, 242, 251);" maxlength="10" value="">
                </td>
            </tr>
            <tr>
                <td id="Cell.0.6a" class="mrGridCategoryText" style="text-align: Left; 
 vertical-align: Middle; background-color: #d8d8d8; width: 430px; border: 1px 
           Solid black;"><span class="rrSumColTotal mrQuestionTextBold" style="float:right;">A. Total # of Interior Design Staff:</span>
                </td>
                <td id="Cell.1.6a" style="text-align: Center; vertical-align: Middle; 
           background-color: #d8d8d8; width: auto; border: 1px Solid black;">
<span id="customSum" style="color: green; background-color: rgb(229, 242, 
             251);">0</span>

                    <div style="display:inline-block; clear:none; width:15px;" id="topFour">&nbsp;</div>
                </td>
            </tr>
            <tr>
                <td id="Cell.0.5" class="mrGridCategoryText" style="text-align: left; 
 vertical-align: middle; width: 430px; border: 1px solid black; 
           background-color: rgb(248, 248, 248);"><span class="mrQuestionText" style=" 
          font-size: 10pt;"><b style="float:right;">B. Total # of Non-Interior Design
          Staff:</b></span>
                </td>
                <td id="Cell.1.5" style="text-align: center; vertical-align: middle; width: 
           auto; border: 1px solid black; background-color: rgb(248, 248, 248);">
                    <input type="text" name="_QP1_QGRQ17A_QNon__Interior_QQ17A" id="_Q33_Q4_Q0" class="mrEdit" autocomplete="on" style=" width: 215px;" maxlength="10" value="">
                </td>
            </tr>
            <tr>
                <td id="Cell.0.5" class="mrGridCategoryText" style=" text-Align: Left; 
 vertical-align: Middle; background-color: #D8D8D8; width: 430px; border-color: 
 black; border-width: 1px; border-left-style: Solid; border-right-style: Solid; 
           border-top-style: Solid; border-bottom-style: Solid;"><span class="rrSumColTotal mrQuestionTextBold" style="float: right;">C. Total # of
          Employees in the Firm:</span>
                </td>
                <td id="Cell.1.5" style=" text-Align: Center; vertical-align: Middle; 
 background-color: #D8D8D8; width: auto; border-color: black; border-width: 
 1px; border-left-style: Solid; border-right-style: Solid; border-top-style: 
           Solid; border-bottom-style: Solid;"><span id="spRunningTotal_12_1" class="rrRunningTotal" data-tableordinal="12" data-columnordinal="1">0</span>
                </td>
            </tr>
        </tbody>
    </table>
</div>

JavaScript + jQuery:

$('#customSum').closest('table').find('tr td:nth-child(2) input[type=text]').not(':last').addClass('rowA').css("border", "solid 1px black");

var checkValid = setInterval( function() {
    $("input.rowA").each(function(i){
        var totals = [0,0,0,0];
        var total = 0;
        if($('input.complete').length == $('input.rowA').length)
        {
            $('#topFour').html('<img src="http://www.alexldixon.com/images/checkmark.png">');
            $("input.rowA").each(function(i){
                $(this).css({"background-color": "#ffe", "border": "1px solid green", "border-left": "5px solid green"}).addClass("complete");
                items = $('input.rowA:eq(' + i + ')').val();
                if(!items.match(/^\d+$/))
                {
                    items = 0;
                    $('.rowA').on("keypress change", function(evt) {
                        $(this).css({"background-color": "#ffe", "border": "1px solid red", "border-left": "5px solid red"}).removeClass("complete");
                    });
                }
                items = parseInt($('input.rowA:eq(' + i + ')').val(), 10);
                totals.push(items);
            });
            total = 0; //ADD SUM LOGIC HERE: http://stackoverflow.com/questions/1230233/how-to-go-through-an-array-and-add-their-values (Tyler Carter)
            $.each(totals,function() {
                total += this;
            });
            $('#customSum').text(total);
        } else {
            $('#topFour').html('');
            totals = [0,0,0,0];
            $("input.rowA").each(function(i){
                var items = $('input.rowA:eq(' + i + ')').val();
                if(!items.match(/^\d+$/)) //Regular Expressions Source: http://www.regexlib.com/RETester.aspx?regexp_id=669
                {
                    items = 0;
                    $('.rowA').on("keypress change", function(evt) {
                        $(this).css({"background-color": "#ffe", "border": "1px solid red", "border-left": "5px solid red"}).removeClass("complete");
                    });
                } else {
                    items = parseInt($('input.rowA:eq(' + i + ')').val(), 10);
                    $(this).css({"background-color": "#ffe", "border": "1px solid green", "border-left": "5px solid green"}).addClass("complete");
                }
                totals.push(items);
            });
            total = 0; //ADD SUM LOGIC HERE: http://stackoverflow.com/questions/1230233/how-to-go-through-an-array-and-add-their-values (Tyler Carter)
            $.each(totals,function() {
                total += this;
            });
            $('#customSum').text(total);
        }
    });
}, 120);
$('#customSum').closest('table').find("td:contains('C.'), tr td:contains('B.')").closest('tr').toggle();

CSS:

.complete {
    border: solid 1px green;
}

.rowA {
    background-color: #CCF3FF !important;
    height: 30px;
    text-align: center;
    font-size: 17px;
    color: green;
}

jsFiddle Demo

Lastly, in addition to the issue stemming from the parseInt() utilization, the jsFiddle says that I needed to move my totals, and total variables to a point where they can both be accessed safely and that is where most of my problems started. That is to say, if I re-declare those variables inside their respective $.each() statements and if() conditionals, it works fine but that is bad practice, supposedly.

Jacob
  • 77,566
  • 24
  • 149
  • 228
Alexander Dixon
  • 837
  • 1
  • 9
  • 24

2 Answers2

1

The main problem is the way setinterval being used. It not only checks the input every 120 ms, it also rebinds the keypress/change events on false input each time and on top of that it uses the same loop within its looping, thus implementing every action multiple times. Another performance thing is that within that interval the jquery objects have to be searched each time in the DOM. For example, each time $("input.rowA") is used the Dom is searched for inputs of class rowA, whereas if they are put in a variable beforehand, the buffered values can be reused.

Instead of the interval it's best to have a single change/keyup event to do the checks and reuse the same code on page load to check for those preloaded values. If despite all, you still need that interval (although normally the triggers would be known when the check has to be called), the cleaned code should prove more resource friendly (but could be improved further if the setinterval is really really really necessary)

var $inputBoxes =  $('#customSum').closest('table').find('tr td:nth-child(2) input[type=text]').not(':last');
$inputBoxes.addClass('rowA');
//or in case the former code was just a test scenario, use var $inputBoxes = $('input.rowA');

var $custSum = $('#customSum'), $chk = $('#topFour').html('<img src="http://www.alexldixon.com/images/checkmark.png">').hide(); //add the check once, but hidden (can be done in hard coded in html instead)

$inputBoxes.on("keyup change propertychange input paste", function(e) {   
    SetInput(this);      
    CheckInputs();
});

function SetInput(inputBox){
    var $input = $(inputBox), val = $input.val(), isvalid = val.length > 0 && isFinite(val) && val > 0;
    $input.data('valid', isvalid).data('number', isvalid ? parseInt(val) : null); //instead of an array, reuse the jquery elements    
    $input.toggleClass('complete', isvalid).toggleClass('error', val.length  > 0 && !isvalid);  
}

function SetAllInputs(){
    $inputBoxes.each(function(){SetInput(this);});
    CheckInputs();
}

function CheckInputs(){
    var total = 0, validcount = 0;    
    $inputBoxes.each(function(){
        if($(this).data('valid')){
            validcount++;        
            total+= $(this).data('number');
        }
    });
    $custSum.text(total);
    $chk.toggle(validcount === $inputBoxes.length); //show the 'check' image if all input is valid
}


 SetAllInputs(); //call on page load, in case of pre entered values.

Fiddle

Me.Name
  • 12,259
  • 3
  • 31
  • 48
  • Hi Me.Name this definitely seems like it is a solid solution to the problem, deviates away from setInterval (which I personally use to solve all dynamic scenario issues). Could you explain a bit more in depths on validcount++ and why you did not use arrays. For examplem where is everything being stored? – Alexander Dixon Jul 22 '15 at 16:02
  • This is the exact solution, especially since you also left in the option for me to use my setInterval(). I thank you very much and will site you as the one who produced the code. I appreciate you taking the time to absorb what I was driving at and then produce such an elegant solution for it. Thank Jesus, as now I have a proper method of coding (declaring multiple variables on one line/ number valid passes/ and the vanilla JavaScript yep nope passes). – Alexander Dixon Jul 22 '15 at 16:37
  • I did end up going the way of setInterval because there were certain events that did not bubble to the functions that check everything i.e., the context menu cut/paste. If you would like to take to the challenge of interweaving your current implementation (which is perfectly fine) to incorporate the setInterval() method, by all means have at it. – Alexander Dixon Jul 22 '15 at 17:06
  • 1
    Hi Alexander, glad it was of use. About not using a separate array: that was because the jquery object $inputBoxes itself already was an array and with a separate array the inputbox would have to be mapped to that array first. About the events not bubbling up/down, I'm curious if you can create a fiddle where this happens. When I try cut/copy/paste throught the context menu it works as soon as the textbox is validated, but perhaps the goal is that the validation happens immediately on paste? – Me.Name Jul 22 '15 at 19:05
  • 1
    Did a test to react immediately on paste and expanded the event catches to `$inputBoxes.on("keyup change propertychange input paste", ` That should catch all scenarios ( http://jsfiddle.net/17gxz1nx/8/ ) – Me.Name Jul 22 '15 at 19:09
  • 1
    Oh, regarding the validcount++, at startup and at every value changed, a data property is set on the inputBoxes. In the each inside CheckInputs, that valid property is checked, and if valid, validcount is increased by one (validcount++ is the same as validcount = validcount + 1) – Me.Name Jul 22 '15 at 19:14
  • Great advice and instruction. How were you able to replace my method of `.each()`, `index:eq( + i + )` entirely just by passing a `SetInput(this);` to `function SetInput(inputBox){` `var $input = $(inputBox)` ? It seems as though you generated a jQuery selector out of nothing. – Alexander Dixon Jul 23 '15 at 02:12
  • 1
    The jquery each (https://api.jquery.com/each/) method, sets the context to the element being iterated, so `this` inside the function passed to each, refers to the current document. (The same could have be done by including a second parameter to the function, the first is set to the index, the second would also be set to the element) – Me.Name Jul 23 '15 at 07:18
0

By changing the listener method from .on() to .bind() and applying more parameter events, the issue of the parseInt NaN result has been solved. I still get a flicker of the red border however. Ultimately however, I think the best result is to build up a master event bubble detector, as many browser plug-gins and standard features (i.e., Internet Explorer's "x" button in form fields) have various ways to bypass jQuery event listeners methods.

**

  • From

**

$('.rowA').on("keypress change", function(evt) {
    $(this).css({"background-color": "#ffe", "border": "1px solid red", "border-left": "5px solid red"}).removeClass("complete");
});

**

  • To:

**

$('.rowA').bind("change keydown keypress paste cut mouseup focus", function(evt) {
    $(this).css({"background-color": "#ffe", "border": "1px solid red", "border-left": "5px solid red"}).removeClass("complete");
});
Alexander Dixon
  • 837
  • 1
  • 9
  • 24