2

I have a JS function here that I'm trying to fix. This function gets triggered when the Check All button is clicked. When this button is clicked, all the items in the table should be checked and disabled. When I run this function and click the Check All button, all the items are selected briefly and then they go back to being unchecked. What am I doing wrong?

PS: I've tried adding e.preventDefault() and the end of the function and it doesn't work

$(document).ready(function () {
        $('body').on('change', '#checkAll', function (e) {
            if(this.checked){
                let boxes = $("input.promo-action-checkbox");
                boxes.toArray().forEach(element => {
                    console.log(element);
                    $(element).attr('disabled', 'disabled');
                    var name = $(element).data('name');
                    var number = $(element).data('number');
                    var user = $(element).data('user');
                    $.ajax({
                        method: 'post',
                        url: '/checker',
                        contentType: "application/json",
                        data: JSON.stringify({
                            name: name,
                            number: number,
                            user: user
                        }),
                        dataType: 'json',
                        error: function () {
                            $(element).attr('disabled', '');
                        }
                        })
                    
                });

How columns are made in html table:

 var table = $('#datatable').DataTable({
    pageLength: 20,
    lengthChange: false,
    language: {
      paginate: {
        previous: '<i class=\'fas fa-angle-left\'>',
        next: '<i class=\'fas fa-angle-right\'>'
      }
    },
    ordering: true,
    processing: true,
    serverSide: true,
    ajax: {
      url: '/table/',
      dataSrc: 'results'
    },
    columns: [
      {
        data: null,
        render: function (data, type, row, meta) {
          let rowHtml = '<div class="custom-control custom-checkbox mb-3"> <input class="custom-control-input promo-action-checkbox" id="checkbox-' + meta.row + '"  data-name="' + row.name + '" data-phone="' + row.number + '" data-admin="' + row.user + '" type="checkbox"'
          if (row.fulfilled) {
            rowHtml += ' disabled checked'
          }
          rowHtml += '> <label class="custom-control-label"  for="checkbox-' + meta.row + '"></label></div>'
          return rowHtml
        }
      },
      {
        searchable: true,
        data: 'name'
      }
    ]
  });

Here is what works however the "checkAll" box does not get ticked. Also this table is not inside a form.

$('#checkAll').click(function (e) {
 e.preventDefault()
    $('input.action-checkbox').prop('checked', this.checked);  
    $('input.action-checkbox').prop('disabled', true);
    return false;
})

Table Structure:

        <div class="table-responsive">
            <table class="table align-items-center table-flush table-striped" id="datatable-promo-code">
                <thead class="thead-light">
                <tr>
                    <th>Check All <input id = "checkAll"  type= "checkbox"></th>
                    <th>Name</th>
                    <th>Info</th>
                    <th>address</th>
                    <th>Email</th>
                    <th>Number</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                </tbody>
            </table>
        </div>

  • Please add ALL the related code (HTML, CSS, and JavaScript) so that we can replicate your issue. – Scott Marcus May 27 '21 at 23:45
  • @ScottMarcus I just updated it – Angela Rubchinksy May 27 '21 at 23:54
  • Your ajax call must be failing since the `error` function is what re-enables them. You are passing `datatype: 'json'` and so there's no need to call `stringify`, jQuery does that for you, just pass the json object directly. As for why are they unchecking, no clue, there's no code that relates to that. – msg May 28 '21 at 00:08
  • For now, I don't mind them being disabled. I just want the ClickAll button to check and disable all the other buttons and stay that way until I refresh the page manually – Angela Rubchinksy May 28 '21 at 00:12
  • Check to see how many times your if(this.checked) statement is being called. I only say that because I’ve had an issue with using the JQuery On method before where it bound the event handler to the same object multiple times. – S. Walker May 28 '21 at 00:23
  • Also I only see where your setting the disabled attribute. Where do you set checked? – S. Walker May 28 '21 at 00:25
  • the if(this.checked) is called once which is correct because I only click it once – Angela Rubchinksy May 28 '21 at 00:28
  • @S.Walker using something like this $(element).attr('checked', this.checked); does not make a difference in terms of the checkbox staying checked. The problem here is the function works FINE, I have a feeling that the initial "click" is being perceived as a form and it's being submitted hence resetting the checkboxes to their default of being unchecked? – Angela Rubchinksy May 28 '21 at 00:30
  • Are they staying disabled? – S. Walker May 28 '21 at 00:31
  • @S.Walker no they are only disabled for half a second before going back to enabled – Angela Rubchinksy May 28 '21 at 00:33
  • And your sure that the AJAX call is not erroring out? The behavior described by @msg is the only thing I can see from your code that would case the behavior you describe. Try removing the $(element).attr('disabled', ''); from your AJAX error callback and see if the behavior changes. – S. Walker May 28 '21 at 00:35
  • @S.Walker when I do that, the checkboxes stay the exact same since we aren't checking them – Angela Rubchinksy May 28 '21 at 00:38
  • I don't believe this is an issue with the AJAX call since that's working fine – Angela Rubchinksy May 28 '21 at 00:39
  • They should disabled and stayed disabled. – S. Walker May 28 '21 at 00:40
  • Even if I removed the AJAX call itself, the problem persists – Angela Rubchinksy May 28 '21 at 00:40
  • When you click the check all button is the page refreshing? – S. Walker May 28 '21 at 00:41
  • The point is that your code doesn't check the inputs at any point, and also you are describing the possibility of "the click being perceived as a form". Are there any other handlers attached to the form? Please try creating a minimal runnable snippet that actually reproduces the problem. – msg May 28 '21 at 00:42
  • @msg Please check my update to this question, I have posted what works but I'm having an issue with the checkbox showing that it has been ticked. I've also included what the table looks like – Angela Rubchinksy May 28 '21 at 06:48
  • Both `preventDefault()` and `return false` prevent changing state of the `#checkAll` checkbox. – msg May 28 '21 at 07:05
  • *they are only disabled for half a second before going back to enabled* - sounds like your whole page is getting reloaded - check the network tab to see what's happening. Then remove bits of code (eg the ajax call) completely until you find exactly what's causing it. You should be able to find the cause via basic debugging techniques. If it does it with no code, then it's the `form` posting and the page refreshing. – freedomn-m May 28 '21 at 07:13
  • @freedomn-m I've actually found a workaround it, please check the end of my question because I've posted what works, however the checkAll button doesn't actually indicate that it has been ticked – Angela Rubchinksy May 28 '21 at 08:13

2 Answers2

0

Hard to tell because I can't actually run your code... but I would put a console.log into the render function just to look when it is being called. My guess would be that your table is being re-rendered before all requests done on your 'change' listener. If that is the case you need to wait all promises end before manually refreshing your page or re-rendering your table... Something like...

$(document).ready(function () {
  $('body').on('change', '#checkAll', async function (e) {
    if(this.checked) {
      let boxes = $("input.promo-action-checkbox");
      const promises = boxes.toArray().map(element => {
        return new Promise(function (resolve, reject) {
          $(element).attr('disabled', 'disabled');
          var name = $(element).data('name');
          var number = $(element).data('number');
          var user = $(element).data('user');
          $.ajax({
              method: 'post',
              url: '/checker',
              contentType: "application/json",
              data: JSON.stringify({
                  name: name,
                  number: number,
                  user: user
              }),
              dataType: 'json',
              error: function () {
                  $(element).attr('disabled', '');
                  reject();
              },
              success: function() {
                resolve()
              }
          });
        });
      });

      await Promise.all(promises);
      // refresh the page
      document.location.reload()
      // or re-render your table... no ideia how to that though...
      someMagicFunctionToReRenderYourTable()
    }
  });
});

  • Thank you for this! I'm getting a ReferenceError: promises is not defined. I'm implementing this in by the way – Angela Rubchinksy May 28 '21 at 00:50
  • Could you show the code so I can take a look? You can get this error but for that you might have put the `Promise.all` outside the `if` maybe... – Rômulo Bourget Novas May 28 '21 at 01:07
  • Please check my update to this question, I have posted what works but I'm having an issue with the checkbox showing that it has been ticked. I've also included what the table looks like – Angela Rubchinksy May 28 '21 at 06:47
0

It's not clear as to why all checkboxes need not only to be checked but also disabled. It's also not very clear as what the rest of the code is doing once that "button" is clicked. More importantly, is this <table> in a <form>? If so, then it's a possibility that the <form> resets itself (a default behavior at "submit" event)

Putting all of that aside, we can isolate the code into smaller portions. This way we can fix something (ie interactive elements like <input>) without breaking something else (ie AJAX POST request).

The example below will:

  1. Listen for the "change" event to fire only upon #checkAll
  2. Once triggered, the event handler lockCells() will run.
    $('#checkAll').on('change', lockCells);
    
  3. lockCells(event) {... passes the Event Object...
  4. ...check if #checkAll (this) is .checked or not.
    let status = this.checked ? true : false;
    
  5. ...then collect all <input> within any <table> and run the .prop() method on each of them.
     $('table input').prop(...)
    
  6. ...with the parameters of 'disabled' and 'checked' which Boolean value of true/false determined earlier at step #4
    {
      'disabled': status,
      'checked': status
    }
    

It's important to note: while method .attr() is used to manipulate attributes, .prop() is a better choice for properties especially with Boolean values (T/F). Refer to this question for details.

$('#checkAll').on('change', lockCells);

function lockCells(event) {
  let status = this.checked ? true : false;
  $('table input').prop({
    'disabled': status,
    'checked': status
  });
};
label {
  display: inline-block;
  padding: 3px 5px;
  border: 1px inset grey;
  border-radius: 6px;
  margin: 5px 10px;
  float: right;
  cursor: pointer;
}

label::before {
  content: 'Disable ';
}

#checkAll:checked+label::before {
  content: 'Enable ';
}

table input {
  display: block;
  width: 10ch;
}
<input id='checkAll' type='checkbox' hidden>
<label for='checkAll'> All Cells</label>
<table>
  <tbody>
    <tr>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
    </tr>

    <tr>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
    </tr>

    <tr>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
    </tr>

    <tr>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
    </tr>

  </tbody>
</table>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • Please check my update to this question, I have posted what works however the original checkbox does not show it has been checked, so I'm ALMOST there. I've also included what the table looks like by the way. – Angela Rubchinksy May 28 '21 at 06:47
  • There are 2 states in which you need to keep track of. You can do it prgramatically or the simpler way by using a checkbox. The new code posted is very simular to my answer. The difference being is yours goesfrom one state and stays that way whilst mine toggles from one state to the other (toggling). BTW change the "checked" value from this.checked to true. In the current conext this points to #checkAll – zer00ne May 28 '21 at 15:34
  • The problem I had with your answer was that the boxed were checked for a half second before dissapearing, with the e.preventDefault() and the return false, the changes remain but I have no idea why. Also, when I click the checkbox the box does not get visibly checked. Maybe this has to do with me using a .click(function (e)) instead of an onchange? – Angela Rubchinksy May 28 '21 at 15:46
  • In your layout the checkboxes are dynamically generated by the dataTable plugin and I assume each input has 3 data-* attributes which values are filled by a JSON obtained by AJAX. It looks as if all of this happens by clicking #checkall. Are the checkboxes generated initially at page load? Do you need to assign values tocheckboxes from a sever? – zer00ne May 28 '21 at 15:52
  • The #checkAll is hidden and the label accosiated with it looks like a button. If you wish to see it checked somply remove the hidden attribute. Do not use e.preventDefault(). Change event is an event specialized for inputs and other form controls do not use click event. Use what I posted and change only the selectior "table input" to "input.action-checkbox". – zer00ne May 28 '21 at 16:00
  • That's correct! Checkboxes are created based on the values received from the server. I've also posted the table creation and the format in my updated question – Angela Rubchinksy May 28 '21 at 16:01
  • So changing my line to "input.action-checkbox" does bring back the checkbox functionality for the checkAll however the previous problem where the checked boxes become quickly unchecked happens. This table is not in a form by the way so I'm not sure why this could be happening – Angela Rubchinksy May 28 '21 at 16:08
  • Do not fire any AJAX requests when clicking #checkAll. Was your intention to keep checkboxes data values by disabling them temporaily when you post to the server?? – zer00ne May 28 '21 at 16:10
  • I'm not firing any AJAX requests, I'm using basically exactly what you've wrote but my intention was to disable each one and send each one's data to the server however I would like to just get the basic functionality of the checkbox showing it has been checked along with all the other check boxes being disabled accordingly – Angela Rubchinksy May 28 '21 at 16:13
  • It might be the dataTable plugin regenerating the whole table on each change or click event. Move #checkAll outside of table. – zer00ne May 28 '21 at 16:15
  • WOW! That is correct and now it works, thank you so much. You are a life saver :) I was wondering would it be possible for me to add it into the table since it needs to be in the same row as the other table headers? – Angela Rubchinksy May 28 '21 at 16:20
  • That's great! If you need help placing it within the twble post a new question because dataTable needs to be negotiated. – zer00ne May 28 '21 at 16:26