3
\$\begingroup\$

I am displaying a loading spinner inside of various elements. There are four potential states an element could be in:

  • Reduced opacity; unresponsive to pointer events.
  • Reduced opacity; responsive to pointer events.
  • Maintain opacity; unresponsive to pointer events.
  • Maintain opacity; responsive to pointer events.

I'm trying to write some CSS which is elegant, but also allows for enough flexibility to achieve the above goals.

I've attached a code-snippet to this post showing my implementation. Please note that it is intended only for modern browsers.

My concerns are:

  • I feel like this is the sort of thing which should be done using an overlay element and clever usage of z-index, but I also feel like my scenarios are too dynamic to support this sort of approach.
  • I am using the * selector with some filters applied to it in order to properly select elements. It is generally frowned upon to use the * selector. However, I don't believe that inverting my logic and applying .is-belowFadeout and .is-unclickableWhenSpinning classes is more appropriate.

How should I be approaching this problem?

.content {
    width: 180px;
    height: 180px;
    position: relative;
    background-color: lightGray;
    padding: 10px;
}

.content-subContent {
    width: 40px;
    height: 40px;
    background-color: red;
    cursor: pointer;
    display: inline-block;
}

@-webkit-keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(359deg);
  }
}

.spinner {
    border: 1px solid #4ea6ea;
    border-left-color: transparent;
    border-top-color: transparent;
    border-radius: 50%;
    -webkit-animation: spin 600ms infinite linear;
    display: none;
    vertical-align: middle;
}

.spinner--large {
    width: 32px;
    height: 32px;
    border-width: 4px;
}

/* TODO: This seems like a poor way of doing this. What would be more robust? */
.is-showingSpinner > *:not(.is-aboveFadeout):not(.spinner) {
    opacity: .2;
}

.is-showingSpinner > *:not(.is-alwaysClickable) {
    pointer-events: none;
}

.is-showingSpinner > .spinner {
    display: inline-block;
}

.u-overlay {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;
}

.u-marginAuto {
    margin: auto;
}

.is-alwaysClickable:hover {
    background-color: #8b0000;
}
<div class='content is-showingSpinner'>
    <div class="spinner spinner--large u-overlay u-marginAuto"></div>
    <div class='content-subContent'></div>
    <div class='content-subContent is-aboveFadeout'></div>
    <div class='content-subContent is-alwaysClickable'></div>
    <div class='content-subContent is-aboveFadeout is-alwaysClickable'></div>
</div>

\$\endgroup\$
1
\$\begingroup\$

First, For the universal selector, the performance diference will be negligible exept if you load 100s or 1000s of those element with into one web page.

This said, I reviewed you code and made several changes :

Used pseudo elements to make the spinner and the overlay for 2 reasons :

  1. one unsemantic html tag removed
  2. this alows to play with z-index so the red elements can be behind or in front of the overlay.

Used z-index instead of opacity to fade red elements out and moved the pointer-events property to the overlay pseudo element so that the .subContent can still be clicked.

.content {
    width: 180px; height: 180px;
    position: relative;
    padding: 10px;
}
.is-showingSpinner:before, .is-showingSpinner:after{
    content:'';
    position:absolute;
    bottom: 0; left: 0; right: 0; top: 0;
    z-index:2;
    pointer-events:none;
}
.is-showingSpinner:before {
    width:100%; height:100%;
    background-color: rgba(211, 211, 211, .8);
}
.is-showingSpinner:after { 
    margin:auto;
    width: 32px; height: 32px;
    border: 4px solid #4ea6ea;
    border-left-color: transparent;
    border-top-color: transparent;
    border-radius: 50%;
    -webkit-animation: spin 600ms infinite linear;
    animation: spin 600ms infinite linear;
}
.content-subContent {
    width: 40px; height: 40px;
    background-color: red;
    display: inline-block;
}
@-webkit-keyframes spin {
    0%   { transform: rotate(0deg); }
    100% { transform: rotate(359deg); }
}
/* TODO: This seems like a poor way of doing this. What would be more robust? */
 .is-showingSpinner > .is-aboveFadeout {
    position:relative;
    z-index:2;
}
.is-alwaysClickable:hover {
    cursor: pointer;
    background-color: #8b0000;
}
<div class='content is-showingSpinner'>
    <div class='content-subContent'></div>
    <div class='content-subContent is-aboveFadeout'></div>
    <div class='content-subContent is-alwaysClickable'></div>
    <div class='content-subContent is-aboveFadeout is-alwaysClickable'></div>
</div>

I also removed the classes you added to the spinner element. If you still need them, you can add them to the .content div to style the spinner ( :after pseudo element) as needed.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.