Home > Blockchain >  Display order of checked checkboxes next to each checkbox
Display order of checked checkboxes next to each checkbox

Time:01-11

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 > </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!

CodePudding user response:

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>

CodePudding user response:

Add span as a container for a number:

<label>
    <input type="checkbox">
    <span ></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("");
              }
          });
        });

CodePudding user response:

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 >
  <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>

  • Related