2

In my sample code, I have 2 dropdownlists (but in the real code the number varies because the dropdownlists are created dynamically) and what I wanted to do is to count how many dropdownlists are selected with a non-zero value. I used closure to keep track of the total number of dropdownlists with non-zero selected value. I was successful in doing that (refer to http://jsfiddle.net/annelagang/scxNp/9/).

Old/Working Code

$("select[id*='ComboBox']").each(          
          function() {              
              $(this).change(
                  function() {
                      compute(this);
              });         
      });

    var compute = (function () {
    var total = 11; 
    var selectedDdl = [];

    $("#total").text(total);     
    return function (ctrl) {
         var id = $(ctrl).attr("id");
         var ddlMeal = document.getElementById(id);
         var found = jQuery.inArray(id, selectedDdl);

        if (ddlMeal.options[ddlMeal.selectedIndex].value != 0){
             if(found == -1){
                 total += 1; 
                 selectedDdl.push(id);        
             } 
             else{
                 total = total;
             }
         }
         else {
             total -= 1;
             var valueToRemove = id;
             selectedDdl = $.grep(selectedDdl, function(val) 
                                      { return val != valueToRemove; });
         }        

         $("#total").text(total); 
    };     
}());

Note: I initialized the total variable with 11 because as I mentioned in the real code I can have more than 2 dropdownlists and I just wanted to test if my code will work on values more than 2.

When I tried transferring the closure inside the .change() event, it no longer works (see http://jsfiddle.net/annelagang/scxNp/12/) Could somebody please help me figure this one out? I've been doing this code for a few days now and it's getting quite frustrating.

New/Non-working code:

$("select[id*='ComboBox']").each(          
          function() {              
              $(this).change(
                  function() {
                     (function () {
                var total = 11; 
                var selectedDdl = [];

                $("#total").text(total);     
                return function (ctrl) {
                     var id = $(ctrl).attr("id");
                     var ddlMeal = document.getElementById(id);
                     var found = jQuery.inArray(id, selectedDdl);

                     if (ddlMeal.options[ddlMeal.selectedIndex].value != 0){
                         if(found == -1){
                             total += 1; 
                             selectedDdl.push(id);        
                         } 
                         else{
                             total = total;
                         }
                     }
                     else {
                         total -= 1;
                         var valueToRemove = id;
                         selectedDdl = $.grep(selectedDdl, function(val) 
                                           { return val != valueToRemove; });
                         }        
                    $("#total").text(total); 
                };     
        }());
          });         
      });

Thanks in advance.

P.S. I'm also open to less messier solutions.

Annie Lagang
  • 3,185
  • 1
  • 29
  • 36
  • 1
    It is helpful to post the relevant code within your question. It keeps the question relevant even if jsfiddle disappears. – James Montagne Jan 06 '12 at 01:44

3 Answers3

3

Problem

You are returning function from self-executing anonymous function, but do not assing the returned value anywhere.

Simplifying your code, it looks like that:

/* some code here */
$(this).click(function(){
    /** The following function is executed, returns result, but the
     *  result (a function) is not assigned to anything, nor returned
     */
    (function(){
        /* some code here */
        return function(ctrl){
            /* some code here */
        };
    }());
});
/* some code here */

Solution

Generally your code is very unreadable, you should improve it for your own good (and to avoid such problems). The problem above can be quickly fixed by passing parameter to the function that returns a function (but did not call it). Solution is here: jsfiddle.net/scxNp/13/

Solution: explanation

What I did was simple - I spotted that you pass this to the function from the first (working) example, but you do not even execute this function in the second (incorrect) example. The solution, simplified, looks like this:

/* some code here */
$(this).click(function(){
    /** Now the result of the following function is also executed, with
     *  parameter passed as in your working example
     */
    (function(){
        /* some code here */
        return function(ctrl){
            /* some code here */
        };
    }())(this);
});
/* some code here */

Hope this makes sense :)

Ps. I have also updated my first code snippet so the changes should be easily spotted :)

