skip to Main Content

I have 3 svgs I use in my button element an unchecked svg, checked and loading. By default the unchecked is set to 1 while the rest are 0. I’d like to change the opacity of the unchecked to zero the checked when the button is clicked, and set a small timeout where the loading svg opacity is set to 1 and after the timeout, the checked svg is set 1 while the rest are 0 and vice versa. But it’s not working as expected the opacity of the unchecked isn’t changing when the button is clicked. Neither do the checked changes.

Here’s my code snippets of the issue

Html:

function toggleState(buttonIndex) {
  const uncheckedSvg = document.querySelector(`#button${buttonIndex} .unchecked`);
  const checkedSvg = document.querySelector(`#button${buttonIndex} .checked`);
  const loadingSvg = document.querySelector(`#button${buttonIndex} .loading`);

  loadingSvg.style.opacity = '1';
  
  setTimeout(() => {
loadingSvg.style.opacity = '0';
uncheckedSvg.style.opacity = checkedSvg.style.opacity === '0' || checkedSvg.style.opacity === '' ? '1' : '0';
checkedSvg.style.opacity = uncheckedSvg.style.opacity === '0' || uncheckedSvg.style.opacity === '' ? '1' : '0';
  }, 1000);
}
.check-box {
  display: block;
  height: fit-content;
  position: relative;
}

.svg-container {
  position: relative;
}

.svg-container .unchecked {
  opacity: 1;
  /* Set initial opacity to 1 */
}

.svg-container .checked,
.svg-container .loading {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0;
}

.checked {
  animation: fadeOutChecked 1s ease-in-out forwards;
}

.loading {
  animation: fadeOutLoading 1s ease-in-out forwards;
}

@keyframes fadeOutChecked {
  to {
    opacity: 0;
  }
}

@keyframes fadeOutLoading {
  to {
    opacity: 0;
  }
}
<button class="check-box" id="button1" onclick="toggleState(1)">

  <div class="svg-container">
    <svg class="unchecked button1" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 32 32" fill="none">
      <circle cx="16" cy="16" r="12" stroke="#8b8b8b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4 6" />
    </svg>

    <svg class="checked button1" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24" fill="none">
        <circle cx="12" cy="12" r="10" fill="#303030"></circle>
        <path d="M17.2738 8.52629C17.6643 8.91682 17.6643 9.54998 17.2738 9.94051L11.4405 15.7738C11.05 16.1644 10.4168 16.1644 10.0263 15.7738L7.3596 13.1072C6.96908 12.7166 6.96908 12.0835 7.3596 11.693C7.75013 11.3024 8.38329 11.3024 8.77382 11.693L10.7334 13.6525L15.8596 8.52629C16.2501 8.13577 16.8833 8.13577 17.2738 8.52629Z" fill="#fff"></path>
    </svg>

    <svg class="loading button1" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 28 28" fill="none">
        <path
          d="M26 14C26 16.3734 25.2962 18.6935 23.9776 20.6668C22.6591 22.6402 20.7849 24.1783 18.5922 25.0866C16.3995 25.9948 13.9867 26.2324 11.6589 25.7694C9.33114 25.3064 7.19295 24.1635 5.51472 22.4853C3.83649 20.8071 2.6936 18.6689 2.23058 16.3411C1.76755 14.0133 2.00519 11.6005 2.91345 9.4078C3.8217 7.21509 5.35977 5.34094 7.33316 4.02236C9.30655 2.70379 11.6266 2 14 2"
          stroke="#8b8b8b"
          stroke-width="2.5"
          stroke-linecap="round"
          stroke-linejoin="round"
          />
    </svg>
  </div>

</button>

Here’s what I have so far. I’d appreciate any assistance. Thanks

2

