Search code examples
javascripthtmlcssvue.jstoggle

Connect two toggle buttons to switch each other


I have two toggle switch buttons in which button 1 is checked as default. When clicking the 1st button, the 2nd button should be on and the 1st button should be off and after clicking the 2nd button 1st button should be on and the second button should close. Only one button can be on at one time. I tried different methods but nothing works perfectly.

HTML code :

<div class="col-md-1" style="margin-top:2em; margin-left:-2em">
     <span class="switch">
      <input type="checkbox" class="switch" id="switch-id1" @click="" checked>
     </span>
</div>

<div class="col-md-1" style="margin-top:2em; margin-left:-2em">
     <span class="switch">
      <input type="checkbox" class="switch" id="switch-id2" @click="" unchecked>
     </span>
</div>

CSS :

$switch-height: calc(#{$input-height} * .8) !default;
$switch-height-sm: calc(#{$input-height-sm} * .8) !default;
$switch-height-lg: calc(#{$input-height-lg} * .8) !default;
$switch-border-radius: $switch-height !default;
$switch-bg: $custom-control-indicator-bg !default;
$switch-checked-bg: map-get($theme-colors, 'primary') !default;
$switch-unchecked-bg: map-get($theme-colors, 'primary') !default;
$switch-disabled-bg: $custom-control-indicator-disabled-bg !default;
$switch-disabled-color: $custom-control-description-disabled-color !default;
$switch-thumb-bg: $white !default;
$switch-thumb-border-radius: 50% !default;
$switch-thumb-padding: 2px !default;
$switch-focus-box-shadow: 0 0 0 $input-btn-focus-width rgba(map-get($theme-colors, 'primary'), .25);
$switch-transition: .2s all !default;

.switch {
  font-size: $font-size-base;
  position: relative;

  input {
    position: absolute;
    height: 1px;
    width: 1px;
    background: none;
    border: 0;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    overflow: hidden;
    padding: 0;

    + label {
      position: relative;
      min-width: calc(#{$switch-height} * 2);
      border-radius: $switch-border-radius;
      height: $switch-height;
      line-height: $switch-height;
      display: inline-block;
      cursor: pointer;
      outline: none;
      user-select: none;
      vertical-align: middle;
      text-indent: calc(calc(#{$switch-height} * 2) + 3.5rem);
    }

    + label::before,
    + label::after {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: calc(#{$switch-height} * 2);
      bottom: 0;
      display: block;
    }

    + label::before {
      right: 0;
      background-color: $switch-bg;
      border-radius: $switch-border-radius;
      transition: $switch-transition;
    }

    + label::after {
      top: $switch-thumb-padding;
      left: $switch-thumb-padding;
      width: calc(#{$switch-height} - calc(#{$switch-thumb-padding} * 2));
      height: calc(#{$switch-height} - calc(#{$switch-thumb-padding} * 2));
      border-radius: $switch-thumb-border-radius;
      background-color: $switch-thumb-bg;
      transition: $switch-transition;
    }

    &:checked + label::before {
      background-color: $switch-checked-bg;
    }

    &:checked + label::after {
      margin-left: $switch-height;
    }

    &:focus + label::before {
      outline: none;
      box-shadow: $switch-focus-box-shadow;
    }

    &:disabled + label {
      color: $switch-disabled-color;
      cursor: not-allowed;
    }

    &:disabled + label::before {
      background-color: $switch-disabled-bg;
    }
  }

  // Small variation
  &.switch-sm {
    font-size: $font-size-sm;

    input {
      + label {
        min-width: calc(#{$switch-height-sm} * 2);
        height: $switch-height-sm;
        line-height: $switch-height-sm;
        text-indent: calc(calc(#{$switch-height-sm} * 2) + .5rem);
      }

      + label::before {
        width: calc(#{$switch-height-sm} * 2);
      }

      + label::after {
        width: calc(#{$switch-height-sm} - calc(#{$switch-thumb-padding} * 2));
        height: calc(#{$switch-height-sm} - calc(#{$switch-thumb-padding} * 2));
      }

      &:checked + label::after {
        margin-left: $switch-height-sm;
      }
    }
  }

  // Large variation
  &.switch-lg {
    font-size: $font-size-lg;

    input {
      + label {
        min-width: calc(#{$switch-height-lg} * 2);
        height: $switch-height-lg;
        line-height: $switch-height-lg;
        text-indent: calc(calc(#{$switch-height-lg} * 2) + .5rem);
      }

      + label::before {
        width: calc(#{$switch-height-lg} * 2);
      }

      + label::after {
        width: calc(#{$switch-height-lg} - calc(#{$switch-thumb-padding} * 2));
        height: calc(#{$switch-height-lg} - calc(#{$switch-thumb-padding} * 2));
      }

      &:checked + label::after {
        margin-left: $switch-height-lg;
      }
    }
  }

  + .switch {
    margin-left: 1rem;
  }
}


Solution

  • You can do that with vue:

    const app = Vue.createApp({
      data: () => ({
        status: true
      }),
      methods: {
        clicked(val) {
          this.status === val ? this.status = !val : this.status = val
          // some other stuff you need on click
        }
      }
    })
    
    app.mount('#demo')
    <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <div id="demo">
      <div class="col-md-1" style="margin-top:2em; margin-left:2em">
         <span class="switch">
          <input type="checkbox" class="switch" id="switch-id1" @click="clicked(false)" :checked="status">
         </span>
      </div>
      <div class="col-md-1" style="margin-top:2em; margin-left:2em">
         <span class="switch">
          <input type="checkbox" class="switch" id="switch-id2" @click="clicked(true)" :checked="!status">
         </span>
      </div>
    </div>