JavaScript Sort Ordering and Choice Limiting

Hi

I have a simple form where the user can make up to three selections. The user can select the checkboxes in any order, but as they click on each one, the order in which they were checked is remembered, and the position is set to either 1, 2 or 3 when the checkbox is checked, or 0 if the checkbox is unchecked.

The catch is that if a user selects 3 checkboxes, but then unselects the checkbox in position 2, position 3 needs to become 2, so the order is 1,2 and not 1,3.

Example in w3schools

<!DOCTYPE html>
<html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script>
        function IsChecked(chk) {

            if (TotalChecked() > 3) {
                alert('Only 3 boxes can be checked!');
                $(chk).prop("checked", false);
                return;
            }

            SetPosition();
        }

        function TotalChecked() {
            var total = 0;
            $(".chk").each(function () {
                if ($(this).prop("checked")) {
                    total++;
                }
            });

            return total;
        }

        function SetPosition() {
            var items = [];

            //Get the items and their values first
            $(".position").each(function () {
                var id = $(this).attr('id');
                var val = $(this).val();
                var item = { id: id, value: val };
                var chk = $("#" + id.replace("p", "c"));
                if ($(chk).prop("checked")) {
                    items.push(item);
                }
            });

            //Sort items

            items.sort(function (a, b) {
                return a.value - b.value;
            });

            $(".position").each(function () {
                var id = $(this).attr('id');
                var chk = $("#" + id.replace("p", "c"));
                var len = items.length;



                if ($(chk).prop("checked")) {
                    var val = $(this).val();

                    if (len == 0) {
                        if (val == 0) { val++ };
                    }
                    else if (len == 1) {
                        items[0].value = 1;
                    }
                    else if (len == 2) {
                        items[0].value = 2;
                        items[1].value = 1;
                    }
                    else {
                        items[0].value = 3;
                        items[1].value = 2;
                        items[2].value = 1;
                    }
                }
                else {
                    $(this).val(0);
                }
            });

            var i;
            for (i = 0; i < items.length; i++) {
                $("#" + items[i].id).val(items[i].value);
            }

        }
    </script>
</head>
<body>

    <input id="c1" onchange="IsChecked(this)" class="chk" type="checkbox" />
    <input id="p1" class="form-control position" type="text" value="0" /><br />
    <input id="c2" onchange="IsChecked(this)" class="chk" type="checkbox" />
    <input id="p2" class="form-control position" value="0" /><br />
    <input id="c3" onchange="IsChecked(this)" class="chk" type="checkbox" />
    <input id="p3" class="form-control position" value="0" /><br />
    <input id="c4" onchange="IsChecked(this)" class="chk" type="checkbox" />
    <input id="p4" class="form-control position" value="0" /><br />
    <hr />
    <p id="message"></p>
</body>
</html>

Open in new window


I just can't seem to get this to work, any help is much appreciated.

Thanks.
LVL 1
maaknplanSenior DeveloperAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Julian HansenCommented:
The solution is quite simple.

Start with a value set to 1 and increment it each time a checkbox is clicked - setting the checkbox's value to this number if the checbox is checked on or 0 if it checked off.

Your HTML might look like this
<input type="checkbox" name="cb1" />
<input type="checkbox" name="cb2" />
<input type="checkbox" name="cb3" />

Open in new window

jQuery would look like this
var start = 1;
$(function() {
  $(':checkbox').change(function() {
    this.value = this.checked ? start++ : 0;
  });
});
</script>

Open in new window


Plain JavaScript would look like this
<script>
var start = 1;
var cb = document.querySelectorAll('input[type=checkbox]');
for(var i = 0; i < cb.length; i++) {
  cb[i].addEventListener('change', function () {
    this.value = this.checked ? start++ : 0;
  });
}
</script>

Open in new window

Working sample here
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
maaknplanSenior DeveloperAuthor Commented:
Thanks for the reply. Your solution is pretty elegant. The only issue is it doesn't reverse if you uncheck the checkboxes.

Here is what I ended up with - w3schools example.

