This is a fragment of a table with amounts to be loaded into the database. When loading (in the parameters of the database stored procedure), each bank has its own parameter, which is passed in the checkbox attribute: value. I need that when clicking on one of the checkboxes, the jackdaw should be set or removed on all checkboxes with this value (and when removing the daws from all checkboxes with the same value, the cell color is highlighted in red, and if the checkbox is checked again, the cell background becomes white). I implemented it through the attributes in the clickbox on the checkbox:

function have_check(elem) { var param_value = elem.value; var elems_group = document.querySelectorAll('input[value=\'' + param_value + '\']'); if (elem.hasAttribute('checked')) { for (var i = elems_group.length - 1; i >= 0; i--) { elems_group[i].removeAttribute('checked'); elems_group[i].parentNode.style.backgroundColor = 'red'; } } else { for (var i = elems_group.length - 1; i >= 0; i--) { elems_group[i].parentNode.style.backgroundColor = 'white'; elems_group[i].setAttribute('checked', true); } } } 
 <table> <tr> <th>Дата</th> <th>Сборщик</th> <th>Сумма</th> <th>Загрузить</th> </tr> <tr> <td>13.05.2018</td> <td>Сбербанк</td> <td>337.35</td> <td><input type="checkbox" value="1_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>18.05.2018</td> <td>Сбербанк</td> <td>248.9</td> <td><input type="checkbox" value="1_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>21.05.2018</td> <td>Сбербанк</td> <td>717.6</td> <td><input type="checkbox" value="1_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>22.05.2018</td> <td>Сбербанк</td> <td>723.45</td> <td><input type="checkbox" value="1_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>22.05.2018</td> <td>Альфабанк</td> <td>723.45</td> <td><input type="checkbox" value="2_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>24.05.2018</td> <td>Альфабанк</td> <td>655.13</td> <td><input type="checkbox" value="2_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>21.05.2018</td> <td>ВТБ-24</td> <td>356.19</td> <td><input type="checkbox" value="3_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>23.05.2018</td> <td>ВТБ-24</td> <td>554.20</td> <td><input type="checkbox" value="3_2" onclick="have_check(this)" checked></td> </tr> </table> 

Everything would be fine, but if you consistently click on the 1st, then the 2nd, then the 3rd and then the 4th checkbox, then you can see that the flags in the group with value = '1_2' begin to randomly stand out and be removed. In general, it does not work as it should, although the cell color changes correctly.

