Search code examples
sveltesvelte-3

limiting the number of checckbox checked in Svelte


I know how to do it in js, example :

    <form id="world" name="world">
        <input type="checkbox" name="rGroup" value="1" id="r1" name="plan" />
        <label class="whatever" for="r1"></label>
        <input type="checkbox" name="rGroup" value="2" id="r2" name="plan"/>
        <label class="whatever" for="r2"></label>
        <input type="checkbox" name="rGroup" value="3" id="r3" name="plan"/>
        <label class="whatever" for="r3"></label>
    </form>

    function checkboxlimit(checkgroup, limit){
      for (var i=0; i<checkgroup.length; i++){
        checkgroup[i].addEventListener('click',function(){
          var checkedcount=0;
          for (var i=0; i<checkgroup.length; i++)
          {
            checkedcount+=(checkgroup[i].checked)? 1 : 0;
            if (checkedcount>limit)
            {
              alert("You can only select a maximum of "+limit+" checkboxes");
              this.checked=false;
            }
          }
        });
      }
    }
    
    checkboxlimit(document.forms.world.rGroup, 2);

But I'm curious if there is a svelte way? I following have my checkbox selections :

    {#each option.optiongroupitems as optionitem, index}
     
      <li style="list-style : none;">
       {index}
        <label>
          <input id={index}  type="checkbox" bind:group={values.options} value={[{"name" : optionitem.name},{ "price" : optionitem.price }, {"index" : index} ]}  on:click={()=>{getvalues(index, optionitem.name, optionitem.price, option.groupheader,  option.rules, option.rulesnumber.number)}}>
           {optionitem.name} : {optionitem.price}
     </label>
    
      </li>
      {/each}

The option.rulesnumber.number is the number of options used to limit selection (for example it might contain the number 2 so user can only choose 2 options)

getvalues() is just a function that pushed the selection values to an array. Is there a svelte way using class where I can prevent user from clicking on checkbox if the user reaches the limit of his selection?

I'm not asking about jquery or js. Just svelte way to do it. If there is any.


Solution

  • From a usability point of view I would disable the remaining checkboxes if the maximal number is reached. This could be done like this REPL

    <script>
        const options = [
            'option 1',
            'option 2',
            'option 3',
            'option 4'
        ]
        const max = 2
        let selectedOptions = []
    </script>
    
    <h1>
        Choose max. {max} options
    </h1>
    
    {#each options as option, index}
    <div class="option">
        <input type=checkbox
                     bind:group={selectedOptions}
                     name="options"
                     value={option}
                     id="option{index}"
                     disabled={selectedOptions.length === max && !selectedOptions.includes(option)}
                     >
        <label for="option{index}">{option}</label>
    </div>
    {/each}
    
    <style>
        .option {
            display: flex;
            align-items: center;
        }
        input {
            margin: 0 .3rem 0 0;
        }
        input:disabled+:global(label) {
            color: lightgrey;
        }
        label {
            user-select: none;
        }
    </style>
    

    This might be an alternative where the last checkbox is switched like a radio button, when the maximal amount is reached REPL

    <script>
        const options = [
            'option 1',
            'option 2',
            'option 3',
            'option 4',
            'option 5'
        ]
        const max = 2
        let selectedOptions = []
        let checked = {}
    
        function handleInput(event) {
            const value = event.target.value
            if(event.target.checked) {
                if(selectedOptions.length === max) {
                    const last = selectedOptions.pop()
                    delete checked[last]
                }
                selectedOptions.push(value) 
            }else {
                selectedOptions = selectedOptions.filter(option => option !== value)
            }
        }
    </script>
    
    <h1>
        Choose max. {max} options
    </h1>
    
    {#each options as option, index}
    <div class="option">
        <input type=checkbox
                     name="options"
                     value={option}
                     id="option{index}"
                     on:input={handleInput}
                     bind:checked={checked[option]}
                     >
        <label for="option{index}">{option}</label>
    </div>
    {/each}
    
    <style>
        .option {
            display: flex;
            align-items: center;
        }
        input {
            margin: 0 .3rem 0 0;
        }
        input:disabled+:global(label) {
            color: lightgrey;
        }
        label {
            user-select: none;
        }
    </style>