How can I make a Vue component close when clicking outside of it?

I am searching for a solution to automatically close a component when there is a click outside of the element.

I attempted to use an addEventListener for this purpose. Although it successfully closes the component, it encounters an issue where it fails to reopen once closed.

window.addEventListener('click', function(e){

if (document.getElementById('shopcartpreview').contains(e.target)){
console.log("Clicked in Box");


} else{
console.log("Clicked outside Box");
$('#shopcartpreview').hide();
 }
 })

Is there a better approach to handle this scenario?

<template>
    <div id="shopcartpreview"  v-if="carthover">
        <div class="cartitem" v-for="item in cartitems">
            <div class="cartitempic"><img class="productImg" width="80px" height="80px" v-bind:src="'assets/products/' + item.image"></div>
            <div class="cartitemdetails">
                <div class="cartitemname">{{item.name}}</div>
                <div class="cartitemqty">1 X </div>
                <div class="cartitemprice">€{{item.unit_price}}</div>
            </div>
            <div class="cartitemdelete">
                <img src="assets/images/icon-bin.png" width="15px" height="15px">
            </div>
        </div>

        <div class="carttotal">
            <div class="carttotaltext">TOTAL:</div>
            <div class="carttotalprice">€2,860.00</div>
        </div>
        <div class="cartcheckouttbn">PROCEED TO CHECKOUT</div>
        <div class="viewcart">VIEW CART</div>



    </div>    
</template>
<script>
    module.exports = {
        data: function () {
                return{ 
                    cartitems: 0,
                    carthover: false,
                }
            },
            created(){
            EventBus.$on('addToCart', (payload) =>{
                this.cartitems = payload
            }),
            EventBus.$on('mouseover', (carthover) =>{
            this.carthover = carthover
            })
        }
    }
</script>

Answer №1

I added a div tag to the component's structure, located at the bottom:

<div v-if="isPopup" class="outside" v-on:click="away()"></div>

The CSS styling for the .outside class is defined as:

.outside {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0px;
  left: 0px;
}

The away() function is included in the Vue instance to handle the click event:

away() {
 this.isPopup = false;
}

Simple and effective solution.

Answer №2

2020.11.10 Update

I realized that the solution I provided earlier had numerous errors, prompting me to make necessary updates.

There are various ways to close a component by clicking outside of it.

One approach is to utilize libraries specifically designed to handle this issue, such as simplesmiler/vue-clickaway, which is also incorporated in nuxt if you examine the source code.

Alternatively, if you prefer a manual implementation, here is the code:

onClickOutside ( event: Event ) {

    const path = event.path || (event.composedPath ? event.composedPath() : undefined)
    // check if the MouseClick occurs inside the component
    if (path && !path.includes(this.em) && !this.em.contains(event.target as HTMLElement)) {
      this.closeThisComponent() // method to close the component
    }
  }

Subsequently, you need to bind this eventHandler onClickOutside to the document.documentElement after opening your component and remove it from document.documentElement upon closing the component.

It is crucial to consider the timing and understand the event loop of JavaScript, distinguishing between MicroTasks and MacroTasks.

For instance, when opening the component:

openThisComponent () {
    this.showThisComponent = true // code to open the component

    // You can also utilize Vue.$nextTick or setTimeout
    requestAnimationFrame(() => {
      document.documentElement.addEventListener('click', this.onClickOutside, false)
    })

  }
closeThisComponent () {
    this.showComponent = false // code to close the component
    document.documentElement.removeEventListener('click', this.onClickOutside, false)
  }

Answer №3

Here's a helpful Demo Fiddle: https://jsfiddle.net/bq8m4fhe/

If you want to create a clickoutside directive, check out this resource: Detect click outside element

module.exports = {
    data: function() {
        return {
            cartitems: 0,
            carthover: false
        };
    },
    directives: {
        clickoutside: {
            bind: function(el, binding, vnode) {
                el.clickOutsideEvent = function(event) {
                    // Check if click was outside the element and its children
                    if (!(el == event.target || el.contains(event.target))) {
                        // Call the method provided in the attribute value
                        vnode.context[binding.expression](event);
                    }
                };
                document.body.addEventListener("click", el.clickOutsideEvent);
                document.body.addEventListener("touchstart", el.clickOutsideEvent);
            },
            unbind: function(el) {
                document.body.removeEventListener("click", el.clickOutsideEvent);
                document.body.removeEventListener("touchstart", el.clickOutsideEvent);
            },
            stopProp(event) {
                event.stopPropagation();
            }
        }
    },
    created() {
        EventBus.$on("addToCart", payload => {
            this.cartitems = payload;
        }),
        EventBus.$on("mouseover", carthover => {
            this.carthover = carthover;
        });
    }
};

To use the directive, implement it like this:

<div id="shopcartpreview"  v-if="carthover" v-clickoutside="SHOPPING_CART_HIDE_FUNCTION">

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

Processing made easy with jQuery

In my HTML page, I have multiple rows that represent records from a database. Each row contains input fields and a link at the end. When I click on the link, I need to capture the values of the input fields within the same row. Here is an example of the H ...

