Why is it that a click event outside of an HTML element cannot be detected in this Vue 3 application?

I've been diving into building a Single Page Application using Vue 3, TypeScript, and The Movie Database (TMDB).

Currently, I'm focused on developing a search form within my project.

Within the file src\components\TopBar.vue, here's what I have:

<template>
  <!-- More code -->
  <form ref="searchForm" class="search_form w-100 mx-auto mt-2 mt-md-0">
    <div class="input-group">
      <input v-on:keyup="debounceMovieSearch" v-model="searchTerm" class="form-control search-box" type="text" placeholder="Search movies...">
      <div class="input-group-append">
        <button class="btn" type="button">
          <font-awesome-icon :icon="['fas', 'search']" />
        </button>
      </div>
    </div>

    <div v-if="isSearch" @click="isSearch = false" class="search-results shadow-sm">
      <div v-if="this.movies.length">
        <router-link v-for="movie in movies.slice(0, 10)" :key="movie.id" :to="`/movie/${movie.id}`">
          <SearchItem :movie="movie" />
        </router-link>
      </div>

      <div v-else>
        <p class="m-0 p-2 text-center">No movies found for this search</p>
      </div>
    </div>
  </form>
</template>

<script lang="ts">
  import { defineComponent, ref } from 'vue';
  import axios from 'axios';
  import env from '../env';
  import SearchItem from './SearchItem.vue';

  export default defineComponent({
        name: 'TopBar',

    components: {SearchItem},

    data() {
      return {
        searchForm: ref(null),
        isSearch: false,
        searchTerm: '',
        timeOutInterval: 1000,
        movies: []
      }
    },

    mounted() {
      this.windowEvents();
    },

    methods: {
      windowEvents() {
        window.addEventListener('click', (event) => {
          if (!(this.$refs.searchForm as HTMLElement).value.contains(event.target)){
            console.log('click outside');
          }
        });
      },
      
      debounceMovieSearch() {
        setTimeout(this.doMovieSearch, this.timeOutInterval)
      },

      doMovieSearch() {
        if (this.searchTerm.length > 2) {
            this.isSearch = true;
            axios.get(`${env.api_url}/search/movie?api_key=${env.api_key}&query=${this.searchTerm}`).then(response => {
            this.movies = response.data.results;
          })
          .catch(err => console.log(err));
        }
      },
    }
  });
</script>

https://i.stack.imgur.com/4FfRLl.jpg

The main objective is to close the search results list when the user clicks outside of the form. To achieve this functionality, I've implemented the following snippet of code:

windowEvents() {
 window.addEventListener('click', (event) => {
  if (!(this.$refs.searchForm as HTMLElement).value.contains(event.target)){
    console.log('click outside');
  }
 });
}

The Issue at Hand

The approach above triggers an error stating:

Property 'value' does not exist on type 'HTMLElement'.

Queries to Ponder

  1. What might be causing this issue?
  2. How can this problem be reliably rectified?

Answer №1

It seems like you've mixed up the ways to declare template refs.

In the composition API, you create a template ref by defining a ref and assigning it as a variable with the ref attribute on a tag. Vue will then connect the node with the variable, giving you access through the .value property of the ref.

However, in the options API, you simply set the ref attribute on the tag and use this.$refs to reference the node directly. Here, there is no need for the .value property.

