1

I'm just starting with JavaScript and jQuery, and I've ran into a bit of a problem I'm not even sure how to begin to solve. I'm working on a teacher gradebook. Whenever a user runs the saveScores() function, if there are ANY scores left blank inside a category that is NOT collapsed, an alert should be sent. Otherwise, if all scores are filled in, the form on the page should submit.

Example HTML for a score: <td class='score'> <input type='text' name='grades[26][32]'> /10 </td>

I assume I'll need to run a loop, but the part I'm having trouble figuring out is the collapsed part. How would I run the loop only on the categories that are NOT collapsed? Also, what would this loop even look like?

Any advice, solutions, or helpful articles would be much appreciated.

For visual people: If the function was ran in this example, the alert would be sent since the input circled in red is empty. All the scores under 'vocabulary worksheet' would be ignored since that category is collapsed right now.

enter image description here

$(document).ready(function() {
  $('[data-toggle="toggle"]').change(function() {
    $(this).parents().next('.hide').toggle();
  });
});

$("input[type=text]").change(function() {
  $('.floater').get(0).style.display = 'block';
});

var confirm = document.getElementById("confirm");

function saveScores(arg) {
  /* If all scores are filled in */
  {
    document.getElementByTagName("form").submit();
  }
  /* Else if any score is blank */
  {
    alert("A score is left blank!");
  } else if (arg == "hide") {
    confirm.style.display = "none";
  }
}
window.onclick = function(event) {
  if (event.target == confirm) {
    confirm.style.display = "none";
  }
}
body {
  margin: 0;
  background-color: #F2F2F2;
}

.topbar {
  z-index: 10;
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 56px;
  font-size: x-large;
  background-color: #5B7042;
  border-bottom: 4px solid #3F5328
}

#btmbar {
  display: inline-block;
  grid-area: btmbar;
  border-top: 2px solid darkgray;
  background-color: #F2F2F2;
}

#content {
  margin-top: 60px;
  margin-left: auto;
  margin-right: auto;
  width: 80vw;
  height: calc(100vh-60px);
}

table {
  width: 100%;
  border-collapse: collapse;
}

.labels td {
  text-align: center;
  color: black;
  padding: 16px;
  border-bottom: 1px solid gray
}

td {
  padding-top: 14px;
  padding-bottom: 14px;
  border-bottom: 0.5px solid gray
}

.name {
  display: flex;
  justify-content: start;
  align-items: center;
  font-size: 18px
}

.name .pic {
  display: inline-block;
  width: 35px;
  clip-path: circle();
  margin-right: 10px;
}

.date {
  color: gray;
  font-size: 15px
}

.comment input {
  padding: 10px;
  border-radius: 6px;
  font-size: 16px;
  width: 90%;
}

.score input {
  text-align: center;
  padding: 10px;
  border-radius: 6px;
  font-size: 16px;
  width: 50px;
}

[data-toggle="toggle"] {
  display: none;
}

.floater {
  display: none;
  position: fixed;
  bottom: 16px;
  right: 10px;
}

.floater button {
  padding: 12px;
  font-size: 18px
}


/* ----------[MODAL]---------- */

.modal {
  display: none;
  position: fixed;
  z-index: 20;
  right: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgba(0, 0, 0, 0.4);
  -webkit-animation-name: fadeIn;
  -webkit-animation-duration: 0.4s;
  animation-name: fadeIn;
  animation-duration: 0.4s
}

.confirm-window {
  display: block;
  position: fixed;
  overflow: hidden;
  width: 500px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 6px;
  background-color: white;
  transition: height 0.5s;
}

.modal-top {
  display: flex;
  justify-content: center;
  flex-direction: column;
  padding-left: 10px;
  box-sizing: border-box;
  width: 100%;
  height: 30px;
  font-size: 20px;
  color: white;
  background-color: #5B7042
}

.modal-content {
  padding: 16px;
  text-align: center;
  margin-bottom: 10px
}

.modal-content .desc {
  margin-bottom: 20px;
}

.modal label {
  display: block;
  margin-bottom: 10px;
  text-align: left;
}

.modal-controls {
  width: 100%;
  display: flex;
  justify-content: space-around;
  align-items: center;
  padding-bottom: 16px
}

.modal-controls button {
  width: 40%
}
<link rel='stylesheet' href='https://classcolonies.com/resources/style.css'>

<div class='topbar'></div>

