Using querySelector() in vue Compostion API

Viewed 1355

I'm new to Vue composition API and need to use the Document method querySelector. However, it is not working as expected. If i write

<nav class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">

<script setup>
import { ref } from "vue";
const observer = new IntersectionObserver(handleIntersection);
const target = document.querySelector(".home-menu");
observer.observe(target);
console.log(target);

target is null. Reading docs I see the ref attribuute and if I

<nav ref="navbar" class="home-menu pure-menu pure-menu-horizontal pure-menu-fixed">
<script setup>
import { ref } from "vue";

const target = ref("navbar");
console.log(target);

I do console an object. Is ref the way that you get a DOM element in composition API? Can I now use target in my observer object? Is it equivalent to querySelector? I tried this

import { ref } from "vue";
const observer = new IntersectionObserver(handleIntersection);

const target = ref("navbar");
observer.observe(target);

but got error

Uncaught TypeError: IntersectionObserver.observe: Argument 1 does not implement interface Element.

Thanks.

2 Answers

The reason you document.querySelector call is returning null is that the DOM does not contain that element yet. The script setup runs when the component is created, so the component's DOM has not been created yet.

You can use the onMounted lifecycle hook to run code when the component has been mounted. I created a playground to demontrate this.

From the docs:

For example, the onMounted hook can be used to run code after the component has finished the initial rendering and created the DOM nodes

To achieve what you want, do the following:

  • Create a ref:

    const navbar = ref(null);
    
  • Connect the ref variable to the element by setting the ref attribute on the element to the name you used for the ref variable:

    <nav ref="navbar" ...>
    
  • Connect the observer when the component is mounted:

    onMounted(() => {
      observer.observe(target);
    })
    
  • Disconnect the observer when the component is unmounted:

    onBeforeUnmount(() => {
      observer.disconnect();
    })
    

This is the lifecycle diagram from the linked docs (the background is the wrong color due to Imgur/SO):

Vue lifecycle diagram

You can use the ref approach, calling the variable with the same name you declared the ref in the template:

const navbar = ref(null);

However, you should await the component to be mounted to observe:

onMounted(() => {
  observer.observe(target);
})

remember also to disconnect it when you unmount the component:

onBeforeUnmount(() => {
  observer.disconnect();
})
Related