Answers


  1. Let’s create a class of .active {opacity:1} along with transition on the element(s) itself, then toggle the class on the correct images.

    function toggleState(buttonIndex) {
      const uncheckedSvg = document.querySelector(`#button${buttonIndex} .unchecked`);
      const checkedSvg = document.querySelector(`#button${buttonIndex} .checked`);
      const loadingSvg = document.querySelector(`#button${buttonIndex} .loading`);
    
      if (checkedSvg.classList.contains("active")) {
        // toggle off
        loadingSvg.classList.add('active');
        checkedSvg.classList.remove('active');
    
        setTimeout(function() {
          uncheckedSvg.classList.add('active');
          loadingSvg.classList.remove('active');
    
        }, 500)
    
      } else {
        // toggle on
        uncheckedSvg.classList.remove('active');
        loadingSvg.classList.add('active');
    
        setTimeout(function() {
          loadingSvg.classList.remove('active');
          checkedSvg.classList.add('active');
        }, 500)
    
      }
    
    }
    .check-box {
      display: block;
      height: fit-content;
      position: relative;
    }
    
    .svg-container {
      position: relative;
      width: 30px;
      height: 30px;
    }
    
    .svg-container .button1.active {
      opacity: 1;
    }
    
    .svg-container .button1 {
      transition: 250ms;
      position: absolute;
      top: 0;
      left: 0;
      opacity: 0;
    }
    <button class="check-box" id="button1" onclick="toggleState(1)">
    
      <div class="svg-container">
        <svg class="unchecked button1 active" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 32 32" fill="none">
          <circle cx="16" cy="16" r="12" stroke="#8b8b8b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4 6" />
        </svg>
    
        <svg class="checked button1" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24" fill="none">
            <circle cx="12" cy="12" r="10" fill="#303030"></circle>
            <path d="M17.2738 8.52629C17.6643 8.91682 17.6643 9.54998 17.2738 9.94051L11.4405 15.7738C11.05 16.1644 10.4168 16.1644 10.0263 15.7738L7.3596 13.1072C6.96908 12.7166 6.96908 12.0835 7.3596 11.693C7.75013 11.3024 8.38329 11.3024 8.77382 11.693L10.7334 13.6525L15.8596 8.52629C16.2501 8.13577 16.8833 8.13577 17.2738 8.52629Z" fill="#fff"></path>
        </svg>
    
        <svg class="loading button1" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 28 28" fill="none">
            <path
              d="M26 14C26 16.3734 25.2962 18.6935 23.9776 20.6668C22.6591 22.6402 20.7849 24.1783 18.5922 25.0866C16.3995 25.9948 13.9867 26.2324 11.6589 25.7694C9.33114 25.3064 7.19295 24.1635 5.51472 22.4853C3.83649 20.8071 2.6936 18.6689 2.23058 16.3411C1.76755 14.0133 2.00519 11.6005 2.91345 9.4078C3.8217 7.21509 5.35977 5.34094 7.33316 4.02236C9.30655 2.70379 11.6266 2 14 2"
              stroke="#8b8b8b"
              stroke-width="2.5"
              stroke-linecap="round"
              stroke-linejoin="round"
              />
        </svg>
      </div>
    
    </button>
    Login or Signup to reply.
  2. This is not better or worse but you can also use data attributes and fade stuff in and out with a timing set for the "loading" and the show/hide transitions with the opacity.

    Test and adjust the times on the transitions in the CSS as well as the JavaScript timeout for each transition to your liking.

    Note how I removed all the position and just put the images all in the same grid location so you can move your "toggle" where ever you like, size it etc. and not have to do any position – should give you a "responsive" layout with for resizing etc.

    You can see this where I "super centered" the button on the view using

    body {
      height: 100vh;
      display:grid;
      place-items:center;
    }
    
    function transistion(current, next, button) {
      if (current == "loading") {
        button.dataset.current = button.dataset.next;
      } else if (current == "unchecked") {
        button.dataset.current = "loading";
        button.dataset.next = "checked";
      } else if (current == "checked") {
        button.dataset.current = "loading";
        button.dataset.next = "unchecked";
      }
      if (current != "loading") {
        setTimeout(() => {
          transistion("loading", button.dataset.next, button);
        }, 2000);
      }
    }
    
    function toggleState(event) {
      const button = this; //same as: event.currentTarget;
      const currentSvg = button.dataset.current;
      const next = button.dataset.next;
      setTimeout(() => {
        transistion(currentSvg, next, button);
      }, 1000);
    }
    document.querySelector('#button1').addEventListener('click', toggleState);
    * {
      font-size: 16px;
      box-sizing: border-box;
      padding: 0;
      margin: 0;
    }
    
    body {
      height: 100vh;
      display:grid;
      place-items:center;
    }
    
    .check-box {
      display: block;
      height: fit-content;
      width: 2em;
      height: 2em;
    }
    
    .svg-container {
      display: grid;
      grid-template-columns: 1fr;
      grid-template-rows: 1fr;
      grid-template-areas: "image";
    }
    
    .svg-container svg.button-image {
      transition: 1000ms;
      grid-area: image;
    }
    
    .svg-container .checked,
    .svg-container .loading {
      opacity: 0;
    }
    
    .check-box[data-current=""] .svg-container .unchecked {
      opacity: 1;
    }
    
    .check-box[data-current="loading"] .svg-container .button-image.loading {
      animation: fadeIn 0.25s ease-in-out forwards;
    }
    
    .check-box[data-current="loading"] .svg-container .button-image.checked,
    .check-box[data-current="loading"] .svg-container .button-image.unchecked {
      animation: fadeOut 0.5s ease-in-out forwards;
    }
    
    .check-box[data-current="unchecked"] .svg-container .button-image.unchecked {
      animation: fadeIn 0s ease-in-out forwards;
    }
    
    .check-box[data-current="unchecked"] .svg-container .button-image.loading,
    .check-box[data-current="unchecked"] .svg-container .button-image.checked {
      animation: fadeOut 0.5s ease-in-out forwards;
    }
    
    .check-box[data-current="checked"] .svg-container .button-image.checked {
      animation: fadeIn 0s ease-in-out forwards;
    }
    
    .check-box[data-current="checked"] .svg-container .button-image.unchecked,
    .check-box[data-current="checked"] .svg-container .button-image.loading {
      animation: fadeOut 1s ease-in-out forwards;
    }
    
    @keyframes fadeOut {
      to {
        opacity: 0;
      }
    }
    
    @keyframes fadeIn {
      to {
        opacity: 1;
      }
    }
    <button class="check-box" id="button1" data-current="unchecked" data-next="loading">
      <div class="svg-container">
        <svg class="unchecked button-image" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 32 32" fill="none">
          <circle cx="16" cy="16" r="12" stroke="#8b8b8b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="4 6" />
        </svg>
        <svg class="checked button-image" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 24 24" fill="none">
            <circle cx="12" cy="12" r="10" fill="#303030"></circle>
            <path d="M17.2738 8.52629C17.6643 8.91682 17.6643 9.54998 17.2738 9.94051L11.4405 15.7738C11.05 16.1644 10.4168 16.1644 10.0263 15.7738L7.3596 13.1072C6.96908 12.7166 6.96908 12.0835 7.3596 11.693C7.75013 11.3024 8.38329 11.3024 8.77382 11.693L10.7334 13.6525L15.8596 8.52629C16.2501 8.13577 16.8833 8.13577 17.2738 8.52629Z" fill="#fff"></path>
        </svg>
        <svg class="loading button-image" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 28 28" fill="none">
            <path
              d="M26 14C26 16.3734 25.2962 18.6935 23.9776 20.6668C22.6591 22.6402 20.7849 24.1783 18.5922 25.0866C16.3995 25.9948 13.9867 26.2324 11.6589 25.7694C9.33114 25.3064 7.19295 24.1635 5.51472 22.4853C3.83649 20.8071 2.6936 18.6689 2.23058 16.3411C1.76755 14.0133 2.00519 11.6005 2.91345 9.4078C3.8217 7.21509 5.35977 5.34094 7.33316 4.02236C9.30655 2.70379 11.6266 2 14 2"
              stroke="#8b8b8b"
              stroke-width="2.5"
              stroke-linecap="round"
              stroke-linejoin="round"
              />
        </svg>
      </div>
    
    </button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search