Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing classes from element without affecting css transitions in progress

Ok, I have a situation where I have basically built a little notification dropdown box that happens when the user does something, at the end it transitions to a opacity: 0; state.

However, because the user may click something else that will trigger this notification box again I am trying to come up with a way to reset it back to normal without affecting any in-progress transitions and attempting to keep the animation done by CSS rather than JavaScript.

CodePen:http://codepen.io/gutterboy/pen/WoEydg

HTML:

<a href="#">Open Notify Window</a>

<div id="top_notify" class="top-notify">
    <div class="container-fluid">
        <div class="row">
            <div class="content col-xs-12">
                <div class="alert" role="alert"></div>
            </div>
        </div>
    </div>
</div>

SCSS:

body {
    text-align: center;
    padding-top: 150px;
}

.top-notify {
    position: fixed;
    top: 0;
    width: 100%;
    z-index: 9999;

    .content {
        text-align: center;
        background-color: transparent;
        transform-style: preserve-3d;
    }

    .alert {
        display: inline-block;
        transform: translateY(-100%);
        min-width: 250px;
        max-width: 500px;
        border-top-left-radius: 0;
        border-top-right-radius: 0;

        &.visible {
            transform: translateY(0%);
            transition: 0.8s 0s, opacity 1s 3.8s;
            opacity: 0;
        }

    }

}

JS:

$('a').on('click', function(e){
    e.preventDefault();
    myFunc();
});

function myFunc() {

    // Set file to prepare our data
    var loadUrl = "https://crossorigin.me/http://codepen.io/gutterboy/pen/ObjExz.html";

    // Run request

    getAjaxData(loadUrl, null, 'POST', 'html')

            .done(function(response) {

                var alert_el = $('#top_notify').find('.alert');

                // Update msg in alert box
                alert_el.text(response);
                alert_el.addClass('alert-success');

                // Slide in alert box
                alert_el.addClass('visible');

            })

            .fail(function() {
                alert('Problem!!');
            });

    // End

}

function getAjaxData(loadUrl, dataObject, action, type) {

    return jQuery.ajax({
        type: action,
        url: loadUrl,
        data: dataObject,
        dataType: type
    });

}

I know I can reset it back to normal by doing this in JS:

$('#top_notify').find('.alert').removeClass().addClass('alert'); // The classes it ends up with vary

...however doing this removes the classes before the transition is finished fading out the opacity and it just vanishes straight away.

I know I can do a delay in JS to counteract the CSS delay but doing it that way just doesn't seem a very good way to do it since you have the timings in 2 different places.

Is there any way I can accomplish this whilst keeping the animation done by CSS or will I have to move to using jQuery's animate so I can run the reset procedure once the animation is complete?

like image 467
Brett Avatar asked Nov 27 '16 17:11

Brett


People also ask

How do you remove a class from an element in CSS?

To add the CSS classes to an element we use addClass() method, and to remove the CSS classes we use removeClass() method.

How do you remove a specific class from all elements?

To remove all CSS classes of an element, we use removeClass() method. The removeClass() method is used to remove one or more class names from the selected element.

How do you remove a class from an element in HTML?

To remove a class from an element, you use the remove() method of the classList property of the element.

What triggers CSS transition?

Transition triggers # Your CSS must include a change of state and an event that triggers that state change for CSS transitions to activate. A typical example of such a trigger is the :hover pseudo-class. This pseudo-class matches when the user hovers over an element with their cursor.


1 Answers

Ok, I came up with a simple solution after coming up with a convoluted one ha.

Simple solution I should have come up with in the first place was removing any additional added classes before the ajax call; I got too focused on doing it within the ajax block and of course that didn't work, but until I started playing around with the other solution I never tried it.

Any way, the simple solution is simply moving this code:

 var alert_el = $('#top_notify').find('.alert');

...above the ajax call, rather than being inside of it.

Then adding this directly under it:

alert_el.removeClass('visible alert-success alert-info alert-danger alert-warning');

With the full function code being:

function myFunc() {

    // Set file to prepare our data
    var loadUrl = "https://crossorigin.me/http://codepen.io/gutterboy/pen/ObjExz.html";

    var alert_el = $('#top_notify').find('.alert');

    alert_el.removeClass('visible alert-success alert-info alert-danger alert-warning');

    // Run request

    getAjaxData(loadUrl, null, 'POST', 'html')

            .done(function(response) {

                // Update msg in alert box
                alert_el.text(response);
                alert_el.addClass('alert-success');

                // Slide in alert box
                alert_el.addClass('visible');

            })

            .fail(function() {
                alert('Problem!!');
            });

    // End

}

CodePen: http://codepen.io/gutterboy/pen/xRXbXy

The other solution I came up with, whilst not really needed now, I thought I would post it anyway in-case it comes in handy for me (or someone) else in the future.

