NUXT: Accessing DOM in a composable

About a 4 minute read

Written by on

#nuxt #vue

So I’m learning Nuxt, right?

Well they have these neat things called composables. These are files that perform some sort of action and can be accessed throughout the application. I like to think of them as global helper functions… but I’m sure someone would tell me that’s wrong.

Anyway. I spent way too long stuck on a recent problem. I wanted to access the DOM via a composable. The problem was I kept getting an error because the composable was being executed before the DOM was created.

Classic, right?

The solution was pretty simple and something I didn’t even know you could do: lifecycle hooks. You can use Vue lifecycle hooks in your composable.

Simple DOM event listener

let scrollDir = "";
let lastScroll = window.scrollY || document.documentElement.scrollTop;
document.addEventListener("scroll", () => {
  let scr = window.scrollY || document.documentElement.scrollTop;
  scrollDir = scr > lastScroll ? "down" : "up";
  lastScroll = scr;
});

Simple, right? Compare the current scroll value to the previous one to determine up or down and store that value in the scrollDir variable.

This could be used in each component that needs the value, but that would get messy in a hurry. So let’s make it reusable by putting it in a composable and storing the directional value with useState.

export const useScrollDirection = () => {
  let lastScroll = window.scrollY || document.documentElement.scrollTop;
  document.addEventListener("scroll", () => {
    let scr = window.scrollY || document.documentElement.scrollTop;
    dir.value = scr > lastScroll ? "down" : "up";
    lastScroll = scr;
  });
  let dir = useState("scrollDirection", () => "");
  return dir;
};

Now we can use this composable in our components like this:

const scrollDirection = useScrollDirection();

Now… we have all the logic in one composable, and can access the information from anywhere in our application, except there’s one problem. This code will error due to the issue stated above. The composable will fire before the DOM is created, resulting in an error.

Lifecycle events in composable

Luckily, the solution is easy. Wrap the code within the onMounted lifecycle hook. This will make sure the DOM exists when we add the scroll event listener and solve our error.

export const useScrollDirection = () => {
  onMounted(() => {
    let lastScroll = window.scrollY || document.documentElement.scrollTop;
    document.addEventListener("scroll", () => {
      let scr = window.scrollY || document.documentElement.scrollTop;
      dir.value = scr > lastScroll ? "down" : "up";
      lastScroll = scr;
    });
  });
  let dir = useState("scrollDirection", () => "");
  return dir;
};

TADA! It took me way too long to figure out you could use lifecycle hooks in composables. Hopefully this helps someone out there that was like me.