Improving animation performance on mobile devices using AngularJS

I've reached the final stages of developing a mobile application using AngularJS wrapped in a cordova webview. However, I'm encountering some issues with the panel transition animations. After experiencing strange behavior with ngAnimate, I deci ...

Only the initial upload file is being passed through the Apollo Express server, with the remaining files missing in action

Currently, I am utilizing the apollo-express server with GraphQL. One issue I am encountering involves a mutation where I pass files from the front-end to the back-end. Strangely, I receive the file:{} object only for the first file - for the others, I rec ...

javascript various backgrounds on click

I have implemented a list to allow users to select their top 3 choices, and I am using JavaScript to track these selections and change the background color accordingly. 1st selection -> Green background 2nd selection -> Yellow background 3rd sel ...

Guide on adjusting the resolution/density of images in JPEG/PNG using JavaScript

I am looking for a way to adjust the resolution/density of JPG/PNG images using JavaScript. The purpose of this adjustment is to provide accurate metadata on the number of pixels per inch (DPI/PPI) to be used for printing by a third-party API. Is there a ...

Important notice: It is not possible to assign refs to function components. Any attempt to do so will result in failure. If you intended to assign a ref, consider

My console is showing a warning when I use the nextJs Link component. Can someone assist me in resolving this issue and providing an explanation? Here is the message from the console: https://i.stack.imgur.com/jY4FA.png Below is a snippet of my code: im ...

Navigational highlighting of current page section on a one-page website

I'm struggling with adding a navigation bar feature that will highlight the current section being viewed on my website. All I want is for the currently viewed section to appear bold in the navigation bar. Check out the codepen link: HTML <na ...

What are the best methods for utilizing scrollbars to navigate a virtual canvas?

I am interested in developing a unique jQuery plugin that can simulate a virtual HTML5 Canvas, where the canvas is not physically larger than its appearance on the page. However, the content intended for display on the canvas may be much larger and will ne ...

evt.target consistently returns the initial input within the list of inputs

My React file uploader allows users to attach multiple file attachments. Each time a user clicks on an input, I retrieve the data-index to identify the input position. renderFileUploader() { let file_attachment = this.state.file_attachment.map(fun ...

Issues with reading response headers in AngularJS when using Apiary.IO?

I am attempting to mock my API using Apiary.io, but I am facing an issue where I cannot retrieve any headers from the response object in angularJS. I have verified that at least Content-Type: application/json is correctly set up by checking in firebug. The ...

Tips for concealing the final click (add) tab after it has been clicked and revealing the hidden (add) button when the user clicks on the remove button

I have created a form where users can add tests. Everything is working smoothly but I want the previous "Add Another Test" field to be removed when the user clicks on it and a new field to be shown. Everything is running well, but the issue is that when t ...

Using setInterval on a batch of freshly generated div elements

I am interested in creating a small website where I can display multiple clocks for various time zones. However, I have encountered an issue with the setInterval function. Below is the code snippet: function addClock () { $("#container").append('& ...

the object '[object Object]' of a distinct supporting nature

I encountered an error stating "ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays." This is my TypeScript file: this.list = data.json(); ...

Convert grouped data in Javascript into a JSON array

After implementing the code snippet provided below, I successfully managed to group objects from my existing dataset using Underscore JS. The grouped data is displayed in distinct groups as depicted by the output: {Group1: Array[10], Group2: Array[13], G ...

Error: selenium web driver java cannot locate tinyMCE

driver.switchTo().frame("tinymce_iframe"); String script="var editor=tinyMCE.get('tinymce_textarea');"; JavascriptExecutor js=(JavascriptExecutor) driver; js.executeScript(script); I'm encountering a WebDriverException while try ...

nuxt-auth is experiencing difficulties retrieving token information through the refresh provider

I'm currently facing challenges with the login functionality in Nuxt 3. To handle user authentication, I've installed the package @sidebase/nuxt-auth. Below are my configurations set in the file nuxt.config.ts: auth: { globalAppMiddleware: t ...

Solving the challenge of converting images to text using react-native and the @react-native-ml-kit/text-recognition package

As I work on my react native project, I have encountered a challenge. I am trying to implement a feature that allows users to either take a photo or select one from their library. Once the image is chosen, I want to extract all the text from it and display ...

Using React hook form to create a Field Array within a Dialog component in Material UI

I have a form with custom fields added via Field Array from react-hook-form, and everything is functioning properly. However, I recently implemented drag and drop functionality for the property items to allow reordering them. Due to the large number of fie ...

Tips for adjusting the autocomplete maxitem dynamically

I am working on a multi autocomplete feature and I have the following code. Is there a way to dynamically set the maximum number of items based on the value entered in another text field? <script> $(function () { var url2 = "<?php echo SI ...

Tips for creating a countdown timer from scratch without relying on a plugin

Looking at this image, it is clear that jQuery can be used. However, I am wondering if there is a way to create a countdown timer without relying on plugins. Can anyone offer some guidance? ...