It doesn't remove the visible class after the animation is finished (as there is no way that I know of to alert JS when it's done) but the visible class - which I would change the name of if you use this method - doesn't add any new styles, it just runs the animation.

Here is how I did it:

The JavaScript remains the same as the solution above, it's all in the CSS.

TLDR;

Basically uses multiple CSS animations to control different states during the effect runtime; CodePen at bottom.

The changes being in the .visible class and the addition of some @keyframes.

.visible class:

&.visible {
    animation: slideDown 0.8s 0s, keepThere 3s 0.8s, fadeAway 1s 3.8s;
}

As you can see we have gotten rid of any additional styling here - this means when the animation is done, it essentially resets back to normal, which is what we want.

Now, let's break down this code:

We are running 3 different animations here and it's important to note they don't run one after the other - meaning they don't wait until one is finished until it starts the next one, hence why we needed to include delay settings.

So first up we start with the slideDown animation:

slideDown 0.8s 0s

If you are new to animations in CSS then basically what this does is sets a delay of 0s before it starts running and the animation runs for 0.8s, and this is the animation:

@keyframes slideDown {
    0% {
        transform: translateY(-100%);
    }
    100% {
        transform: translateY(0%);
    }
}

So, pretty simple, just slides it down with transform from -100% to 0% and this animation takes 0.8s as we set in our call to this animation.

Now, I wanted this to stay visible for 3 seconds before it started to fade away, but we have a problem; once the animation ends then it goes back to it's standard styling, which in our case means it vanishes as it goes back to transform: translateY(-100%) since we have no extra styles in the .visible class, and we can't put any extra styles in there as then we won't be able to reset it back to it's original state (style wise).

But what do we do? The fadeAway animation doesn't start for another 3 seconds and at the moment it doesn't have anything to fade away (well it does, but you can't see it as it's hidden).

The solution to that was adding another animation - which technically doesn't really animate anything, it just keeps it visible until the fadeAway animation starts.

That's where we get to:

keepThere 3s 0.8s

Now, remembering the settings of our fadeAway animation are: fadeAway 1s 3.8s this means that we have 3 seconds before this animation is going to start and hence before we can control any of the styling with it.

So that's where these parameter values comes in - we set the delay to 0.8s so the keepThere animation doesn't start until the slideDown one has finished; then we set the duration for 3s to counter for the wait time until the fadeAway animation starts, and this is the keepThere animation:

@keyframes keepThere {
    0%, 100% {
        transform: translateY(0%);
    }
}

Since it has the same start and end styling we combine it into one selector of 0%, 100% and as you can see, this does just what it says it does, keeps the element visible for the set duration of 3s until we can control the styling with the fadeAway animation.

I guess technically you could combine this functionality into the fadeAway animation if you wanted to do the math at what % equals 3 seconds and hence know when to start fading the element away.

Lastly we have the fadeAway animation:

fadeAway 1s 3.8s

Now as we have discussed above, we already know why we have set the delay to 3.8s, the 0.8s offset to allow the slideDown animation to run and an additional 3s delay as that's how long we want the element to be visible for until it starts fading away and then of course the fade takes 1s to complete.

The animation for this is:

@keyframes fadeAway {
    0%, 100% {
        transform: translateY(0%);
    }
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0;
    }
}

Now, since the keepThere animation has completed, we have to make sure to keep the element visible so the fade has something visible to actually fade away, that's why we make sure to include the style transform: translateY(0%); as a value from start to finish; after that it's quite obvious what it's doing I think.

Put it all together and you get:

.top-notify {
    position: fixed;
    top: 0;
    width: 100%;
    z-index: 9999;

    .content {
        text-align: center;
        background-color: transparent;
        transform-style: preserve-3d;
    }

    .alert {
        display: inline-block;
        transform: translateY(-100%);
        min-width: 250px;
        max-width: 500px;
        border-top-left-radius: 0;
        border-top-right-radius: 0;

        &.visible {
            animation: slideDown 0.8s 0s, keepThere 3s 0.8s, fadeAway 1s 3.8s;
        }

    }

}

@keyframes slideDown {
    0% {
        transform: translateY(-100%);
    }
    100% {
        transform: translateY(0%);
    }
}

@keyframes keepThere {
    0%, 100% {
        transform: translateY(0%);
    }
}

@keyframes fadeAway {
    0%, 100% {
        transform: translateY(0%);
    }
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0;
    }
}

CodePen: http://codepen.io/gutterboy/pen/QGqwBg

Then of course to be able to run it again the class has to be re-added and hence that was the purpose of removing the .visible class at the start of each run (before the ajax call) and then when it gets re-added during the ajax call it runs again.

Thanks to @Nathaniel Flick for sharing the link that led me down this path to begin with :)

Well, hopefully that comes in handy for someone seeing as I am no longer going to use that option ha!

like image 123
Brett Avatar answered Nov 15 '22 01:11

Brett