<div id='content'>
  <form action='score.int.php' method='POST'>
    <table>
      <colgroup>
        <col width='20%'>
        <col width='20%'>
        <col width='30%'>
        <col width='10%'>
      </colgroup>
      <tbody class='labels'>
        <tr>
          <td colspan='4'>
            <label for="1">American Revolution</label>
            <input type="checkbox" id="1" data-toggle="toggle">
          </td>
        </tr>
      </tbody>
      <tbody class='hide'>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>John Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>Jane Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
      </tbody>
      <tbody class='labels'>
        <tr>
          <td colspan='4'>
            <label for="2">Vocabulary Worksheet</label>
            <input type="checkbox" id="2" data-toggle="toggle">
          </td>
        </tr>
      </tbody>
      <tbody class='hide'>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>John Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>Jane Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
      </tbody>
      <tbody class='labels'>
        <tr>
          <td colspan='4'>
            <label for="3">Interactive Notebook</label>
            <input type="checkbox" id="3" data-toggle="toggle">
          </td>
        </tr>
      </tbody>
      <tbody class='hide'>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>John Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>Jane Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
      </tbody>
    </table>
  </form>
</div>

<div class='floater'>
  <button class='button green-btn' onclick='saveScores("show");'>Record</button>
</div>


<div id="confirm" class="modal">
  <div class="confirm-window">
    <div class='modal-top'></div>
    <div class='modal-content'>
      <div class='desc'>There are scores left blank. How should these be handled?</div>
      <label>
          <input type='checkbox'>
          Reset streak
        </label>
      <label>
          <input type='checkbox'>
          Mark as zero
        </label>
      <label>
          <input type='checkbox'>
          Send back to student
        </label>
    </div>
    <div class='modal-controls'>
      <button class='button grey-btn' onclick='saveScores("hide");'>Cancel</button>
      <button class='button green-btn'>Save Scores</button>
    </div>
  </div>
</div>
Alireza Ahmadi
  • 8,579
  • 5
  • 15
  • 42
Reid
  • 43
  • 5
  • IMO the right way to do this is to use Input's `pattern` attribute, which is a regex to validate the field before the form is submitted – ControlAltDel Jul 22 '21 at 19:01
  • The usual way is just to select inputs (`$('input')`) and run them through an each loop, checking for empty values. – isherwood Jul 22 '21 at 19:01
  • @ControlAltDel well it's going to be more complex than just an alert and preventing them from submitting the form, I'm planning on presenting the user with a list of options if there are blank scores, but for the purposes of asking the question, i just left it to alert to be simple. – Reid Jul 22 '21 at 19:03
  • @isherwood How would I tell the difference between 1.) an input that is a score vs an input that is a 'comment' 2.) only inputs that are in categories that are NOT collapsed – Reid Jul 22 '21 at 19:05
  • 1) put a class on them, 2) check the ancestor container for its state. – isherwood Jul 22 '21 at 19:07
  • @isherwood how do I check an ancestor container's state? that is the part I'm not understanding – Reid Jul 22 '21 at 19:26
  • All of this is well-trodden ground. See https://stackoverflow.com/questions/17084839/check-if-any-ancestor-has-a-class-using-jquery – isherwood Jul 22 '21 at 19:32

1 Answers1

1

You can filter the array of inputs by value and filter out the hidden tbody's, like

allscoresfilled = $('tbody:not(:hidden) td.score input').filter(function() {
    return !this.value;
  }).length === 0

$(document).ready(function() {
  $('[data-toggle="toggle"]').change(function() {
    $(this).parents().next('.hide').toggle();
  });
});

$("input[type=text]").change(function() {
  $('.floater').get(0).style.display = 'block';
});

var confirm = document.getElementById("confirm");

function saveScores(arg) {
  let allscoresfilled = $('tbody:not(:hidden) td.score input').filter(function() {
    return !this.value;
  }).length === 0
 

  if (allscoresfilled) {
    document.querySelector("form").submit();
  } else {
    alert("A score is left blank!");
  }

  if (arg == "hide") {
    confirm.style.display = "none";
  }
}
window.onclick = function(event) {
  if (event.target == confirm) {
    confirm.style.display = "none";
  }
}
body {
  margin: 0;
  background-color: #F2F2F2;
}

.topbar {
  z-index: 10;
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 56px;
  font-size: x-large;
  background-color: #5B7042;
  border-bottom: 4px solid #3F5328
}

#btmbar {
  display: inline-block;
  grid-area: btmbar;
  border-top: 2px solid darkgray;
  background-color: #F2F2F2;
}

#content {
  margin-top: 60px;
  margin-left: auto;
  margin-right: auto;
  width: 80vw;
  height: calc(100vh-60px);
}

table {
  width: 100%;
  border-collapse: collapse;
}

.labels td {
  text-align: center;
  color: black;
  padding: 16px;
  border-bottom: 1px solid gray
}

