Dynamic limit of Checkbox Group selection












0















I have a checkboxGroupInput with 4 choices (A, B, C, D).

I want to limit the allowed selection to 2 options.



The user is allowed to select a 3rd option.

But in that case only the new (3rd) and last (2nd) options that were selected should stay checked.



For example, if a user selects B, then D, and then A -

the outcome should be only D and A checked.



I'm trying to implement this logic in JS, since it doesn't work properly in R/Shiny.

The main reason is that updating the input doesn't occur immediately.

(it is delayed by other invalidations in my original Shiny app, that take quite some time)



Minimal example:



library(shiny)

shinyApp(
ui = fluidPage(
tags$script(
"JS code here..."
),

checkboxGroupInput(
inputId = "the_checkbox",
label = "Checkbox",
choices = c("A", "B", "C", "D")
)
),

server = function(input, output, session) {}
)









share|improve this question



























    0















    I have a checkboxGroupInput with 4 choices (A, B, C, D).

    I want to limit the allowed selection to 2 options.



    The user is allowed to select a 3rd option.

    But in that case only the new (3rd) and last (2nd) options that were selected should stay checked.



    For example, if a user selects B, then D, and then A -

    the outcome should be only D and A checked.



    I'm trying to implement this logic in JS, since it doesn't work properly in R/Shiny.

    The main reason is that updating the input doesn't occur immediately.

    (it is delayed by other invalidations in my original Shiny app, that take quite some time)



    Minimal example:



    library(shiny)

    shinyApp(
    ui = fluidPage(
    tags$script(
    "JS code here..."
    ),

    checkboxGroupInput(
    inputId = "the_checkbox",
    label = "Checkbox",
    choices = c("A", "B", "C", "D")
    )
    ),

    server = function(input, output, session) {}
    )









    share|improve this question

























      0












      0








      0








      I have a checkboxGroupInput with 4 choices (A, B, C, D).

      I want to limit the allowed selection to 2 options.



      The user is allowed to select a 3rd option.

      But in that case only the new (3rd) and last (2nd) options that were selected should stay checked.



      For example, if a user selects B, then D, and then A -

      the outcome should be only D and A checked.



      I'm trying to implement this logic in JS, since it doesn't work properly in R/Shiny.

      The main reason is that updating the input doesn't occur immediately.

      (it is delayed by other invalidations in my original Shiny app, that take quite some time)



      Minimal example:



      library(shiny)

      shinyApp(
      ui = fluidPage(
      tags$script(
      "JS code here..."
      ),

      checkboxGroupInput(
      inputId = "the_checkbox",
      label = "Checkbox",
      choices = c("A", "B", "C", "D")
      )
      ),

      server = function(input, output, session) {}
      )









      share|improve this question














      I have a checkboxGroupInput with 4 choices (A, B, C, D).

      I want to limit the allowed selection to 2 options.



      The user is allowed to select a 3rd option.

      But in that case only the new (3rd) and last (2nd) options that were selected should stay checked.



      For example, if a user selects B, then D, and then A -

      the outcome should be only D and A checked.



      I'm trying to implement this logic in JS, since it doesn't work properly in R/Shiny.

      The main reason is that updating the input doesn't occur immediately.

      (it is delayed by other invalidations in my original Shiny app, that take quite some time)



      Minimal example:



      library(shiny)

      shinyApp(
      ui = fluidPage(
      tags$script(
      "JS code here..."
      ),

      checkboxGroupInput(
      inputId = "the_checkbox",
      label = "Checkbox",
      choices = c("A", "B", "C", "D")
      )
      ),

      server = function(input, output, session) {}
      )






      javascript jquery r checkbox shiny






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 23 '18 at 15:34









      yanirmoryanirmor

      205212




      205212
























          1 Answer
          1






          active

          oldest

          votes


















          1














          Note: The HTML you produce likely looks different, but the JS you need is most certainly the same. Everything is written in vanilla JS, in case you usually use jQuery, all you need to change is the "activation" code at the end.



          Given this HTML:



          <div class="inputgroup">
          <div class="checkbox">
          <input type="checkbox" name="check1" value="A" id="c1">
          <label for="c1">A</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check2" value="B" id="c2">
          <label for="c2">B</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check3" value="C" id="c3">
          <label for="c3">C</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check4" value="D" id="c4">
          <label for="c4">D</label>
          </div>
          </div>


          The JS code to do it is pretty simple (plenty of comments to explain what's going on):



          function justTwo (checkboxGroup) {
          // checkboxGroup is the <div class="inputgroup"> node
          // ---------

          // step 1: grab all <input> elements inside the group
          var boxes = Array.prototype.slice.call(checkboxGroup.querySelectorAll('input'));

          // step 2: create a list, where nodes which are checked are stored
          var checked = ;

          // step 3: create a function which unchecks boxes from the beginning
          // of the list if a third checkbox is checked
          function handleCheckedChange (event) {
          if (event.target.checked) { // if the user checked the box...
          if (checked.length >= 2) { // ... and two or more boxes are checked already ...
          var fst = checked.shift(); // ... take the first/oldest checked ...
          fst.checked = null; // ... uncheck it ...
          }
          checked.push(event.target); // ... and save the reference to the newly checked
          } else { // if he unchecked a box ...
          checked = checked.filter(function (box) { // ... remove possible references
          return box !== event.target;
          });
          }
          }

          // step 4: make every <input> "listen" to check-changes
          boxes.forEach(function (box) {
          box.addEventListener('change', handleCheckedChange);
          });
          }


          You then have to "activate" it on each checkbox group. This is the part I know nothing about. Hopefully it helps anyway :)



          justTwo(document.querySelector('.inputgroup'));


          Or with jQuery:



          $('.inputgroup').each(function () { justTwo(this); });





          share|improve this answer
























          • Thank you so much! It worked very well with a little adaptation to Shiny.

            – yanirmor
            Nov 27 '18 at 19:11











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53449412%2fdynamic-limit-of-checkbox-group-selection%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          Note: The HTML you produce likely looks different, but the JS you need is most certainly the same. Everything is written in vanilla JS, in case you usually use jQuery, all you need to change is the "activation" code at the end.



          Given this HTML:



          <div class="inputgroup">
          <div class="checkbox">
          <input type="checkbox" name="check1" value="A" id="c1">
          <label for="c1">A</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check2" value="B" id="c2">
          <label for="c2">B</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check3" value="C" id="c3">
          <label for="c3">C</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check4" value="D" id="c4">
          <label for="c4">D</label>
          </div>
          </div>


          The JS code to do it is pretty simple (plenty of comments to explain what's going on):



          function justTwo (checkboxGroup) {
          // checkboxGroup is the <div class="inputgroup"> node
          // ---------

          // step 1: grab all <input> elements inside the group
          var boxes = Array.prototype.slice.call(checkboxGroup.querySelectorAll('input'));

          // step 2: create a list, where nodes which are checked are stored
          var checked = ;

          // step 3: create a function which unchecks boxes from the beginning
          // of the list if a third checkbox is checked
          function handleCheckedChange (event) {
          if (event.target.checked) { // if the user checked the box...
          if (checked.length >= 2) { // ... and two or more boxes are checked already ...
          var fst = checked.shift(); // ... take the first/oldest checked ...
          fst.checked = null; // ... uncheck it ...
          }
          checked.push(event.target); // ... and save the reference to the newly checked
          } else { // if he unchecked a box ...
          checked = checked.filter(function (box) { // ... remove possible references
          return box !== event.target;
          });
          }
          }

          // step 4: make every <input> "listen" to check-changes
          boxes.forEach(function (box) {
          box.addEventListener('change', handleCheckedChange);
          });
          }


          You then have to "activate" it on each checkbox group. This is the part I know nothing about. Hopefully it helps anyway :)



          justTwo(document.querySelector('.inputgroup'));


          Or with jQuery:



          $('.inputgroup').each(function () { justTwo(this); });





          share|improve this answer
























          • Thank you so much! It worked very well with a little adaptation to Shiny.

            – yanirmor
            Nov 27 '18 at 19:11
















          1














          Note: The HTML you produce likely looks different, but the JS you need is most certainly the same. Everything is written in vanilla JS, in case you usually use jQuery, all you need to change is the "activation" code at the end.



          Given this HTML:



          <div class="inputgroup">
          <div class="checkbox">
          <input type="checkbox" name="check1" value="A" id="c1">
          <label for="c1">A</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check2" value="B" id="c2">
          <label for="c2">B</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check3" value="C" id="c3">
          <label for="c3">C</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check4" value="D" id="c4">
          <label for="c4">D</label>
          </div>
          </div>


          The JS code to do it is pretty simple (plenty of comments to explain what's going on):



          function justTwo (checkboxGroup) {
          // checkboxGroup is the <div class="inputgroup"> node
          // ---------

          // step 1: grab all <input> elements inside the group
          var boxes = Array.prototype.slice.call(checkboxGroup.querySelectorAll('input'));

          // step 2: create a list, where nodes which are checked are stored
          var checked = ;

          // step 3: create a function which unchecks boxes from the beginning
          // of the list if a third checkbox is checked
          function handleCheckedChange (event) {
          if (event.target.checked) { // if the user checked the box...
          if (checked.length >= 2) { // ... and two or more boxes are checked already ...
          var fst = checked.shift(); // ... take the first/oldest checked ...
          fst.checked = null; // ... uncheck it ...
          }
          checked.push(event.target); // ... and save the reference to the newly checked
          } else { // if he unchecked a box ...
          checked = checked.filter(function (box) { // ... remove possible references
          return box !== event.target;
          });
          }
          }

          // step 4: make every <input> "listen" to check-changes
          boxes.forEach(function (box) {
          box.addEventListener('change', handleCheckedChange);
          });
          }


          You then have to "activate" it on each checkbox group. This is the part I know nothing about. Hopefully it helps anyway :)



          justTwo(document.querySelector('.inputgroup'));


          Or with jQuery:



          $('.inputgroup').each(function () { justTwo(this); });





          share|improve this answer
























          • Thank you so much! It worked very well with a little adaptation to Shiny.

            – yanirmor
            Nov 27 '18 at 19:11














          1












          1








          1







          Note: The HTML you produce likely looks different, but the JS you need is most certainly the same. Everything is written in vanilla JS, in case you usually use jQuery, all you need to change is the "activation" code at the end.



          Given this HTML:



          <div class="inputgroup">
          <div class="checkbox">
          <input type="checkbox" name="check1" value="A" id="c1">
          <label for="c1">A</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check2" value="B" id="c2">
          <label for="c2">B</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check3" value="C" id="c3">
          <label for="c3">C</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check4" value="D" id="c4">
          <label for="c4">D</label>
          </div>
          </div>


          The JS code to do it is pretty simple (plenty of comments to explain what's going on):



          function justTwo (checkboxGroup) {
          // checkboxGroup is the <div class="inputgroup"> node
          // ---------

          // step 1: grab all <input> elements inside the group
          var boxes = Array.prototype.slice.call(checkboxGroup.querySelectorAll('input'));

          // step 2: create a list, where nodes which are checked are stored
          var checked = ;

          // step 3: create a function which unchecks boxes from the beginning
          // of the list if a third checkbox is checked
          function handleCheckedChange (event) {
          if (event.target.checked) { // if the user checked the box...
          if (checked.length >= 2) { // ... and two or more boxes are checked already ...
          var fst = checked.shift(); // ... take the first/oldest checked ...
          fst.checked = null; // ... uncheck it ...
          }
          checked.push(event.target); // ... and save the reference to the newly checked
          } else { // if he unchecked a box ...
          checked = checked.filter(function (box) { // ... remove possible references
          return box !== event.target;
          });
          }
          }

          // step 4: make every <input> "listen" to check-changes
          boxes.forEach(function (box) {
          box.addEventListener('change', handleCheckedChange);
          });
          }


          You then have to "activate" it on each checkbox group. This is the part I know nothing about. Hopefully it helps anyway :)



          justTwo(document.querySelector('.inputgroup'));


          Or with jQuery:



          $('.inputgroup').each(function () { justTwo(this); });





          share|improve this answer













          Note: The HTML you produce likely looks different, but the JS you need is most certainly the same. Everything is written in vanilla JS, in case you usually use jQuery, all you need to change is the "activation" code at the end.



          Given this HTML:



          <div class="inputgroup">
          <div class="checkbox">
          <input type="checkbox" name="check1" value="A" id="c1">
          <label for="c1">A</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check2" value="B" id="c2">
          <label for="c2">B</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check3" value="C" id="c3">
          <label for="c3">C</label>
          </div>
          <div class="checkbox">
          <input type="checkbox" name="check4" value="D" id="c4">
          <label for="c4">D</label>
          </div>
          </div>


          The JS code to do it is pretty simple (plenty of comments to explain what's going on):



          function justTwo (checkboxGroup) {
          // checkboxGroup is the <div class="inputgroup"> node
          // ---------

          // step 1: grab all <input> elements inside the group
          var boxes = Array.prototype.slice.call(checkboxGroup.querySelectorAll('input'));

          // step 2: create a list, where nodes which are checked are stored
          var checked = ;

          // step 3: create a function which unchecks boxes from the beginning
          // of the list if a third checkbox is checked
          function handleCheckedChange (event) {
          if (event.target.checked) { // if the user checked the box...
          if (checked.length >= 2) { // ... and two or more boxes are checked already ...
          var fst = checked.shift(); // ... take the first/oldest checked ...
          fst.checked = null; // ... uncheck it ...
          }
          checked.push(event.target); // ... and save the reference to the newly checked
          } else { // if he unchecked a box ...
          checked = checked.filter(function (box) { // ... remove possible references
          return box !== event.target;
          });
          }
          }

          // step 4: make every <input> "listen" to check-changes
          boxes.forEach(function (box) {
          box.addEventListener('change', handleCheckedChange);
          });
          }


          You then have to "activate" it on each checkbox group. This is the part I know nothing about. Hopefully it helps anyway :)



          justTwo(document.querySelector('.inputgroup'));


          Or with jQuery:



          $('.inputgroup').each(function () { justTwo(this); });






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 23 '18 at 17:15









          DavidDavid

          1,086116




          1,086116













          • Thank you so much! It worked very well with a little adaptation to Shiny.

            – yanirmor
            Nov 27 '18 at 19:11



















          • Thank you so much! It worked very well with a little adaptation to Shiny.

            – yanirmor
            Nov 27 '18 at 19:11

















          Thank you so much! It worked very well with a little adaptation to Shiny.

          – yanirmor
          Nov 27 '18 at 19:11





          Thank you so much! It worked very well with a little adaptation to Shiny.

          – yanirmor
          Nov 27 '18 at 19:11


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53449412%2fdynamic-limit-of-checkbox-group-selection%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          404 Error Contact Form 7 ajax form submitting

          How to know if a Active Directory user can login interactively

          TypeError: fit_transform() missing 1 required positional argument: 'X'