skip to Main Content

I have list of many checkboxes and I need to display the order of them being checked next to each other.
Something like this:

[1] checkbox
    checkbox
    checkbox
[2] checkbox
    checkbox
[3] checkbox

Order in which they are being checked does not matter, the thing is that they need to be ordered from top to bottom as showed.

I have limited options with editing the HTML, since it is dinamically rendered, and the structure looks like this:

<td>
   <label>
      <input type="checkbox">
      "Sample text"
   </label>
</td>
<td>
   <label>
      <input type="checkbox">
      "Sample text"
   </label>
</td>
<td>
   <label>
      <input type="checkbox">
      "Sample text"
   </label>
</td>
etc.

So I have tried the following:

$('input[type=checkbox]').on('change', function(){
    var number = $('input[type=checkbox]:checked').length;
    $('label:has(input[type=checkbox]:checked)').text(number);
});

But it ends up replacing each label contents with 1 (not even a count).

I searched for answers here on Stackoverflow and I found the most suitable as this one:

document.querySelectorAll('td').forEach(el => {
  el.innerHTML += '<span class="letter"> </span>'
})
let checkedItems=[]
document.querySelectorAll('[type=checkbox]').forEach(el => {
el.value = el.closest('label').innerText.trim()
  el.addEventListener('change', e => {
    let n = el.closest('label').innerText.trim();
    if (!e.target.checked)  checkedItems.splice(checkedItems.indexOf(n),1)
    else checkedItems.push(n);
    document.querySelectorAll('.letter').forEach( l=> l.innerHTML = '')
    checkedItems.forEach((n,i) => {
    document.querySelector(`input[value=${n}]`).closest('td').querySelector('.letter').innerHTML = i;
    })
 
  });

});

In this case I get an error caused by the input value, since it is not alphanumeric. In what way can I edit either of these in order to work? Thank you!

EDIT: I have found the problem in the last code block in my question > it was in the input[value=${n}], since it did not match my input values, I just did not spend that much time in dev tools to see this before asking

3

Answers


  1. If I understood correctly that you wish to display, next to the checkbox itself, the order ( out of all checkboxes ) that a particular checkbox was checked then perhaps the following might work?

    querySelectorAll can be combined with the :checked attribute selector to find the number of checked elements and this is then used within a new span element which is appeneded to the label element when the delegated event listener fires a changed event.

    update
    per the comment by @janPfiefer, a resequencing function could be added if required to re-index the checked order should all checkboxes be checked and then some unchecked in random sequence.

    const resequence=()=>{
      let col=document.querySelectorAll('table [type="checkbox"]:checked');
          col.forEach(n=>{
            let span=n.parentNode.querySelector('span');
                span.textContent=Math.max( 1, parseInt( span.textContent ) - 1 );
          })
    };
    
    document.addEventListener('change',e=>{
      if( e.target instanceof HTMLInputElement && e.target.type=='checkbox' ){
        let index=document.querySelectorAll('table [type="checkbox"]:checked').length;
        let label=e.target.parentNode;
        
        if( e.target.checked ){
          let span=document.createElement('span');
              span.textContent=index;
          label.appendChild( span )
        }else{
          label.removeChild( label.querySelector('span') );
          resequence();
        }
      }
    });
    <table>
      <tr>
        <td>
          <label>
          <input type="checkbox">
          "Sample text"
       </label>
        </td>
        <td>
          <label>
          <input type="checkbox">
          "Sample text"
       </label>
        </td>
        <td>
          <label>
          <input type="checkbox">
          "Sample text"
       </label>
        </td>
      </tr>
    </table>
    Login or Signup to reply.
  2. Add span as a container for a number:

    <label>
        <input type="checkbox">
        <span class="nr"></span>Text X
    </label>
    

    Then script will be:

    const cbs = $("input[type=checkbox]");
          cbs.click(function() {
              let order = 0;
    
              cbs.each(function(i,o) {
                  const nr = $(o).next();
                  if(o.checked) {
                      order++;
                      nr.html(order);
                  } else {
                      nr.html("");
                  }
              });
            });
    
    Login or Signup to reply.
  3. Introduction and assumptions:

    I edited the html putting the <input> before the <label> and no more nested inside it as before. I did it because the label should not contain the input element and should be a distinct element and also because I needed an after element to style following the checkbox. It could be a wrong assumption if you needed the html to remain exactly the same. Anyway there’s a further attempt later that will also address the original html.

    So we have this:

    <td>
      <input type="checkbox">
      <label>Sample text</label>
    </td>
    

    Instead of this:

    <td>
      <label>
        <input type="checkbox">
        "Sample text"
      </label>
    </td>
    

    Showing table columns as rows instead:

    You may simply have a css rule addressing the <tr> elements and turn them as flex container with a column direction:

    tr{
      display: flex;  
      flex-direction: column;
    }
    

    Using CSS counters:

    It could be worth knowing that you could achieve the same result you are chasing with js, but using css only with css rulesets like these:

    body{
      counter-reset: checked-cb;
    }
    
    input[type="checkbox"]:checked{
      counter-increment: checked-cb;
    }
    
    input[type="checkbox"]:checked + label::after{
      content: " " counter(checked-cb);
    }
    

    Where a counter checked-cb is incremented at each input checked and echoed in an ::after pseudo element inside the label following.

    Using the same css approach with the original html:

    Since <input> elements are self contained they cannot host ::after elements so we are forced to style its parent because there are no other sibling elements to adopt as container like we had in the example before where I made the html a little better.

    Unfortunately the only css selector to get there requires the :has() psudo class currently still not supported on Firefox:

    *:has(> input[type="checkbox"]:checked)::after{ 
      content: counter(checked-cb);
    }
    

    References:

    https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Counter_Styles/Using_CSS_counters

    CSS counters let you adjust the appearance of content based on its
    location in a document. For example, you can use counters to
    automatically number the headings in a webpage, or to change the
    numbering on ordered lists.

    Counters are, in essence, variables maintained by CSS whose values may
    be incremented or decremented by CSS rules that track how many times
    they’re used. You can define your own named counters, and you can also
    manipulate the list-item counter that is created by default for all
    ordered lists.

    https://developer.mozilla.org/en-US/docs/Web/CSS/:has

    The functional :has() CSS pseudo-class represents an element if any of
    the relative selectors that are passed as an argument match at least
    one element when anchored against this element. This pseudo-class
    presents a way of selecting a parent element or a previous sibling
    element with respect to a reference element by taking a relative
    selector list as an argument.

    Demo (w/ html improved):

    body {
      counter-reset: checked-cb;
    }
    
    input[type="checkbox"]:checked {
      counter-increment: checked-cb;
    }
    
    input[type="checkbox"]:checked+*::after {
      content: " " counter(checked-cb);
    }
    
    tr {
      display: flex;
      flex-direction: column;
    }
    <table class="showasrows">
      <tr>
        <td>
          <input type="checkbox">
          <label>Sample text</label>
        </td>
        <td>
          <input type="checkbox">
          <label>Sample text</label>
        </td>
        <td>
          <input type="checkbox">
          <label>Sample text</label>
        </td>
      </tr>
    </table>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search