Showing and hiding elements with JS (and some CSS)
This one is going to be a short one (hopefully). I know a lot of developers depend on Bootstrap and jQuery to add a little bit of pizzazz to their apps with fade-ins, smooth transitions, and whatever other fancy little thing they find themselves needing. However, I want to encourage you to start thinking outside the box. HOW do those frameworks accomplish things? After all, jQuery is just a wrapper around JavaScript right? So JavaScript is what’s really doing all of these things for you. For the purpose of this post, we’re going to focus on a very common utility, the javascript fade.
For instance, let’s talk about $("elem").show()
Ah the good ole’ days… That function is a seemingly super simple jQuery function that show’s an element. That little function executes the following code:
function showHide( elements, show ) { var display, elem, values = [], index = 0, length = elements.length; // Determine new display value for elements that need to change for ( ; index < length; index++ ) { elem = elements[ index ]; if ( !elem.style ) { continue; } display = elem.style.display; if ( show ) { // Since we force visibility upon cascade-hidden elements, an immediate (and slow) // check is required in this first loop unless we have a nonempty display value (either // inline or about-to-be-restored) if ( display === "none" ) { values[ index ] = dataPriv.get( elem, "display" ) || null; if ( !values[ index ] ) { elem.style.display = ""; } } if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { values[ index ] = getDefaultDisplay( elem ); } } else { if ( display !== "none" ) { values[ index ] = "none"; // Remember what we're overwriting dataPriv.set( elem, "display", display ); } } } // Set the display of the elements in a second loop to avoid constant reflow for ( index = 0; index < length; index++ ) { if ( values[ index ] != null ) { elements[ index ].style.display = values[ index ]; } } return elements; }
I will point out that this function was written to handle hiding multiple elements and handle some cascading cases, but do we really need THAT much code to fade a div in and out? Oh, it also just turns the div on or off. It doesn’t actually give you a smooth transition…
There was a time for jQuery…
When I began my endeavor with web development, I thought jQuery was the bee’s knees. Looking back, I don’t really know why I cared about it that much. I always thought JavaScript was too difficult and jQuery was so much cleaner I guess.
It’s funny how things can change so drastically. jQuery starts getting kind of screwy when you have to mix some vanilla JS in with it, especially when it comes to DOM manipulation. While writing this, I needed to test something with jQuery to make sure I was correct and embarrassingly, it took me about 5 minutes to get a simple onclick
command to fire with jQuery. Why? Because I forgot that annoying little wrapper that you have to put around an element in order to actually use jQuery functions on it. (You know, $(elem)
)
Anyway, I digress.
The vanilla JS way:
Technically, if you want to hide/show an element, it really is as simple as this:
const toggleTarget = document.getElementById("toggle-target"); if(toggleTarget.style.display === "") { toggleTarget.style.display = "none"; } else{ toggleTarget.style.display = ""; };
However, changing the display of an object, effectively removes the element from the DOM. Yea, it’s still there, but the other elements around it can’t see it anymore either. You’ll get some funky positioning and jumping. This really isn’t a fade, even though it does use javascript.
There is another way that won’t cause those positioning problems AND it will look pretty while doing it.
Enter CSS and classes
To make this fade look pretty, we need to understand how a javascript fade really works.
It’s pretty easy really. You tell a DOM element to start fading out or in. When you do that, the fade starts. When it’s done, it’s done. That’s really all there is to it.
Let’s modify the above code a little bit:
const fadeTarget = document.getElementById("fade-target"); if(!fadeTarget.classList.contains("fade")) { fadeTarget.classList.add("fade"); } else{ fadeTarget.classList.remove("fade"); };
First we get the element we want to fade with the document.getElementById
function. This is equivalent to using $("#fade-target")
from jQuery.
Then, if the target doesn’t have the class “fade”, we will add it to it.
If it does have the class “fade”, we will just remove that class.
Adding that class is what tells the object to fade in or out. After we tell it to start, it should just start fading, right?
Wrong! There is a very important piece of the puzzle missing here. If you run that code as it is, nothing will happen (unless you watch the DOM inspector closely).
In order to get that smooth transition, we have to use CSS transitions and the opacity
property.
#fade-target{ opacity:0; transition:opacity 1s linear; } #fade-target.fade{ opacity:1; }
The opacity property is pretty self explanatory here. However, it’s the transition
property that makes the magic happen.
Essentially, any changes to the opacity
property are going to happen steadly (linearly) over a 1 second interval.
In this case, we want the element to start out hidden and then appear when the fade class is added to the element. You can reverse this by simply reversing the opacity property values.
Here’s a demo of how it all works together:
Now you know how to make a vanilla JavaScript fade.