Since you are using the options API, the correct line should be:

 if (!(this.$refs.searchForm as HTMLFormElement).contains(event.target as Node|null)) { // no ".value"

You can also eliminate the searchForm ref from the data section.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

All file upload requests consistently result in a status code of 400

I am encountering an issue with file uploading in Express. Every time I send a request with multipart/form-data, I receive a 400 bad request response with no error message, just an empty object. Currently, I am using busboy-body-parser for parsing multipar ...

Invoke a method on a child component once an event has been emitted

Currently, I am working on creating a custom button component that includes a spinner. The functionality works well - the button gets disabled and displays a spinner upon click. However, I am facing issues when it comes to resetting the button's state ...

Utilize the power of passing multiple parameters in Vue.js methods while working on a Razor page

I am working on an asp.net core7 program that incorporates vue.js. My query pertains to passing parameters to the methods in vue.js. <i v-on:click="DeleteSale('@item.ID','@item.Image')" class="far fa-trash-alt cursor ...

Observing the closing of a modal window from a different controller in AngularJS

Within my main controller, I have a function called $scope.showDialog: $scope.showDialog = function(ev) { $mdDialog.show({ controller: 'DialogController', templateUrl: 'partials/dialog.tmpl.ejs', targetEvent: ev ...

What does the "Undefined" group label mean in Vue-Multiselect?

I recently started learning Vue and have revamped this code based on information from different tutorials. However, I am encountering an issue with the group name showing as "Undefined" here. .html: <multiselect v-model="value" :options="op ...

The shopping cart in our e-commerce website is refreshed in real-time thanks to the integration of J

I am currently enhancing the Codeigniter Cart with JQuery by making an Ajax call for updates. Below is my JQuery function: $(function() { $('.cart_form select').on('change', function(ev) { var rowid = $(this).attr('c ...

In what way can a container impact the appearance of a child placed in the default slots?

Visiting the vue playground. The main goal is for the container component to have control over an unspecified number of child components in the default slot. In order to achieve this, it's assumed that each child component must either hold a propert ...

Unable to locate the 'react-native' command, attempted various fixes but none were successful

Working on an older react native project that was functioning perfectly until I tried to pick it back up and encountered a problem. https://i.stack.imgur.com/1JUdh.png This issue revolves around the package.json file. https://i.stack.imgur.com/v6ZEf.png ...

What are some effective methods for handling error objects in REST API services?

Encountered an error object: Error: ER_ACCESS_DENIED_ERROR: Access denied for user 'root'@'localhost' (using password: YES) Type of (err): Object Now, I am looking to pass this object to another web service (REST API) What content ty ...

Cannot retrieve the <li> element from the array

I am trying to display list items inside an ordered list (ul) from an array, but I am facing issues with it. Whenever I try to map through the array, I encounter an error message saying Map is not a function. Any assistance on resolving this would be hig ...

"Unexpected behavior: NextAuth is failing to return defined custom scopes

I am currently working on a NextJS project that utilizes NextAuth. Initially, everything was functioning properly with the default scopes. However, my project now requires additional claims, which are listed in the supported scopes here. "scopes_supporte ...

What is the process of transforming a basic JavaScript function into a TypeScript function?

As a Java developer diving into TypeScript for frontend development, I've encountered a simple JavaScript code snippet that I'd like to convert to TypeScript. The original JavaScript code is: let numbers = [123, 234, 345, 456, 567]; let names = ...

Please provide the text input for the specified version numbers (1.1, 1.2, 3.0, etc.)

I'm currently working on a form that includes a section for searching by version number: <label class="formLabel">Version</label> <html:text property="valueVersion" styleClass="value" tabindex="11"/> <br/& ...

Fetching JSON object from a node.js/express server using AJAX request

I've implemented server-side code to fetch data from an external website and return a JSON object to the client side of my node.js/express application. The intention is to further process this JSON on the client side. Within my index.js file, I have ...

JavaScript has encountered a syntax error

When working on an animation in javascript, I encountered a problem that I can't seem to identify. I am attempting to make the pan function work with the "mover" function, but it seems like either I am not using the properties correctly within the "tr ...

Is there a way to locate a null string within this data arrangement?

I am looking to create functionality where, when a button is clicked, the application checks the state and takes different actions based on the result. Specifically, I want to ensure that there are no empty "value" fields, and if there are, redirect to ano ...

How to extract data from URLs in Angular

Looking into how to extract a specific value from the URL within Angular for parsing purposes. For example: http://localhost:1337/doc-home/#/tips/5?paginatePage=1 The goal is to retrieve the value "5". HTML snippet: <a href="#/tips/comments/{{ tip ...

Error occurs in console when using .getJSON with undefined JSON, but does not happen when using embedded data

Can someone help me understand why I'm getting an 'undefined' response when I use console.log(tooltipValues), but there's no issue with console.log(tooltipJSON). I've noticed that when I embed the data directly in my JS code, ever ...

Another approach to utilize JavaScript for populating content into a <div> container?

Upon loading the page, I aim to display a message in the <div> element. Below is the HTML and JavaScript code I have implemented: <body onload="printMsg()"> <div id="write"></div> </body> function printMsg() { var no ...

Ignore one specific file when importing all files in Angular 7

In my Angular 7 project, I am utilizing C3 and importing all the necessary files at the beginning of my .ts component file using a wildcard. import * as c3 from 'c3'; While this method works well overall, I encountered an issue where my CSS ove ...