Background: I am working with an e-learning platform that asks students a series of questions with 4 options to determine what career path would suit them best. We recently decided to move towards a configuration where, instead of putting them through the difficult decision of choosing just one #1 option, we would allow them to sort a drag-and-drop list to order the 4 options in their order of preference.

The catch is, the system is designed to still only care about whatever they placed first. Call it reverse psychology, if you will. Each option has a weight of 1-4 - and they don't always show up in that order - so you reorder them, it adds the weight to your running total score, and later on there's ranges of scores that determine where you fit.

The system works great, even if my methods of handling the data are unorthodox (keeping a running total and feeding it back into the URL to be carried along - I'm not worried about tampering because the accuracy of the numbers is for the student's benefit. If they want to tinker with it, they can blame themselves for false results :D)

Here's the bug: If the student does not touch the sorted list - i.e. decides that they like the order of the options as they loaded on the page - the line to update the running total score doesn't run. It only runs if they move something around. Even if the #1 option stays in place but other stuff is moved around, it still works fine - but if they just do not touch it at all, it keeps the value from the previous question, effectively adding "0" to their score and this does affect results.

Code after the jump, but first I figured I would cite specifically the line that updates the total:

.done(function(data) { var runningTotalNew = parseInt(runningTotalCurrent) + parseInt(data); document.getElementById("runningTotal").value = parseInt(runningTotalNew); });

To be completely honest, I jerry-rigged it so bad that I don't even really fully understand how "data" knows what the first option is, but that's where the action happens and it resets a hidden input (that's later passed into the URL) with the new score. It's fairly near the top of the full code:

<script type="text/javascript"> $(document).ready(function(){ $(function() { $("#qoptions ul").sortable({ opacity: 0.8, cursor: 'move', update: function() { var order = $(this).sortable("serialize"); var runningTotalCurrent = "<?php echo $_GET['rt']; ?>"; $.get("/logic_getTopRankedItem.php", { rand: Math.random(), sortableData: encodeURIComponent(order) }) .done(function(data) { var runningTotalNew = parseInt(runningTotalCurrent) + parseInt(data); document.getElementById("runningTotal").value = parseInt(runningTotalNew); }); } }); }); }); </script> <script type="text/javascript"> function nextQuestion() { // Some info to figure this out var totalQuestions = 15; var currentQuestion = <?php echo $_GET['page']; ?>; var thisPage = "/questionnaires/skills/"; var completePage = "/questionnaires/skills/complete/"; var userSalt = <?php echo $_GET['salt']; ?>; // The logic if(currentQuestion <= totalQuestions) { var nextQuestion = currentQuestion + 1; if(nextQuestion <= totalQuestions) { window.location = thisPage + userSalt + "/" + nextQuestion + "/" + parseInt(document.getElementById("runningTotal").value); } if(nextQuestion > totalQuestions) { window.location = completePage + parseInt(document.getElementById("runningTotal").value); } } if(currentQuestion > totalQuestions) { // Not possible, 404 it out window.location = "/404"; } } </script> <?php // Only show "let's get started" if they're on the first question. if($_GET['page'] == "1") { echo "<div align=\"center\">\r

"; echo "<h1>let's get started.</h1>\r

"; echo "</div>\r

"; } // Load question/weight data from data file include($_SERVER['DOCUMENT_ROOT']."/data/questionnaire_skills.php"); // Determine current question from page number $currentQ = $_GET['page']; // Determine current running total, which the website sets to 0 at survey start $runningTotal = $_GET['rt']; ?> <div style="padding-top: 15px;"></div> <div style="width: 610px; margin: auto;"> <p style="font-size: 16px;" align="center"> For each option set, <strong>rank the options from <i>most</i> preferred to <i>least</i> preferred</strong> to indicate the skill you'd like to use most in your next career move. </p> </div> <table border="0" align="center" width="625" height="175"> <tr> <td style="width: 75px; background: url('/images/pref_spec.png') no-repeat;" valign="top"><!-- Preference Spectrum --></td> <td style="width: 550px;"> <!-- Begin options drag n drop --> <div id="qoptions"> <ul> <li id="option_<?php echo $weight[$currentQ][1]; ?>"><?php echo $option[$currentQ][1]; ?> <div class="clear"></div> </li> <li id="option_<?php echo $weight[$currentQ][2]; ?>"><?php echo $option[$currentQ][2]; ?> <div class="clear"></div> </li> <li id="option_<?php echo $weight[$currentQ][3]; ?>"><?php echo $option[$currentQ][3]; ?> <div class="clear"></div> </li> <li id="option_<?php echo $weight[$currentQ][4]; ?>"><?php echo $option[$currentQ][4]; ?> <div class="clear"></div> </li> </ul> </div> <!-- End options drag n drop --> <!-- Start options form handler --> <form name="optionsHandler"> <input type="hidden" name="runningTotal" id="runningTotal" value="<?php echo $runningTotal; ?>" /> </form> <!-- End options form handler --> <!-- Button to push all this data into the next question in an informal cookie --> <p align="center"> <a href="javascript:nextQuestion();" class="btn btn-success btn-lg" style="font-family: 'Ubuntu';" role="button">continue »</a> </p> </td> </tr> </table>

Don't worry about the PHP file that it references in /data/ - that's just containing the weights and options and it's just a bunch of arrays. I think this is an issue with the jQuery handling of the sortable data. FWIW, this uses Bootstrap and it's my first foray into both Bootstrap and an application so dense into data processing.