<!DOCTYPE html>
<html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script>
        function IsChecked(chk) {
            var total = TotalChecked();
            if (total > 3) {
                alert('Only 3 boxes can be checked!');
                $(chk).prop("checked", false);
                return;
            }
            var id = $(chk).attr('id');
            var pos = $("#" + id.replace("c", "p")); //Find the corresponding input
            var initVal = pos.val(); //Get the initial value of the corresponding input

            if ($(chk).prop("checked") == false) {
                pos.val(0); //Reset the corresponding input to 0 if the checkbox is unchecked
            }

            //If the corresponding input value of the function checkbox is 3, no changes need to be made as the other
            //inputs will be correctly set.
            if (initVal == 3) { return };

            switch (total) {
                case 1: //Only one checkbox is selected, so the corresponding input must be 1
                    if ($(chk).prop("checked") == true) {
                        pos.val(1); //If the function checkbox is checked, set the corresponding input to 1
                    }
                    else { //If the function checkbox is not checked, find the checked checkbox, and set its corresponding input to 1
                        $(".chk").each(function () {
                            if ($(this).prop("checked")) {
                                var id = $(this).attr('id');
                                var pos2 = $("#" + id.replace("c", "p"));
                                pos2.val(1);
                            }
                        });
                    }
                    break;
                case 2: //Two checkboxes are selected, so the corresponding inputs must be 1 and 2
                    if ($(chk).prop("checked") == true) {
                        pos.val(2); //If the function checkbox is checked, set the corresponding input to 2
                    }
                    else {//If the function checkbox is not checked, find the checked checkboxes, and set the corresponding inputs
                        $(".chk").each(function () {
                            if ($(this).prop("checked")) {
                                var id = $(this).attr('id');
                                var pos2 = $("#" + id.replace("c", "p")); //Find the corresponding input for the checked checkbox
                                //It doesn't matter if pos2.val() == 1
                                if (pos2.val() == 2) { 
                                    pos2.val(1);
                                }
                                else if (pos2.val() == 3) {
                                    pos2.val(2);
                                }
                            }
                        });
                    }
                    break;
                case 3:
                    if ($(chk).prop("checked") == true) {
                        pos.val(3);
                    }
                    break;
            }
        }

        function TotalChecked() {
            var total = 0;
            $(".chk").each(function () {
                if ($(this).prop("checked")) {
                    total++;
                }
            });
            return total;
        }

    </script>
</head>
<body>

    <input id="c1" onchange="IsChecked(this)" class="chk" type="checkbox" />
    <input id="p1" class="form-control position" disabled="disabled" type="text" value="0" /><br />
    <input id="c2" onchange="IsChecked(this)" class="chk" type="checkbox" />
    <input id="p2" class="form-control position" disabled="disabled" value="0" /><br />
    <input id="c3" onchange="IsChecked(this)" class="chk" type="checkbox" />
    <input id="p3" class="form-control position" disabled="disabled" value="0" /><br />
    <input id="c4" onchange="IsChecked(this)" class="chk" type="checkbox" />
    <input id="p4" class="form-control position" disabled="disabled" value="0" /><br />
    <hr />
    <p id="message"></p>
</body>
</html>

Open in new window

0
maaknplanSenior DeveloperAuthor Commented:
The solution offered is simple and elegant, however it does not allow the order selected to be reversed.
0
Julian HansenCommented:
however it does not allow the order selected to be reversed.

Please explain by reversed?
0
Julian HansenCommented:
Just to explain the solution a bit more.

The paradigm of this solution is to use a progressive counter - like the queue tickets you sometimes get in a store. Each click represents a new arrival and so gets the next number in the queue. A click off represents the leaving of the queue so the number goes down to 0.

The checkbox with the lowest (non-zero) value is the one that was clicked first out of all the remaining clicked items.

The actual value in this case is not relevant - only its magnitude relative to the values of the other clicked items - which provides an ordering on the selected checkboxes.

Trying to see how this solution does not meet the requirements of the questions asked?
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
JavaScript

From novice to tech pro — start learning today.