shubham agrawal
shubham agrawal

Reputation: 3541

CSS pseudo class :focus after button click

I was working with some basic CSS animation for button.The problem is the :focus pseudo class even works when we press tab on keyboard. So I want that :focus should only works when I click on the button i.e only when active.

Here is the code:

button {
  background: #c33;
  width: 150px;
  height: 30px;
  border: 0;
  color: #fff;
}

button:after {
  content: 'RENT ME';
  display: block;
}

button:active,
button:focus {
  background: green;
}

button:active:after,
button:focus:after {
  display: block;
  animation: shake 1s linear, revert 2s 1s;
  position: relative;
}

@keyframes shake {
  from {
    content: "O";
    font-family: FontAwesome;
    transform: rotate(0deg);
  }
  to {
    content: "O";
    font-family: FontAwesome;
    transform: rotate(360deg);
  }
}

@keyframes revert {
  0% {
    content: 'ADDED TO CART';
    left: -60px
  }
  50% {
    content: 'ADDED TO CART';
    left: 0px;
  }
  100% {
    content: 'ADDED TO CART';
    left: 0px;
  }
}
<button></button>
<button></button>

In the above code the button changes to green on press of tab which I want to avoid.Is there a pure CSS solution to it.

JSFIDDLE

Upvotes: 11

Views: 15301

Answers (6)

nashcheez
nashcheez

Reputation: 5183

To avoid the selection of your buttons using the tab key at the start, you can try to avoid the use of the button element and instead replicate the same behaviour using a div with a tabindex attribute.

Use tabindex = "-1" specifically to avoid the switch via the tabs key.

Refer code :

div {
  background: #c33;
  width: 150px;
  height: 30px;
  padding-top: 6px;
  border: 0;
  color: #fff;
  display: inline-block;
  align-items: flex-start;
  text-align: center;
  box-sizing: border-box;
  cursor: pointer;
  font-size: 12px;
  z-index: 10;
  outline: 0;
}

div:first-child {
  margin-left: 1px;
}

div:after {
  content: 'RENT ME';
  display: block;
  z-index: 10;
}

div:focus {
  background: green;
}

div:focus:after {
  display: block;
  animation: shake 1s linear, revert 2s 1s;
  -webkit-animation: shake 1s linear, revert 2s 1s;
  position: relative;
}

@keyframes shake {
  from {
    content: "O";
    font-family: FontAwesome;
    transform: rotate(0deg);
    -webkit-transform: rotate(0deg);
  }
  to {
    content: "O";
    font-family: FontAwesome;
    transform: rotate(360deg);
    -webkit-transform: rotate(360deg);
  }
}

@keyframes revert {
  0% {
    content: 'ADDED TO CART';
    left: -60px
  }
  50% {
    content: 'ADDED TO CART';
    left: 0px;
  }
  100% {
    content: 'ADDED TO CART';
    left: 0px;
  }
}
<div tabindex="-1"></div>
<div tabindex="-1"></div>

Upvotes: 0

Alexander
Alexander

Reputation: 4527

You can set green background temporary. Is it what you want?

button {
  background: #c33;
  width: 150px;
  height: 30px;
  border: 0;
  color: #fff;
}

button:after {
  content: 'RENT ME';
  display: block;
}

button:active,
button:focus {
    animation: green-bg 4s step-start;
}

button:active:after,
button:focus:after {
  display: block;
  animation: shake 1s linear, revert 2s 1s;
  position: relative;
}

@keyframes shake {
  from {
    content: "O";
    font-family: FontAwesome;
    transform: rotate(0deg);
  }
  to {
    content: "O";
    font-family: FontAwesome;
    transform: rotate(360deg);
  }
}

@keyframes revert {
  0% {
    content: 'ADDED TO CART';
    left: -60px
  }
  50% {
    content: 'ADDED TO CART';
    left: 0px;
  }
  100% {
    content: 'ADDED TO CART';
    left: 0px;
  }
}

@keyframes green-bg {
  from {
    background: green;
  }
  to {
    background: green;
  }
}
<button></button>
<button></button>

Upvotes: 0

Yash Yadav
Yash Yadav

Reputation: 655

MY ANSWER IS DIFFERENT FROM OTHERS.

$("button").focus(function() {
  $(document).on('keydown', function(event) {
    if (event.keyCode == 9) { //tab pressed
      event.preventDefault(); // stops its action
    }
  })
});

$("button").blur(function() {
  $("this").unbind();
});
button {
  background: #c33;
  width: 150px;
  height: 30px;
  border: 0;
  color: #fff;
}

button:after {
  content: 'RENT ME';
  display: block;
}

button:active,
button:focus {
  background: green;
}

