What is the best way to dismiss a custom popover when clicking outside of it?

I'm currently developing a Cordova application. I have opted to utilize Vue.js and jQuery for bindings and scripts, while also taking on the responsibility of designing the user interface myself. While I've managed to implement page transitions and animations for elements like radio buttons and checkboxes, I've encountered difficulty when attempting to create a custom popover. Below is the code snippet I've been working with:

Vue.directive('popover', {
    bind: function(el, bindings, vnode) {
        $(el).click(function() {
            var pageEl = $(this).closest('.ui-page');
            pageEl.find('.drawer').toggleClass('active');

            $(el).closest('.ui-page').click(function(e) {
//                $('.drawer', this).removeClass('active');
            });
        });
    }
})

var pageInstance = new Vue({
    el: '#popover-page',
    data: {
        options: [1, 2, 3, 4, 5]
    }
})
// CSS styles here...
// JavaScript scripts here...

The current implementation successfully toggles the popover upon button click. Despite my exhaustive attempts, I haven't been able to figure out how to hide the popover when clicking outside of it.

Any guidance on how to achieve popover hiding functionality when clicking outside of it?

Answer №1

Please give this a shot. I've incorporated jQuery

$('body').click(function(e) {
  if (!$(e.target).closest('.drawer').length){
    $(".drawer").removeClass("active");
  }
});

$('body').click(function(e) {
    if (!$(e.target).closest('.drawer').length){
        $(".drawer").removeClass("active");
    }
});

Vue.directive('popover', {
    bind: function(el, bindings, vnode) {
        $(el).click(function(e) {
            e.stopPropagation();
            var pageEl = $(this).closest('.ui-page');
            pageEl.find('.drawer').toggleClass('active');

            $(el).closest('.ui-page').click(function(e) {                  
//                $('.drawer', this).removeClass('active');
            });
        });
    }
})

var pageInstace = new Vue({
    el: '#popover-page',
    data: {
        options: [1, 2, 3, 4, 5]
    }
})
html,
body {
    position: relative;
    width: 100%;
    height: 100%;
}

body {
    font-family: 'Open Sans';
    font-size: 16px;
    margin: 0;
    overflow: hidden;
}

* {
    box-sizing: border-box;
}

*, *:active, *:hover, *:focus {
outline: 0;
}

button {
    padding: 0;
}

img {
    max-width: 100%;
}

.ui-page,
.header,
.scroll-content {
    position: absolute;
    width: 100%;
    top: 0;
    left: 0;
    overflow: hidden;
}

.ui-page {
    height: 100%;
    background-color: #fff;
}

.page-content {
    position: relative;
    height: 100%;
    overflow-y: auto;
    z-index: 1;
}

.header {
    height: 54px;
    box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.12), 0 1px 8px 0 rgba(0, 0, 0, 0.2);
    background-color: #607D8B;
    color: #fff;
    display: flex;
    align-items: center;
    padding-left: 16px;
    padding-right: 16px;
    z-index: 1;
}

.scroll-content {
    bottom: 0;
    overflow: auto;
}

.scroll-content.has-header {
    top: 54px;
}

.header button {
    color: #fff;
    height: 100%;
}

.header .header-title {
    margin: 0 22px;
    font-size: 18px;
    font-weight: 600;
    width: 100%;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}

.header .buttons {
    position: relative;
    display: flex;
}

.header .buttons button {
    padding: 4px 8px;
}

.header .buttons button:last-child {
    padding: 4px 0 4px 8px;
}

.btn {
    position: relative;
    box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
    border: none;
    padding: 8px 16px;
    font-size: 16px;
    border-radius: 4px;
    font-family: unset;
    overflow: hidden;
}

.btn-clear {
    background-color: transparent;
    border: none;
}

.item {
    position: relative;
    display: flex;
    overflow: hidden;
    border-bottom: 1px solid #bdbdbd;
}

