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

The use of Ajax post results in the retrieval of multiple arrays containing objects that possess various values

I have a PHP file (ajax.php) that retrieves messages from a database and a JavaScript file (main.js) that sends an AJAX request to this PHP file. My goal is to create a table row in the JS file for each message returned by the PHP file. Main.js: functio ...

What steps should be taken to create a two-column table from a given list of items?

I am trying to display a list of words in two columns, one word after another from left to right. Here is the desired table structure: <table id="wordTable"> <tr> <td>ac </td> <td>bd </td> </tr> ...

Encountering a syntax error when attempting to generate a div dynamically with jQuery

I am in the process of creating a view application form using Bootstrap after submitting a form. Initially, I created it utilizing two 'div' elements. Now, I am exploring how to dynamically generate a div upon clicking a button: <i> Sectio ...

Custom directives are designed to receive arrays as string inputs

I've encountered an issue with my custom directive that has an isolated scope. When I pass an Array variable to the directive, it is being treated as a String inside the directive. This is how my directive looks: angular.module('my.directives& ...

Is there a way to access a JSON node dynamically without relying on the eval function?

Path variables can be unpredictable, ranging from just a to a/b, and even a/b/c. My goal is to dynamically reach a node based on the path provided. The code snippet below achieves this, but I am open to exploring alternative methods that do not involve usi ...

The UI router fails to render the template

I've recently started working with ui-router, but I'm facing an issue where nothing shows up in the ui-view. To simplify things, I even tried adding it to Plunker but still couldn't get it to work. Here's a link to my project: https://p ...

What is the best way to iterate through my array of objects using a forEach loop and assign a value to the property of any object that has an empty string?

Inquiry for Part 1: I am currently exploring the use of forEach loop to iterate through an array of objects. My goal is to update the property "profile_image_url" of objects that have an empty string as its value, setting it to a default link ("/media/arti ...

A Vue filtering feature that consistently adds 5 additional elements upon each use

I was wondering, how can I create a computed property filter function that always adds 5 more elements to the current array? Here are more details: Template: <span class="box-content" v-for="item in activeItems" :key="item.id"> <img class=" ...

Access and retrieve numerous variables from the data object sent back through an ajax request

When using jQuery to make an ajax call to an MVC controller, the goal is to return multiple variables from the controller. What is the best way to package this data in the controller and then extract it using jQuery? ...

Adding roles in ReactJS: A step-by-step guide

I am looking to enhance the admin's roles upon login to enable the creation of new users. Unfortunately, I am uncertain on how to add these roles and make them functional. Below is the code I have: Login.js class Login extends Component { cons ...

The identical page content is displayed on each and every URL

Implementing a multi-step form in Next JS involves adding specific code within the app.js file. Here is an example of how it can be done: import React from "react"; import ReactDOM from "react-dom"; // Other necessary imports... // Add ...

Step-by-step guide to selecting a specific point on an HTML5 canvas using Python's selenium webdriver

Looking to automate interactions with a simple HTML5 application on a website using Selenium webdriver in Python with Firefox on Linux. The challenge is clicking a button on an HTML5 canvas, then dragging one or two objects around the canvas post-button cl ...

Steps to prevent uib-timepicker from automatically adjusting time based on the Browser Timezone

Currently in my database, timestamps are stored in UTC format. On the frontend, I am implementing uib-timepicker for time editing and updating purposes. However, I do not want uib-timepicker to automatically convert the time from the server's timezone ...

The history.push function seems to be leading me astray, not bringing me back

Issue with History.Push in Register Component App function App() { const logoutHandler = () =>{ localStorage.removeItem("authToken"); history.push("/") } const [loading, setLoading]= React.useState(true) useEffect(()=>{ ...

Reacting with Angulatory: Response heads are not being transferred in applications

I'm facing an issue where the cookie containing my authentication token doesn't get passed along with my requests. After authenticating in Chrome, the response sets the cookie correctly, but it's not included in subsequent requests. This is ...

Having trouble finding the right path. Is there an issue with Angular routing?

After successfully creating a simple application, I decided to write test cases for it. My first attempt was to work on the routing part of the application. Check out the code on StackBlitz The routing code snippet is as follows: Main module: export cons ...

Activate Click, or Pop-up Box

I've been attempting to log in to the Udemy site using JavaScript, but I'm having trouble triggering the click action on the "log in" link. Unfortunately, the .click() method doesn't seem to be working when I try to select the element. The l ...

Issues with receiving data on $.ajax POST request

If you'd like to check out my website Please use the following login details (case sensitive): Username: stack Password: stack Click on the tab labeled "yourhours." The main goal is to send all input box data to a database. At the moment, I am fo ...

Make sure to bind by clicking on a list item in a similar way to using

I currently have a dropdown component that allows users to select a language for localization. <template> <div class="localeDropdown"> <dropdown v-model="selectedLocale" :items="locales" :togg ...

Converting an array of numbers into an object using JSON

When I use jQuery to encode an array, this is the JSON I receive: {"1":{"name":"11233","po":"121212","po_item_number":"000001","po_item_material_code":"material","po_item_description":"assemble","sales_order":"11000000","sales_order_item":"10","tracable": ...