Hiding a dropdown in Vuejs by clicking outside (component) - javascript

I'm creating a menu component on a Vuejs project. This menu has 2 dropdowns, I already created some methods and used Vue directives so when I click in 1 of them, the other hides and vice versa, I'd also like to know how to hide them by clicking outside.
I've tried 2 Vue libs but didn't work for me. Also, I'd like to do this manually if possible and not externally.
HTML:
<!-- menu -->
<div>
<ul>
<li><span>Home</span></li>
<li v-on:click="toggle1(), dropAreas =! dropAreas">
<span>Areas</span>
</li>
<li v-on:click="toggle2(), dropAdmin =! dropAdmin">
<span>Administration</span>
</li>
</ul>
</div>
<!-- /menu -->
<!-- dropdowns-->
<div v-if="dropAreas">
<ul>
<li>
<span>Kitchen</span>
</li>
<li>
<span>Baths</span>
</li>
</ul>
</div>
<div v-if="dropAdmin">
<ul>
<li>
<span>Profile</span>
</li>
<li>
<span>Services</span>
</li>
</ul>
</div>
<!-- /dropdowns-->
JS
data () {
return {
dropAreas: false,
dropAdmin: false
}
},
methods: {
toggle1 () {
if (this.dropAdmin === true) {
this.dropAdmin = false
}
},
toggle2 () {
if (this.dropAreas === true) {
this.dropAreas = false
}
}
}
*This code is being called in another component, "Home", like this:
<template>
<div>
<menu></menu>
<!-- [...] -->
</div>
</template>
Any ideas on how to do it manually? Is it possible? Thank you.

It's a bit of a hack, but you can do it using tabindex HTML attribute and :focus CSS pseudo-class:
new Vue({
el: '#app',
template: `
<div class="container">
<div
ref="menu"
id="menu"
tabindex="0"
>Menu</div>
<ul id="dropdown">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
`
});
#menu {
display: inline-block;
padding: 1em;
border: 1px solid #e6e6e6;
cursor: pointer;
}
#dropdown {
display: none;
}
#menu:focus + #dropdown {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app"></div>

Related

Get child element from element through an event with Vue