.drawer {
    position: absolute;
    background-color: #fff;
    z-index: 4;
    top: 60px;
    right: 4px;
    border-radius: 2px;
    box-shadow: 0px 2px 8px 2px rgba(0, 0, 0, 0.4);
    transform: scale(0, 0);
    transform-origin: top right;
    transition: transform ease 0.3s;
    min-width: 180px;
}

.drawer.active {
    transform: scale(1, 1);
}

.drawer .drawer-content {
    position: relative;
    padding: 4px 0;
}

.drawer .drawer-content:after {
    content: '';
    position: absolute;
    border: 8px solid transparent;
    border-bottom-color: #fff;
    top: -14px;
    right: 22px;
}

.drawer .item {
    padding: 12px 16px;
    font-size: 14px;
}

.drawer .item:last-child {
    border-bottom: none;
}
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>

<div class="ui-page" id="popover-page">
    <div class="page-content">
        <div class="header">
            <div class="header-title">
                Page Title
            </div>
            <button class="btn-clear" v-popover>Popover</button>
        </div>
        <div class="drawer">
            <div class="drawer-content">
                <div class="item" v-for="option in options">{{ option }}</div>
            </div>
        </div>
        <div class="scroll-content has-header">
            <div class="page-content">
                <p>Some Content</p>
            </div>
        </div>
    </div>
</div>

Answer №2

Define('toggle-popover', {
    attach: function(element, settings) {

        $(element).click(function() {
            $(element).closest('.ui-page').find('.drawer').toggleClass('active');
        });
        $('body').on('click', $(element).closest('.ui-page'), function(e) {
            var $drawer = $(element).closest('.ui-page').find('.drawer');
            var $target = $(e.target);

            if ($target.closest($(element)).length <= 0
                && $drawer.hasClass('active')
                && $target.closest('.drawer').length <= 0) {
                $drawer.removeClass('active');
            }
        });
    }
})

var pageInstance = new Page({
    el: '#popover-page',
    data: {
        items: [1, 2, 3, 4, 5]
    }
})
html,
body {
    position: relative;
    width: 100%;
    height: 100%;
}

body {
    font-family: 'Open Sans';
    font-size: 16px;
    margin: 0;
    overflow: hidden;
}

* {
    box-sizing: border-box;
}

*, *:active, *:hover, *:focus {
outline: 0;
}

button {
    padding: 0;
}

img {
    max-width: 100%;
}

.ui-page,
.header,
.scroll-content {
    position: absolute;
    width: 100%;
    top: 0;
    left: 0;
    overflow: hidden;
}

.ui-page {
    height: 100%;
    background-color: #fff;
}
/* Rest of the CSS code remains unchanged */

</script>

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

What is the proper method for setting initial values for scope upon loading the view using AngularJS and ngInit?

For the last few weeks, I've been immersing myself in AngularJS, studying large-scale applications to gain insights into real-world development practices. One common pattern I observed is the use of ng-init="init()" when loading a view - essentially c ...

utilizing an arrow function in the same manner as a traditional function

I am a fan of the new arrow ()=>{} syntax and would love to use it wherever possible. I understand that arrow functions point to the outer this context. Is there a way to modify an arrow function so that "this" points to its inner scope? For example, h ...

Utilizing CSS nested spans and setting custom width attributes

I'm curious about how nested spans and CSS handle the width attribute. In the HTML code provided, the 'wide' class sets the width while the 'box' class adds a border. I've noticed that the width is only applied when both class ...

Instead of constantly updating my stateful component, I prefer to create a new paragraph for each new state

Each time the Create Sales Order Button is clicked, an API call is made and the response is printed as a Stateful Component. Rather than updating the existing component, I want to generate a new paragraph for each response received so that if the user clic ...

Tips for effectively utilizing the Angular ngIf directive for toggling the visibility of elements

<div *ngFor = "let element of myElements, let i=index" [ngClass]="{'selected':element[i] == i}"> <li> Name: {{element.element.name}}</li> <li> Description: {{element.element.description}}</li ...

