Harish Harish - 3 years ago 119
CSS Question

How do I close custom popover when click outside?

I'm developing a Cordova app. For that I'm using Vue.js and jQuery for bindings and script and I'm developing UI on my own. I could do page transitions and animations for most of the UI like, Radio buttons and check boxes etc. But I could not be able to develop a custom popover. I have tried following code.



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 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>





This works fine for toggling popover on button click. I tried so much. But I could not hide the popover when click on outside of the popover.

How can I hide popover when clicking out side of it?

Answer Source

Please try this. I have added 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>

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download