Harish Kommuri
Harish Kommuri

Reputation: 2854

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?

Upvotes: 0

Views: 1405

Answers (2)

JiangangXiong
JiangangXiong

Reputation: 2426

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

            if ($target.closest($(el)).length <= 0 
              && $drawer.hasClass('active') 
              && $target.closest('.drawer').length <= 0) {
                $drawer.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>

Upvotes: 1

ankita patel
ankita patel

Reputation: 4251

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>

Upvotes: 1

Related Questions