Goal
What I'm trying to achieve is that whenever the user clicks on the .menu-item element, the dropdown element .menu-item-dropdown will get a class that will show the element.
Problem
Currently, when I click on the .menu-item class element, the returned value from the console.log in showMobileNavDropdown() returns null. When clicking, the event.target element is .menu-item-main and not .menu-item. This happens only when I click on the text of the li. Otherwise this works as intended.
What would be the best way to include the li text so that I can still grab the .menu-item-dropdown class?
Vue
<template>
<div>
<nav class="nav">
<div class="nav-left">
<img class="logo" src="/images/logo.svg" alt="" />
</div>
<div class="nav-center">
<ul class="menu-items">
<li
class="menu-item"
#click="showMobileNavDropdown($event)"
>
<!-- Main Title On Nav -->
<a class="menu-item-main" href="#">Company</a>
<!-- Dropdown -->
<div class="menu-item-dropdown">
<ul>
<li class="">
About
</li>
<li class="">
Contact
</li>
</ul>
</div>
</li>
</ul>
</div>
<div class="nav-right">
Contact us
</div>
<div class="hamburger" #click="openMobileNav">
<span class="ham-line"></span>
<span class="ham-line ham-line-2"></span>
</div>
</nav>
<div class="mobile-nav"></div>
</div>
</template>
<script>
export default {
methods: {
openMobileNav() {
var mobileNav = document.querySelector(".mobile-nav");
mobileNav.classList.toggle("showMobileNav");
},
// This function
showMobileNavDropdown(event) {
console.log(event); // This element returns 'menu-item-main' and not 'menu-item'
console.log(event.target.querySelector(".menu-item-dropdown"));
},
},
};
</script>
Assuming you have a longer list of possible click-targets and can't or won't just use refs, I would use event-delegation and in the click-handler check, whether the clicked element is inside a clickable parent and based on that, toggle a class on the parent, which can then be used to select the child in CSS, for example to change it's opacity.
Vue.component('foo-bar', {
methods: {
toggleSubItemVisibility({ target }) {
const item = target.closest('.nav-item');
// we check because the user might have clicked on the surrounding ul
if (item) {
item.classList.toggle('active');
}
}
},
template: `
<nav>
<ul #click="toggleSubItemVisibility">
<li class="nav-item">
<span class="nav-label">Foo</span>
<div class="nav-sub-item">More details...</div>
</li>
<li class="nav-item">
<span class="nav-label">Bar</span>
<div class="nav-sub-item">More details...</div>
</li>
</ul>
</nav>`
})
new Vue({
el: '#app'
});
nav ul {
list-style-type: none;
display: flex;
flex-direction: row;
gap: 12px;
}
.nav-label {
cursor: pointer;
}
.nav-sub-item {
opacity: 0;
transition: opacity 1s ease;
}
.nav-item.active .nav-sub-item {
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<foo-bar></foo-bar>
</div>
The solution was to either grab the element, and if that returned null, grab the sibling. This way, it's always possible to select the .menu-item-dropdown element. When clicking on the element, it can either be a child or sibling.
Part of Solution
var element =
event.target.querySelector(".menu-item-dropdown") ||
event.target.nextElementSibling;
element.classList.toggle("hide_drop");

How can I make all elements work with onmouseover, onmouseout correctly?

My first go at making my own mini webpage from scratch, specifically looking to understand how onmouseover/onmouseout works using .display.opacity. I would like to hover my mouse over each element and it displays text specified text while the rest of the webpage disappears. Hope I gave enough information first time using StackOverflow.
my first element works perfectly with the function created showHilton().
however my second element and function "showInnovel();" does not
I currently have 3 elements and would like this to work with all if possible
<ul>
<li onmouseover="showHilton();" onmouseout="showHilton();"class="hiltonGrand" id="HGV">Hilton Grand Vacations</li>
<li id="hilton" style="opacity:0; background: black; color: white; text-align:right;">hello</li>
<li onmouseover="showInnovel();" onmouseout="showInnovel();"class="in" id="inSol">Innovel Solutions</li>
<li id="iS" style="opacity:0; background: black; color: white; text-align:right;">innovel</li>
<li class="DR" id="Dr">Diamond Resorts</li>
<li id="diamond" style="opacity:0; background: black; color: white; text-align:right;">Diamond</li>
</ul>
</div>
<div>
<!--Back button-->
<!--Home Button-->
<!--Customer Service-->
</div>
<script type="text/javascript">
var hilton = document.getElementById("hilton");
var innovel = document.getElementById("inSol");
var Dr = document.getElementById("Dr");
var hGV = document.getElementById("HGV");
var iS = document.getElementById("iS");
function showHilton() {
if(hilton.style.opacity == 0){
//element.style.display = "block";
hilton.style.opacity= 1;
innovel.style.opacity = 0;
Dr.style.opacity=0;
} else {
//element.style.display = "none";
hilton.style.opacity=0;
innovel.style.opacity=1;
Dr.style.opacity=1;
}
}
function showInnovel() {
if(iS.style.visibility == 0){
//element.style.display = "block";
iS.style.opacity = 1;
hGV.style.opacity = 0;
Dr.style.opacity=0;
} else {
//element.style.display = "none";
iS.style.opacity=0;
hGV.style.opacity=1;
Dr.style.opacity=1;
}
}
Your current setup uses a HTML structure like so:
<ul>
<li>Toggle 1</li>
<li>Content 1</li>
<li>Toggle 2</li>
<li>Content 2</li>
</ul>
This makes it quite hard to toggle the next element, whichc would require some js as you've already stated in your question.
I would re-work the html, to something like:
<ul>
<li>
Toggle 1
<p>Content 1</p>
</li>
<li>
Toggle 2
<p>Content 2</p>
</li>
</ul>
This way we can use native css to toggle the display property on the child elements to get the desired output:
/* Hide */
li > p {
display: none;
background: black;
color: white
}
/* Show on hover */
li:hover > p {
display: block;
}
<div>
<ul>
<li class="hiltonGrand" id="HGV">
Hilton Grand Vacations
<p>hello</p>
</li>
<li class="in" id="inSol">
Hilton Grand Vacations
<p>innovel</p>
</li>
<li class="DR" id="DR">
Hilton Grand Vacations
<p>Diamond</p>
</li>
</ul>
</div>
<div>
<!--Back button-->
<!--Home Button-->
<!--Customer Service-->
</div>

Display following 2 elements on button click

I have a ul li list.
On page load I want to hide all elements with class hide and on add more button i want to display next 2 elements and so on.
$(function() {
$('.hide').hide();
$('#add_more').on('click', function() {
// show
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<button id="add_more">Add next 2</button>
<ul>
<li class="show">1</li>
<li class="show">2</li>
<li class="hide">3</li>
<li class="hide">4</li>
<li class="hide">5</li>
<li class="hide">6</li>
<!-- Other elements -->
<li class="hide">n</li>
</ul>
To achieve this you can retrieve the last currently shown elements, then use slice() to get the following two and display them. Try this:
$(function() {
$('#add_more').on('click', function() {
$('.show:last').nextAll().slice(0, 2).toggleClass('show hide');
})
})
.hide { display: none; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<button id="add_more">Add next 2</button>
<ul>
<li class="show">1</li>
<li class="show">2</li>
<li class="hide">3</li>
<li class="hide">4</li>
<li class="hide">5</li>
<li class="hide">6</li>
<!-- Other elements -->
<li class="hide">n</li>
</ul>

onclick expand multiple menu

I want to make an onclick expand multiple menu in my website
Before I follow this thread: this with little bit modify I get:
<ul>
<rg><li id="auctions">Menu</li></rg>
<br></br>
<lf>
<li class="submenu">Left</li>
</lf>
<rg>
<li class="submenu">Right</li>
</rg>
</ul>
But it only shows a menu, then I create a duplicate like this:
<ul>
<rg><li id="auctions">Menu</li></rg>
<br></br>
<lf>
<li class="submenu">Left</li>
</lf>
<rg>
<li class="submenu">Right</li>
</rg>
</ul><ul>
<rg><li id="auctions2">Menu</li></rg>
<br></br>
<lf>
<li class="submenu2">Left</li>
</lf>
<rg>
<li class="submenu2">Right</li>
</rg>
</ul>
And JS and CSS like this:
<script>
$(function() {
$('#auctions').click(function(){
$('.submenu').slideToggle();
});
});
$(function() {
$('#auctions2').click(function(){
$('.submenu2').slideToggle();
});
});
</script>
<style>
.submenu{display:none;}
.submenu2{display:none;}
rg {float:right}
lf {float:left}
</style>
Its work but doesn't run inline. Then I useul {display:inline-block}
Yes, the menu running inline, but it's broken and float doesn't work properly. Can it's fixed? or can I make multiple menu in same <ul>?
make rg and lf as classes and change the html accordingly to get your desired style.
But I recommend, you should consider studying about ul and li tags and its properties before using it
$(function() {
$('#auctions').click(function() {
$('.submenu').slideToggle();
});
});
$(function() {
$('#auctions2').click(function() {
$('.submenu2').slideToggle();
});
});
ul {
display: block;
margin:0;
padding:0;
width:100%;
}
li {
list-style: none;
}
li.main {
width:100%;
text-align:right;
}
.submenu {
display: none;
}
.submenu2 {
display: none;
}
.rg {
float: right;
}
.lf {
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<ul>
<li id="auctions" class="main rg">Menu</li>
<li>
<ul>
<li class="submenu lf">Left</li>
<li class="submenu rg">Right</li>
</ul>
</li>
</ul>
</li>
<li>
<ul>
<li id="auctions2" class="main rg">Menu</li>
<li>
<ul>
<li class="submenu2 lf">Left</li>
<li class="submenu2 rg">Right</li>
</ul>
</li>
</ul>
</li>
</ul>

Keep UL visible when hovering from div to UL

I've got a custom header / nav where I have two elements that represent two sections of a website. I want to be able to hover over one of the elements and display a particular menu. However the Menu list isn't a child of the element. So I can't do it with CSS.
The problem i'm having is when I hover over the element, the menu shows. But I move my mouse to hover over the menu and and as soon as move away from the element the menu disappears. I have tried adding a display:block to the manu items, with a .delay() method running but there is still a slight flicker when moving the mouse away from the div.
Here is my current code:
//HTML
<header>
<a class='hoverOverOne'>Hover over me to show menu</a>
<a class='hoverOverTwo'>Hover over me to show menu</a>
<nav>
<ul id='menuToShow-One'>
<li>testing</li>
<li>testing</li>
</ul>
<ul id='menuToShow-Two'>
<li>testing</li>
<li>testing</li>
</ul>
</nav>
</header>
// jQuery
jQuery("a.hoverOverOne").hover(
function () {
jQuery('#menuToShow-One').slideDown('medium').delay(500);
},
function () {
jQuery('#menuToShow-One').slideUp('medium').delay(500);
});
jQuery("a.country").hover(
function () {
jQuery('#menuToShow-Two').slideDown('medium').delay(500);
},
function () {
jQuery('#menuToShow-Two').slideUp('medium').delay(500);
});
// CSS
#menuToShow-One{
display:none;
}
#menuToShow-Two{
display:none;
}
Any help would be much appreciated.
Thanks.
$(function(){
$("a.hoverOverOne, a.hoverOverTwo").hover(function () {
var menu = '#menu'+this.id;
$('.menu').not(menu).slideUp(0);
$(menu).slideDown('medium');
});
$("ul.menu").mouseleave(function () {
$(this).slideUp('medium');
});
});
a.hoverOverOne{
margin-right: 20px;
}
#menuToShow-One{
display:none;
}
#menuToShow-Two{
display:none;
}
nav{
display:inline-block;
}
ul li{
display:block;
}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<header>
<a class='hoverOverOne' id="ToShow-One">Hover over me to show menu 1</a>
<a class='hoverOverTwo' id="ToShow-Two">Hover over me to show menu 2</a><br />
<nav>
<ul id='menuToShow-One' class="menu">
<li>testing menu 1</li>
<li>testing menu 1</li>
</ul>
<ul id='menuToShow-Two' class="menu">
<li>testing menu 2</li>
<li>testing menu 2</li>
</ul>
</nav>
</header>
Check the following solution. No need for JS to power this one.
#hover-one:hover ~ nav ul#menuToShow-One,
#hover-two:hover ~ nav ul#menuToShow-Two {
max-height: 500px;
transition: 0.2s;
}
#menuToShow-One,
#menuToShow-Two {
max-height: 0;
transition: 0.2s;
transition-delay: 1s;
overflow: hidden;
}
nav:hover #menuToShow-One,
nav:hover #menuToShow-Two {
max-height: 500px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<header>
<a id="hover-one" href='#hoverOverOne'>Hover over me to show menu</a>
<a id="hover-two" href='#hoverOverTwo'>Hover over me to show menu</a>
<nav>
<ul id='menuToShow-One'>
<li>testing 1</li>
<li>testing 1</li>
</ul>
<ul id='menuToShow-Two'>
<li>testing 2</li>
<li>testing 2</li>
</ul>
</nav>
</header>

Categories