Vue.js v-hide directive, whilst keeping element's occupied space

Posted by ryansouthgate on 30 Jan 2020

In this (small) post I’m going to demonstrate how to use a VueJS custom directive to hide an element, whilst still keeping it’s occupied space. It makes good use of the CSS Property “visibility”.

v-if

The built in Vue.js directive (v-if) works by totally removing the element from the DOM when it’s bound condition is false. Therefore we wont be looking looking at this as it’s not using CSS (to modify visibility), it’s doing a delete.

v-show

This directive works by adding the following css property onto the element, when the bound condition is false.

display: "none";

That’s literally it. Only problem I personally had is that the element being “hidden” is in the middle of two, always visible, elements. which means as that flag changes (and it can change quite frequently throughout the page’s lifecycle) the other two visible elements are jumping around the screen, taking the user’s focus. The problem is magnified on smaller devices with smaller screens, as other elements also move around because of the “reactive-ness” of the UI.

v-hide

(no it doesn’t just do the inverse of v-show)

I created this custom directive (code below). The naming isn’t brilliant, but I didn’t want a really verbose directive name like “v-if-you-need-to-hide-the-thing-whilst-still-keeping-its-used-space” and couldn’t think of anything better at the time. The directive code itself is nicely commented with a brief summary of what it does and why. The code below uses the CSS property “visibility”, more info on that here

From the first sentence of the linked docs: “The visibility CSS property shows or hides an element without changing the layout of a document.”

Perfect, now let’s use it! (I’m using TypeScript in my current project, and it’s awesome!)

// Extract the function out, up here, so I'm not writing it twice
const update = (el: HTMLElement,
    binding: DirectiveBinding,
    vnode: VNode,
    oldVnode: VNode) => el.style.visibility = (binding.value) ? "hidden" : "";

/**
 * Hides an HTML element, keeping the space it would have used if it were visible (css: Visibility)
 */
Vue.directive("hide", {
    // Run on initialisation (first render) of the directive on the element
    bind: update,
    // Run on subsequent updates to the value supplied to the directive
    update: update
})

And that’s it, anywhere where I don’t want the element actually losing its height/width, and the layout of the page changing and things jumping around. I just use this simple directive. Just remember the bool value it accepts is true when you want to hide the element.

I hope this post serves as some use to people, it’ll also help me in the future when I no-doubt forget this and need to do something similar in another project

I hope this post has helped, if anyone has any questions/improvements then leave a comment below, or get hold of me on Twitter



comments powered by Disqus