button:active:after,
button:focus:after {
  display: block;
  animation: shake 1s linear, revert 2s 1s;
  position: relative;
}

body[data-whatinput="keyboard"] button:focus {
  background: red;
}

@keyframes shake {
  from {
    content: "O";
    font-family: FontAwesome;
    transform: rotate(0deg);
  }
  to {
    content: "O";
    font-family: FontAwesome;
    transform: rotate(360deg);
  }
}

@keyframes revert {
  0% {
    content: 'ADDED TO CART';
    left: -60px
  }
  50% {
    content: 'ADDED TO CART';
    left: 0px;
  }
  100% {
    content: 'ADDED TO CART';
    left: 0px;
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/what-input/4.1.1/what-input.min.js"></script>
<button tabindex="-1"></button>
<button tabindex="-1"></button>

Upvotes: 0

scottohara
scottohara

Reputation: 765

You're not going to get an accessible, or production-worthy solution to this without the use of JavaScript.

If we're to take the purpose of the animation at face value, you're simulating a function that adds an item to a shopping cart. If that's true, you would only want to load the 'added to card' portion of the animation after the item was actually added. By itself, CSS can't know if that's actually true or not, so you'll never get a working solution with CSS alone.

This is unfortunately not the answer you're looking for, as it involves JavaScript, but it is the solution you're looking to achieve.

var myBtn = document.getElementById('rent_me');

myBtn.addEventListener('click', function () {

  if ( myBtn.getAttribute('aria-pressed') === 'false') {
    myBtn.setAttribute('aria-pressed', 'true');
    myBtn.classList.add('loading');
    myBtn.querySelector('.init-text').setAttribute('aria-hidden', 'true');

    /*
      This is to simulate the function of actually
      adding this 'item' to the shopping cart.
      Wouldn't be used outside of this example
    */

    setTimeout( function () {
      myBtn.classList.remove('loading');
      myBtn.querySelector('.added-text').setAttribute('aria-hidden', 'false')
    }, 2000);
  }
  else {
    myBtn.classList.remove('loading');
    myBtn.setAttribute('aria-pressed', 'false');
    myBtn.querySelector('.init-text').setAttribute('aria-hidden', 'false');
    myBtn.querySelector('.added-text').setAttribute('aria-hidden', 'true')
  }
});
body {
  padding: 20px;
  text-align: center;
}

/* setup the button styling */
button {
  background: #c33;
  border: 2px solid rgba(0,0,0,0);
  width: 150px;
  height: 40px;
  padding: 8px;
  position: relative;
  color: #fff;
  overflow: hidden;
  text-align: center;
}

/* the text inside the button is placed within spans that get pushed in/out the bounding of the button with text-indent and overflow hidden */
button span {
  position: absolute;
  left: 0;
  width: 100%;
  white-space: nowrap;
  transition:
    text-indent .2s ease-in-out;
  top: 50%;
  transform: translateY(-50%);
}

/* start with rent me text in view */
.init-text {
  text-indent: 0%;
}

/* added to cart text pushed off to the right */
.added-text {
  text-indent: 200%;
}

/* buttons need a focus state so keyboard users know they've 'focused it'
button:focus {
  border: 2px solid #000;
}

/* when the button has been activated, JS will set the aria-pressed value to true, changing the background color of the button */
button[aria-pressed="true"] {
  background: green;
}

/* if the button has been pressed, get the 'rent me' text out of view */
button[aria-pressed="true"] .init-text {
  text-indent: -200%;
}

/* once the button is no longer in the loading phase, bring the 'added to cart' text into view */
button[aria-pressed="true"]:not(.loading) .added-text {
  text-indent: 0%;
}

/* when the button is loading, show a spinner */
.loading:before {
  transform-origin: center center;
  animation: rotate 1s infinite;
  position: absolute;
  content: "\21BB";
  left: 0;
  right: 0;
  margin: auto;
  speak: none;
  top: 50%;
  transform: translateY(-50%) rotate(0deg);
}


/* animation for the spinner */
@keyframes rotate {
  from {
    transform: translateY(-50%) rotate(0deg);
  }
  to {
    transform: translateY(-50%) rotate(360deg);
  }
}
<button type="button" id="rent_me" aria-pressed="false" aria-live="polite" aria-atomic="true">
  <span class="init-text">
    Rent Me
  </span>
  <span class="added-text">
    Added to Cart
  </span>
</button>

Upvotes: 2

FIx
FIx

Reputation: 142

use tabIndex="-1" on button if you don't want the button to be focusable when pressing tab

<button tabIndex="-1"></button>

Upvotes: 7

Quentin
Quentin

Reputation: 943214

If a button is active, then it will always be focused at the same time.

So just provide an :active rule and provide no :focus rule at all.

Upvotes: 4

Related Questions