skip to Main Content

With this toggle switch:

* { box-sizing: border-box; }
.switch { appearance: none; padding: 9px 18px; border-radius: 13px; background: radial-gradient(circle 9px, #007722 100%, transparent 100%) transparent -9px; border: 1px solid #007722; transition: all 0.3s; }
.switch:checked { background-position: 9px; }
<input type="checkbox" class="switch"></div>

I’d like the CSS smooth transition animation to happen if and only if the state change is done by the user.

On the other hand, if the state change is triggered by JS code (item.checked = false), then no transition happens.

Is there a more elegant solution than removing the transition CSS with JS, changing the state with JS, and set again the transition CSS with JS?

Maybe is there a CSS attribute for transition triggered by user only?

3

Answers


  1. If you’re talking about to do things triggered by user’s hand, then you’re refering to events. So, if you want to make some changes in element’s CSS styles, you must attach those changes to some events like click and the like.

    const element = document.querySelector("#Element's ID");
    
    element.addEventListener("click", () => {
        //Do things
    }, false);
    

    On the other hand, you can manipulate transitions through style.transition property or just changing the class name linked to the element.

    Login or Signup to reply.
  2. No, there is no simpler way to do this.

    But, keep in mind that this is exactly why classList.add() and classList.remove() were added to the DOM API. Not only is there no simpler way than this already simple solution, but this is really the preferred way to handle any kind of dynamic CSS class changes that may need to occur in a page.

    Just remove the transition from the baseline class and set it up in its own class and apply both classes by default. When the JS code needs to make the change (as opposed to a human), first remove the class that includes the transition, make the element change, and then add the class back (for possible human interaction later).

    There is one caveat here, which is that the browser can sometimes act upon the UI faster or slower than JS can process a UI instruction so adding a slight imperceptible delay with .setTimeout() helps the UI get updated correctly.

    You can see that without a short delay built-in, the effect doesn’t work:

    document.querySelector("button").addEventListener("click", function(event){
      const element = document.querySelector("input.transition");
    
      // JS code is going to affect the element: remove the transition class
      element.classList.remove("transition");
    
      element.checked = !element.checked; // Toggle the UI
    
      // Add the class back for futue possible human interaction
      element.classList.add("transition");  
    
    });
    * { box-sizing: border-box; }
    
    /* This class applies all the time but does not include transition */
    .switch { appearance: none; padding: 9px 18px; border-radius: 13px; background: radial-gradient(circle 9px, #007722 100%, transparent 100%) transparent -9px; border: 1px solid #007722;  }
    
    /* This class is used by default but is removed when no transition is wanted. */
    .transition { transition: all .3s; }
    
    .switch:checked { background-position: 9px; }
    <div>First, change the toggle switch manually. Then, use the button to do it.</div>
    <input type="checkbox" class="switch transition">
    
    <button type="button">Click to have JS change the value</button>

    But, with the delay it does:

    See comments inline.

    document.querySelector("button").addEventListener("click", function(event){
      const element = document.querySelector("input.transition");
    
      // JS code is going to affect the element: remove the transition class
      element.classList.remove("transition");
    
      element.checked = !element.checked; // Toggle the UI
    
      // Adding a small delay ensures that the UI has enough time to update the UI
      setTimeout(function(){
        element.classList.add("transition");  // Now, add back the transition
      }, .1);
    });
    * { box-sizing: border-box; }
    
    /* This class applies all the time but does not include transition */
    .switch { appearance: none; padding: 9px 18px; border-radius: 13px; background: radial-gradient(circle 9px, #007722 100%, transparent 100%) transparent -9px; border: 1px solid #007722;  }
    
    /* This class is used by default but is removed when no transition is wanted. */
    .transition { transition: all .3s; }
    
    .switch:checked { background-position: 9px; }
    <div>First, change the toggle switch manually. Then, use the button to do it.</div>
    <input type="checkbox" class="switch transition">
    
    <button type="button">Click to have JS change the value</button>
    Login or Signup to reply.
  3. Note that I agree with Scott’s answer (adding/removing a class is generally the way to go) but there is another, albeit limited, way.

    This at least works when the user is using a mouse.
    Only apply the transition to: .switch:hover

    Keyboard users get supported by adding .switch:focus
    Touchscreen users should be covered with .switch:active

    I’ve updated my example to use all of the user action pseudo-class selectors found here. So it essentially only applies the transition when the element is in a state triggered by a user action. Note: using the keyboard to click the "flip" button while the mouse cursor is hovering the .switch still results in a transition.

    const toggle = document.querySelector('input.switch')
    
    document.querySelector('button').addEventListener('click', event => toggle.checked = !(toggle.checked))
    * {
      box-sizing: border-box;
    }
    
    .switch {
      appearance: none;
      padding: 9px 18px;
      border-radius: 13px;
      background: radial-gradient(circle 9px, #007722 100%, transparent 100%) transparent -9px;
      border: 1px solid #007722;
    }
    
    .switch:hover,
    .switch:active,
    .switch:focus,
    .switch:focus-visible,
    .switch:focus-within {
      transition: all 0.3s;
    }
    
    .switch:checked {
      background-position: 9px;
    }
    <input type="checkbox" class="switch"></div>
    
    <br/>
    <button>flip</button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search