Ps.2. This is only a quickfix and - as mentioned above - there is a lot to be made to make your code readable. Also every function works as closure, so do not overuse self-executing (function(){/* your code */})(); that you do not assign to anything (it is useful, but one time for one script should be enough). Instead use the existing closures.

Tadeck
  • 132,510
  • 28
  • 152
  • 198
  • I followed what you said. It still didn't work. Am I missing something here? http://jsfiddle.net/annelagang/scxNp/14/ Thanks. – Annie Lagang Jan 06 '12 at 01:56
  • That solution only fixes that one problem. There are others. In the new posted fiddle it will reset the total to 11 each time something changes and then only update it based on that one select that changed, meaning it will only be able to show 11 or 12. – James Montagne Jan 06 '12 at 02:00
  • @AnneLagang: James may be right, it works differently from your example (my example returns 10 or 12). Could you explain what do you expect from the following combinations? Here is the list, answer accordingly, please (option1, option2): (0,0), (0,1), (0,2), (0,3), (1,0), (1,1), (1,2), (1,3), (2,0), (2,1), (2,2), (2,3), (3,0), (3,1), (3,2), (3,3) – Tadeck Jan 06 '12 at 02:16
  • @Tadeck (0,0) = 11, (0,1) = 12, (0,2) = 12, (0,3) = 12, (1,0) = 12, (1,1) = 13, (1,2) = 13, (1,3) = 13, (2,0) = 12, (2,1) = 13, (2,2) = 13, (2,3) = 13, (3,0) = 12, (3,1) = 13, (3,2) = 13, (3,3) = 13... I don't care what the selected value is. All what I am after for is to count how many non-zero selected values are there assuming that the initial count is 11. Anyways, thanks for your lengthy answer. I'm a beginner when it comes to closures. I will definitely keep your tips on mind. Thanks! – Annie Lagang Jan 06 '12 at 02:22
  • @AnneLagang: No problem, I see now (after the explanation), that James's approach is the best for you here :) Good luck! Ps. Consider looking at this: [O'Reilly: JavaScript Development and Resources](http://oreilly.com/javascript/index.html) - very good starting point! Also valuable for advanced developers :) [it is not an ad, I am not affiliated with O'Reilly, I really love some of their books] – Tadeck Jan 06 '12 at 02:40
2

I believe this should work:

$("select[id*='ComboBox']").change(function() {
    $("#total").text($("select[id*='ComboBox'][value!=0]").length);
});

http://jsfiddle.net/scxNp/15/

James Montagne
  • 77,516
  • 14
  • 110
  • 130
  • You may also want to consider putting a shared class on each element and using that in the selector rather than `*=`. With the number of elements you have it probably doesn't matter for performance but it just seems cleaner. – James Montagne Jan 06 '12 at 02:02
  • Cool, succinct and straight to the point. :D Few questions, what's length for? What if I wanted the initial variable to start at 11 instead of zero? – Annie Lagang Jan 06 '12 at 02:08
  • Basically what this does is get all selects with an appropriate id and a value not equal 0. Then the `.length` tells how many elements were returned. If you want some sort of offset, just add it to `length` http://jsfiddle.net/scxNp/17/ – James Montagne Jan 06 '12 at 02:10
  • Oh, I see. I should have saved myself from a lot of grief if I knew such property existed. Thanks! :D – Annie Lagang Jan 06 '12 at 02:17
0

how about this?

html:

<select id="ComboBox1" class="howMany">
<option value="0">Value 0</option>
<option value="1">Value 1</option>
<option value="2">Value 2</option>
<option value="3">Value 3</option>
</select>

<select id="ComboBox2" class="howMany">
<option value="0">Value 0</option>
<option value="1">Value 1</option>
<option value="2">Value 2</option>
<option value="3">Value 3</option>
</select>

<div id="total"></div>

script:

$(".howMany").change(
function ()
{
    var nonZero = 0;
    $(".howMany").each(
        function ()
        {
            if ($(this).val() != '0')
                ++nonZero;
        }           
    );
    $("#total").text('There are '+nonZero+' options selected');       
}
);
al01
  • 122
  • 1
  • 4