Executing a dynamic function following an ajax request

$('a.action').on('click',function(event){ event.preventDefault(); var classes = $(this).attr("class").split(/\s/); var action = classes[classes.length -1]; var url = "/api/" + action + "/" + $(this).attr('href'); ...

Is there a way to instantly show the contents of a newly opened tab in BootstrapVue?

Good day, my objective is as follows: Whenever a new tab is opened, it should become 'active' and display its content on the browser Issue: Currently, when a new tab is opened, the previous tab remains 'active'. Check out a simple e ...

Is it possible for a table row's cell to determine the value of the cell in the row above it?

Let me illustrate my issue with an example. Consider the following table: <table> <tr> <th>First</th><th>Second</th><th>Third</th> </tr> <tr> <td>A</td><t ...

The setInterval() function is not executing the IF Else statement correctly

I'm having an issue with accessing if else conditions. Currently, both the if block and else block are being called at the same time. I need help in making the if else block work properly. Here is a snippet of my code: const UserCard=(props)=>{ c ...

Steps to access a Windows folder after clicking on a link within an HTML page using Asp.net

I attempted to use the following code : <a href="file:///C:\Programs\sort.mw">Link 1</a> <a href="file:///C:\Videos\lecture.mp4">Link 2</a> Unfortunately, this code does not work in Google Chrome or Firefox bro ...

What is the process for importing specific modules from jQuery?

When working with webpack or browserify, what is the specific procedure required to import only the essential modules from jQuery as mentioned here? import {core, dimensions} from 'jquery' Unfortunately, this approach does not seem to be effect ...

Can you please verify my account by sending a confirmation email and entering my password?

I've been struggling to check in my category controller if a user is logged in, allow them to post a category. Otherwise, show them "You must be logged in to create a category." The problem I'm facing is that the category gets posted whether the ...

Radio button fails to be marked as selected

Currently, I am conducting a test on , and one of the tasks involves styling Radio Buttons using JavaScript (jQuery). Despite my repeated attempts to reconstruct the code, I always encounter the same issue: although JS successfully sets the input radio but ...

Data cannot be transferred to a child element unless it has been initialized during the definition phase

Passing an array data from parent to child component has brought up some interesting scenarios: parent.component.html: <child-component ... [options]="students" > </child-component> Status I: Setting the array on definition ...

Achieving consistent positioning for an image across resolutions and scales: A foolproof guide

Here is a sample of the div I am working with: <div id="information"> <a href="https://www.com.edu"><img src="https://com-bb-dev.com.edu/branding/themes/as_2012_orig/images/bbbanner.png" style="position: absolute;left: 296px;"></a> ...

Connect a function to create a new document element in order to modify the

I am attempting to intercept document.createElement in order to modify the value of the src property for each assignment. My current approach involves: var original = document.createElement; document.createElement = function (tag) { var element ...

Updates in .net modify values prior to the file being downloaded as a Word document

After dynamically filling my page values with jQuery, I am trying to generate a Word file. However, when the Word file is created, it does not reflect the new values brought in by jQuery. Here is the ASPX code: <form runat="server"> <asp:Label r ...

Is it possible to customize the color of the modal backdrop in layer v4 of the Material-UI library

I am struggling to modify the background color of an MUI V4 modal. Instead of getting a clean color, I am seeing a gray layer on top of the backdrop. Though this seems to be the default behavior, I want to remove it and target the shaded div directly rathe ...

When you hover over the button, a picture will appear

This is an example code snippet from W3Schools. It showcases an animated button that reveals a small arrow next to the text when hovered over. The specific line of code responsible for this effect is .button span:after {content: '\00bb'. How ...

Using React Material UI to implement a button function that triggers a file picker window

I have a customized Material UI button component that I want to use for selecting files from my computer upon clicking. However, my attempt to achieve this by nesting the <Button></Button> within a <label> associated with an <input typ ...