td {
  padding-top: 14px;
  padding-bottom: 14px;
  border-bottom: 0.5px solid gray
}

.name {
  display: flex;
  justify-content: start;
  align-items: center;
  font-size: 18px
}

.name .pic {
  display: inline-block;
  width: 35px;
  clip-path: circle();
  margin-right: 10px;
}

.date {
  color: gray;
  font-size: 15px
}

.comment input {
  padding: 10px;
  border-radius: 6px;
  font-size: 16px;
  width: 90%;
}

.score input {
  text-align: center;
  padding: 10px;
  border-radius: 6px;
  font-size: 16px;
  width: 50px;
}

[data-toggle="toggle"] {
  display: none;
}

.floater {
  display: none;
  position: fixed;
  bottom: 16px;
  right: 10px;
}

.floater button {
  padding: 12px;
  font-size: 18px
}


/* ----------[MODAL]---------- */

.modal {
  display: none;
  position: fixed;
  z-index: 20;
  right: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgba(0, 0, 0, 0.4);
  -webkit-animation-name: fadeIn;
  -webkit-animation-duration: 0.4s;
  animation-name: fadeIn;
  animation-duration: 0.4s
}

.confirm-window {
  display: block;
  position: fixed;
  overflow: hidden;
  width: 500px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 6px;
  background-color: white;
  transition: height 0.5s;
}

.modal-top {
  display: flex;
  justify-content: center;
  flex-direction: column;
  padding-left: 10px;
  box-sizing: border-box;
  width: 100%;
  height: 30px;
  font-size: 20px;
  color: white;
  background-color: #5B7042
}

.modal-content {
  padding: 16px;
  text-align: center;
  margin-bottom: 10px
}

.modal-content .desc {
  margin-bottom: 20px;
}

.modal label {
  display: block;
  margin-bottom: 10px;
  text-align: left;
}

.modal-controls {
  width: 100%;
  display: flex;
  justify-content: space-around;
  align-items: center;
  padding-bottom: 16px
}

.modal-controls button {
  width: 40%
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel='stylesheet' href='https://classcolonies.com/resources/style.css'>

<div class='topbar'></div>

<div id='content'>
  <form action='score.int.php' method='POST'>
    <table>
      <colgroup>
        <col width='20%'>
        <col width='20%'>
        <col width='30%'>
        <col width='10%'>
      </colgroup>
      <tbody class='labels'>
        <tr>
          <td colspan='4'>
            <label for="1">American Revolution</label>
            <input type="checkbox" id="1" data-toggle="toggle">
          </td>
        </tr>
      </tbody>
      <tbody class='hide'>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>John Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>Jane Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
      </tbody>
      <tbody class='labels'>
        <tr>
          <td colspan='4'>
            <label for="2">Vocabulary Worksheet</label>
            <input type="checkbox" id="2" data-toggle="toggle">
          </td>
        </tr>
      </tbody>
      <tbody class='hide'>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>John Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>Jane Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
      </tbody>
      <tbody class='labels'>
        <tr>
          <td colspan='4'>
            <label for="3">Interactive Notebook</label>
            <input type="checkbox" id="3" data-toggle="toggle">
          </td>
        </tr>
      </tbody>
      <tbody class='hide'>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>John Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
        <tr>
          <td class='name'>
            <img class='pic' src='https://mrdansby.com/resources/pics/1.png'>
            <span>Jane Doe</span>
          </td>
          <td class='date'> Fri, Jul 22 at 3:15 PM </td>
          <td class='comment'> <input type='text'> </td>
          <td class='score'> <input type='text'> /10 </td>
        </tr>
      </tbody>
    </table>
  </form>
</div>

<div class='floater'>
  <button class='button green-btn' onclick='saveScores("show");'>Record</button>
</div>


<div id="confirm" class="modal">
  <div class="confirm-window">
    <div class='modal-top'></div>
    <div class='modal-content'>
      <div class='desc'>There are scores left blank. How should these be handled?</div>
      <label>
          <input type='checkbox'>
          Reset streak
        </label>
      <label>
          <input type='checkbox'>
          Mark as zero
        </label>
      <label>
          <input type='checkbox'>
          Send back to student
        </label>
    </div>
    <div class='modal-controls'>
      <button class='button grey-btn' onclick='saveScores("hide");'>Cancel</button>
      <button class='button green-btn'>Save Scores</button>
    </div>
  </div>
</div>
Kinglish
  • 23,358
  • 3
  • 22
  • 43
  • Thank you! This gets me a bit closer to where I am headed, but this solution checks ALL scores, and I need a solution that only checks scores of categories that are visible, not collapsed. Inputs located inside of collapsed categories should be ignored – Reid Jul 22 '21 at 19:29