Why does my :active selector lose its click event state on scale tranformation?

Viewed 831

I designed a button for a website and tried to add transform: scale(0.95); on a click event (using the :activate CSS selector).

a.btn, input[type="submit"].btn, label.btn, button.btn {
  text-transform: uppercase;
  border: solid 1px #282828;
  font-size: 11px;
  line-height: 17px;
}
a.btn:active, input[type="submit"].btn:active, label.btn:active, button.btn:active {
  transform: scale(0.95);
}
<input type="submit" value="Show more recipes" class="btn"/>

If the user clicks on the border of the button, the :active state reduces the icon and the cursor no longer covers the button, and probably no longer triggers the click event. If the user clicks inside the scale(0.95) area, the click event is triggered as normal.

Scale lost click example

I am trying to keep the click event; I tried to apply an invisible :before panel with no success, since input[type='submit'] does not support it, and was hoping someone solved this issue already.

Edit Jan 14th: Issue has been rewritten now using input[type='submit'] which cannot contain elements.

6 Answers

You can easily work around this

Make the content(button look) with another element in the button or anchor, and put the transform on the new element inside of the button. This way when you click the button, you get the transform and the js triggering since the actual button isn't shrinking

here is a simple example

document.getElementById('moreRecipes').addEventListener('click', function() {
    alert("clicked");
});
button.btn span {

    text-transform: uppercase;
    border: solid 1px #282828;
    font-size: 11px;
    line-height: 17px;
    display: block;
}
button.btn:active span {
    transform: scale(.95);
}
<button id="moreRecipes" class="btn">
  <span>Show more recipes</span>
</button>

When you use the click event you actually waiting for the both: mousedown and mouseup events and you just lose it on the mouseup part.

use the moousedown event and it will work for you.

Change your line to:

document.getElementById('moreRecipes').addEventListener('mousedown', showMore);

:active Indefinitely

There's a way to have :active go on indefinitely or until the user clicks something else that becomes :active.

  1. Set a transition: 0.5s on the input:active
  2. Set input transition: 99999s`

The input transition to transform:scale(0.95) takes 0.5s but the transition back to normal will take about 28 hours or until the user clicks something that becomes :active.

BTW if you want a submit button that can hold content then:

<button type='submit'>Show more recipe</button>

A submit button usually is used to send a form which means that the whole page reloads. I'm assuming that you are not using a form. If so, then using a submit button has as much use as a normal button.

Demo

.btn {
  text-transform: uppercase;
  border: solid 1px #282828;
  font-size: 11px;
  line-height: 17px;
  transition: 99s;
  transform: scale(1);
}

.btn:active {
  transform: scale(.95);
  transition: 0.7s;
}
<button type="submit" class="btn">Show more recipes</button>
<input type='submit' class='btn' value='Show more recipes'>

Not the very best solution, but the only thing that I can think of that doesn't mean rewriting all your html.

Use a box shadow to simulate a border. Now, increase the real border, set to transparent, to get click events.

In the snippet, i Have changed the border color (well, the inset shadow) to red, and set a partial transparent blue color to the border, to make the way it's working clearer.

As a side note, you don't need to perfectly match the final dimension, even if it is a little greater than necesary will not be noticeable.

a.btn, input[type="submit"].btn, label.btn, button.btn {

    text-transform: uppercase;
    border: solid 0px rgba(0,0,255,0.1);   /* should be transparent */
    box-shadow: inset 0px 0px 0px 2px red;
    font-size: 30px;
    line-height: 40px;
}

input[type="submit"].btn:active {
    transform: scale(.95);
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-right-width: 14px;
    border-left-width: 14px;
    margin-top: -5px;
    margin-left: -14px;
}
<input type="submit" value="Show more recipes" class="btn"/>

I hope you are kidding that this button is already used EVERYWHERE. :)

Let's assume we have a technical challenge and our goal is to apply the transform: scale(0.95); rule to the button on click. We cannot change the button by itself because of some "unknown" reasons.

The only pseudo-class that can help us is :active. It works on anchor and button elements when they are activated.

The solution would be to wrap the button into an anchor element and write a selector.

a:active input['submit'] {...}

I've updated the snippet. The gray area shows where a user can click to apply the pseudo-class. You can adjust the size of this area and remove the gray background.

a.btn, input[type="submit"].btn, label.btn, button.btn {
  text-transform: uppercase;
  border: solid 1px #282828;
  font-size: 11px;
  line-height: 17px;
}
.wrapper:active input[type="submit"],
a.btn:active, input[type="submit"].btn:active, label.btn:active, button.btn:active {
  transform: scale(0.95);
}
.wrapper {
  padding: 10px;
  background: gray;
}
<a class="wrapper">
  <input type="submit" value="Show more recipes" class="btn"/>
</a>

BTW, I have a course about CSS Selectors if you would like to learn more.

The simplest way is to add an unnoticed amount of transition-duration and transition-delay to the transformed element.

a.btn, input[type="submit"].btn, label.btn, button.btn {
    // ...
    transition-duration: .01s;
    transition-delay: 0.05s;
}

That amount of delay is enough for JS to fire the event on the correct element. Thus, the event will fire during the delay time and it will never go unnoticed as it's low.

Related