Tell me, please, can this simultaneous setting / clearing of checkboxes for a group be done not through an attribute, but through a property? Or in the code I have some errors, because of which the program behaves differently than we would like?

  • format so that you can read - Mykola Veriga
  • "... then you can see that the flags ..." is not visible (((( - MedvedevDev

2 answers 2

In short, the problem is that you are trying to work with the state not through a property , but through an attribute .

Let's go through the steps and see what happens. So that the color does not confuse you, turn off its change and monitor only the state of checkboxes.

Before you start, you should talk about two important points that for some reason are not covered in the Russian-speaking segment of the Internet.

State flag

According to the specification, the input has a boolean flag of the changed state ( dirty checkedness flag ), something like a seal, by which it is possible to check whether the user has changed the state of the checkbox. When the page loads, it is false.

HTML attribute ≠ DOM property

Elements have properties and attributes, they are different entities. Often, their values ​​are related to each other, but, there are many but. For example, the id attribute is associated with the id property, so you can write elem.id = 'newId' and see the changes in the DOM. However, editing the text in <input type="text"> , you will not notice changes in the value attribute, since it is synchronized with the defaultValue property.

And finally, the main thing.

The state of the element is reflected through the property . In the case of the checkbox, the current state ( on / off ) can be obtained and recorded by referring to the elem.checked property. Let's take a look at how your code works “on attributes” and how it can be rewritten “on properties”.


So, we successively call out 4 Sberbank checkboxes.

  1. In your case, first click on the first checkbox, changing the state and the dirty checkedness flag (it becomes true). And only then does your function. The preliminary result before executing the function:

    ☑️🏳 ☑️🏳 ☑️🏳

    1.1. In the function, you check whether the element has the checked attribute, mistakenly assuming that the change in the input state should also affect the attribute, but such a dependency is simply not provided. You can verify this by creating the usual <input type="checkbox" checked> , clicking on it and checking in the inspector what became of the attribute (spoiler: nothing). The result: the check for the presence of the attribute is successful, because it was originally in the code.

    1.2. Further, by code, you remove the checked attribute from inputs with the same value. Again, we ’ll refer to the specification : when removing the checked attribute, the browser should change the state to false , but only if the changed state flag is false . Total:

    ⬜️🏳 ⬜️🏳 ⬜️🏳

  2. Now you click on the second checkbox. Its state changes to the marked one, the changed state flag becomes true . Preliminary result:

    ☑️🚩 ⬜️🏳 ⬜️🏳

    2.1. Since all checkboxes of Sberbank have the checked attribute in clause 1.2, in the function you submit the else condition.

    2.2. Here checkboxes are set to the checked attribute. When an attribute is added, the changed state flag must also be checked , so the browser will change the state to on only those 2 checkboxes whose flag is still false . Total:

    ☑️🚩 ☑️🏳 ☑️🏳

  3. Click on the third checkbox. Its status becomes off , and the dirty checkedness flag is true . Preliminary result:

    ☑️🚩 ⬜️🚩 ☑️🏳

    3.1. We successfully pass the condition in the function, because we added attributes in section 2.2.

    3.2. We remove checkbox attribute checked . The browser changes the state to off only on the 4th checkbox, since the rest of the flags are true. Total:

    ⬜️🚩☑️🚩⬜️🚩⬜️🏳

  4. Click on the fourth checkbox: it was off , it became on . The altered state flag also becomes true . Preliminary result:

    ⬜️🚩☑️🚩⬜️🚩☑️🚩

    4.1. We fall into the else condition, since the attributes are removed in Section 3.2.

    4.2. Add checkbox checked attribute. The browser looks at the input, all have true flags - it spreads its hands, it does not switch anything. Total:

    ⬜️🚩☑️🚩⬜️🚩☑️🚩


We work with the state through the properties

Instead of elem.hasAttribute we will get the current state of the checkbox by accessing the elem.checked property. To change the state, we will overwrite the same property.

Also, for brevity, I made a few less important changes:

  • instead of passing this element, it is retrieved from event.target
  • instead of a loop for search, elements are implemented through the array method map (but querySelectorAll does not return an array, but an object of type NodeList , we will lend the method to the array prototype);
  • instead of if used ternary operator .

 function have_check() { var elem = event.target; var elems_group = document.querySelectorAll('input[value="' + elem.value + '"]'); Array.prototype.map.call(elems_group, function(checkbox) { checkbox.checked = elem.checked; checkbox.parentNode.style.backgroundColor = elem.checked ? 'white' : 'red'; }); } 
 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>JS Bin</title> </head> <body> <table> <tr> <th>Дата</th> <th>Сборщик</th> <th>Сумма</th> <th>Загрузить</th> </tr> <tr> <td>13.05.2018</td> <td>Сбербанк</td> <td>337.35</td> <td> <input type="checkbox" value="1_2" onclick="have_check()" checked> </td> </tr> <tr> <td>18.05.2018</td> <td>Сбербанк</td> <td>248.9</td> <td> <input type="checkbox" value="1_2" onclick="have_check()" checked> </td> </tr> <tr> <td>21.05.2018</td> <td>Сбербанк</td> <td>717.6</td> <td> <input type="checkbox" value="1_2" onclick="have_check()" checked> </td> </tr> <tr> <td>22.05.2018</td> <td>Сбербанк</td> <td>723.45</td> <td> <input type="checkbox" value="1_2" onclick="have_check()" checked> </td> </tr> <tr> <td>22.05.2018</td> <td>Альфабанк</td> <td>723.45</td> <td> <input type="checkbox" value="2_2" onclick="have_check()" checked> </td> </tr> <tr> <td>24.05.2018</td> <td>Альфабанк</td> <td>655.13</td> <td> <input type="checkbox" value="2_2" onclick="have_check()" checked> </td> </tr> <tr> <td>21.05.2018</td> <td>ВТБ-24</td> <td>356.19</td> <td> <input type="checkbox" value="3_2" onclick="have_check()" checked> </td> </tr> <tr> <td>23.05.2018</td> <td>ВТБ-24</td> <td>554.20</td> <td> <input type="checkbox" value="3_2" onclick="have_check()" checked> </td> </tr> </table> </body> </html> 

    Try not to put an attribute, but a slightly different method.

    elem.checked = true | false

      function have_check(elem) { var param_value = elem.value; var elems_group = document.querySelectorAll('input[value=\'' + param_value + '\']'); if (elem.checked) { for (var i = elems_group.length - 1; i >= 0; i--) { elems_group[i].checked = true; elems_group[i].parentNode.style.backgroundColor = 'white'; } } else { for (var i = elems_group.length - 1; i >= 0; i--) { elems_group[i].parentNode.style.backgroundColor = 'red'; elems_group[i].checked = false; } } } 
      <table> <tbody> <tr> <th>Дата</th> <th>Сборщик</th> <th>Сумма</th> <th>Загрузить</th> </tr> <tr> <td>13.05.2018</td> <td>Сбербанк</td> <td>337.35</td> <td><input type="checkbox" name='1_2' value="1_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>18.05.2018</td> <td>Сбербанк</td> <td>248.9</td> <td><input type="checkbox" name='1_2' value="1_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>21.05.2018</td> <td>Сбербанк</td> <td>717.6</td> <td><input type="checkbox" name='1_2' value="1_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>22.05.2018</td> <td>Сбербанк</td> <td>723.45</td> <td><input type="checkbox" name='1_2' value="1_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>22.05.2018</td> <td>Альфабанк</td> <td>723.45</td> <td><input type="checkbox" value="2_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>24.05.2018</td> <td>Альфабанк</td> <td>655.13</td> <td><input type="checkbox" value="2_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>21.05.2018</td> <td>ВТБ-24</td> <td>356.19</td> <td><input type="checkbox" value="3_2" onclick="have_check(this)" checked></td> </tr> <tr> <td>23.05.2018</td> <td>ВТБ-24</td> <td>554.20</td> <td><input type="checkbox" value="3_2" onclick="have_check(this)" checked></td> </tr> </tbody> </table> 

    • Thanks, helped! Could you briefly at least explain why attributes behave so strangely? After all, the jackdaw is installed, and in the console in the browser I display the html-code of the checkbox and see that the attribute for the element is set, and the click handler ignores it. Maybe you will recommend some article with a more detailed description of this feature attributes and properties? For the future: working with checkboxes, is it better to use properties, and not attributes? - Galka
    • In general, I could not understand exactly, but changing the attribute through setattribute is not desirable, you already understood it. And even when you put an attribute, also by standards you only need to put checked = "checked". And what else I found is when you change an attribute through an attribute, you change the state but not the html value of the element. Here are the links bugzilla.mozilla.org/show_bug.cgi?id=327020 , w3.org/TR/DOM-Level-2-HTML/html.html#ID-30233917 , stackoverflow.com/questions/10832640/… - Dima Kaukin