I am having trouble correctly toggling classes in my tab list using javascript.
At first click the menu is open and on second click on the same will close, but after close will not open again if I click on the same menu.
Check my example to see exactly what I mean:
var prev = '';
var tabsList = document.querySelectorAll('.main-col');
for (i = 0; i < tabsList.length; ++i) {
tabsList[i].addEventListener("click", function(){
var elements = document.getElementsByClassName('main-col');
for (j = 0; j < elements.length; ++j) {
elements[j].classList.remove("open");
}
var index = this.dataset.foo;
document.querySelectorAll('[data-foo="' + index + '"]')[0].classList.toggle("open");
if ( prev == this) {
this.classList.remove("open");
}
prev = this;
});
}
h3 {
cursor: pointer;
}
.main-col .menu {
max-height: 0;
overflow: hidden;
transition: max-height .5s cubic-bezier(0, 1, 0, 1);
}
.main-col.open .menu {
background: #333;
max-height: 500px;
transition: max-height .5s ease-in-out;
}
<div id="f-col1" class="main-col" data-foo="1">
<h3 class="title-col1">Column 1 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-1">
<ul id="column-1-container" class="menu">
<li id="menu-item-1" class="menu-item">LINK 1</li>
<li id="menu-item-1" class="menu-item">LINK 1</li>
<li id="menu-item-1" class="menu-item">LINK 1</li>
</ul>
</div>
</div>
<div id="f-col2" class="main-col" data-foo="2">
<h3 class="title-col2">Column 2 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-2">
<ul id="column-2-container" class="menu">
<li id="menu-item-2" class="menu-item">LINK 2</li>
<li id="menu-item-2" class="menu-item">LINK 2</li>
<li id="menu-item-2" class="menu-item">LINK 2</li>
</ul>
</div>
</div>
<div id="f-col3" class="main-col" data-foo="3">
<h3 class="title-col3">Column 3 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-3">
<ul id="column-3-container" class="menu">
<li id="menu-item-3" class="menu-item">LINK 3</li>
<li id="menu-item-3" class="menu-item">LINK 3</li>
<li id="menu-item-3" class="menu-item">LINK 3</li>
</ul>
</div>
</div>
PS: Please don't give me alternatives with jQuery to this little problem. :)
You could achieve this by the following:
// Iterate each tab (main-col) that the toggle behaviour is applied to
document.querySelectorAll('.main-col').forEach(function(tabItem) {
// Add click event listener to current tab to process toggle logic
tabItem.addEventListener("click", function() {
// Use the toggle method to toggle open class on and off
tabItem.classList.toggle('open');
// Select any currently open tabs, and remove the open class from tabs
// that are not the tab we're currently toggling
document.querySelectorAll('.main-col.open').forEach(function(openTabItem) {
if(openTabItem !== tabItem) {
openTabItem.classList.remove('open');
}
});
})
})
h3 {
cursor: pointer;
}
.main-col .menu {
max-height: 0;
overflow: hidden;
transition: max-height .5s cubic-bezier(0, 1, 0, 1);
}
.main-col.open .menu {
background: #333;
max-height: 500px;
transition: max-height .5s ease-in-out;
}
<div id="f-col1" class="main-col" data-foo="1">
<h3 class="title-col1">Column 1 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-1">
<ul id="column-1-container" class="menu">
<li id="menu-item-1" class="menu-item">LINK 1</li>
<li id="menu-item-1" class="menu-item">LINK 1</li>
<li id="menu-item-1" class="menu-item">LINK 1</li>
</ul>
</div>
</div>
<div id="f-col2" class="main-col" data-foo="2">
<h3 class="title-col2">Column 2 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-2">
<ul id="column-2-container" class="menu">
<li id="menu-item-2" class="menu-item">LINK 2</li>
<li id="menu-item-2" class="menu-item">LINK 2</li>
<li id="menu-item-2" class="menu-item">LINK 2</li>
</ul>
</div>
</div>
<div id="f-col3" class="main-col" data-foo="3">
<h3 class="title-col3">Column 3 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-3">
<ul id="column-3-container" class="menu">
<li id="menu-item-3" class="menu-item">LINK 3</li>
<li id="menu-item-3" class="menu-item">LINK 3</li>
<li id="menu-item-3" class="menu-item">LINK 3</li>
</ul>
</div>
</div>
IE11 solution, with no polyfill (forEach)...
let FcolsParent = document.getElementById("Parent_of_f-cols");
FcolsParent.onclick = function(e) {
if (!e.target.matches('h3')) return;
e.stopPropagation();
let
isOpen = e.target.parentNode.classList.contains('open'),
Open_Tabs = FcolsParent.querySelectorAll('.main-col.open')
;
for( let i=0, iMax=Open_Tabs.length; i<iMax; i++ ) {
Open_Tabs[i].classList.remove('open');
}
if (!isOpen) e.target.parentNode.classList.add('open');
}
h3 {
cursor: pointer;
}
.main-col .menu {
max-height: 0;
overflow: hidden;
transition: max-height 1.5s cubic-bezier(0, 1, 0, 1);
/* transition: max-height 1.5s ease-in-out; */
}
.main-col.open .menu {
background: #333;
max-height: 500px;
transition: max-height 1.5s ease-in-out;
}
<div id="Parent_of_f-cols">
<div id="f-col1" class="main-col" data-foo="1">
<h3 class="title-col1">Column 1 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-1">
<ul id="column-1-container" class="menu">
<li id="menu-item-1" class="menu-item">LINK 1</li>
<li id="menu-item-1" class="menu-item">LINK 1</li>
<li id="menu-item-1" class="menu-item">LINK 1</li>
</ul>
</div>
</div>
<div id="f-col2" class="main-col" data-foo="2">
<h3 class="title-col2">Column 2 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-2">
<ul id="column-2-container" class="menu">
<li id="menu-item-2" class="menu-item">LINK 2</li>
<li id="menu-item-2" class="menu-item">LINK 2</li>
<li id="menu-item-2" class="menu-item">LINK 2</li>
</ul>
</div>
</div>
<div id="f-col3" class="main-col" data-foo="3">
<h3 class="title-col3">Column 3 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-3">
<ul id="column-3-container" class="menu">
<li id="menu-item-3" class="menu-item">LINK 3</li>
<li id="menu-item-3" class="menu-item">LINK 3</li>
<li id="menu-item-3" class="menu-item">LINK 3</li>
</ul>
</div>
</div>
</div>
what about if a click will happen on one of your link ?
actually this remove too your ".open" class
let FcolsParent = document.getElementById("Parent_of_f-cols");
FcolsParent.onclick = function(e) {
if (!e.target.matches('h3')) return;
e.stopPropagation();
let isOpen = e.target.parentNode.classList.contains('open');
FcolsParent.querySelectorAll('.main-col.open').forEach( openTabItem=>openTabItem.classList.remove('open') );
if (!isOpen) e.target.parentNode.classList.add('open');
}
h3 {
cursor: pointer;
}
.main-col .menu {
max-height: 0;
overflow: hidden;
transition: max-height 1.5s cubic-bezier(0, 1, 0, 1);
}
.main-col.open .menu {
background: #333;
max-height: 500px;
transition: max-height 1.5s ease-in-out;
}
<div id="Parent_of_f-cols">
<div id="f-col1" class="main-col" data-foo="1">
<h3 class="title-col1">Column 1 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-1">
<ul id="column-1-container" class="menu">
<li id="menu-item-1" class="menu-item">LINK 1</li>
<li id="menu-item-1" class="menu-item">LINK 1</li>
<li id="menu-item-1" class="menu-item">LINK 1</li>
</ul>
</div>
</div>
<div id="f-col2" class="main-col" data-foo="2">
<h3 class="title-col2">Column 2 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-2">
<ul id="column-2-container" class="menu">
<li id="menu-item-2" class="menu-item">LINK 2</li>
<li id="menu-item-2" class="menu-item">LINK 2</li>
<li id="menu-item-2" class="menu-item">LINK 2</li>
</ul>
</div>
</div>
<div id="f-col3" class="main-col" data-foo="3">
<h3 class="title-col3">Column 3 with sub-menu (1st click open, 2nd close and from now on will not open)</h3>
<div class="column-3">
<ul id="column-3-container" class="menu">
<li id="menu-item-3" class="menu-item">LINK 3</li>
<li id="menu-item-3" class="menu-item">LINK 3</li>
<li id="menu-item-3" class="menu-item">LINK 3</li>
</ul>
</div>
</div>
</div>
Related
I'm new to vuejs and I want to create a menu with below html structure.
<ul>
<li>item 1
<ul class="list-unstyled">
<li>submenu 1-1</li>
<li>submenu 2-1</li>
<li>submenu 3-1</li>
</ul>
</li>
<li>item 2
<ul class="list-unstyled">
<li>submenu 1-2</li>
<li>submenu 2-2</li>
</ul>
</li>
</ul>
submenu's are hidden with css.
I want when I click on item 1 link, the child ul element will be toggled with slide down animation. And also this will happens for item 2 and others.
I wrote a demo for the first submenu. I use the transition component for the animation. you can also visit https://codesandbox.io/s/polished-butterfly-z1zf8?file=/src/App.vue:0-1263
<template>
<div id="app">
<ul>
<li>
item 1
<transition name="slideDown">
<ul class="list-unstyled" v-show="item1Visible">
<li>
<a href>submenu 1-1</a>
</li>
<li>
<a href>submenu 2-1</a>
</li>
<li>
<a href>submenu 3-1</a>
</li>
</ul>
</transition>
</li>
<li>
item 2
<ul class="list-unstyled" v-show="item2Visible">
<li>
<a href>submenu 1-2</a>
</li>
<li>
<a href>submenu 2-2</a>
</li>
</ul>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
item1Visible: true,
item2Visible: true
};
}
};
</script>
<style>
.slideDown-enter-active,
.slideDown-leave-active {
transition: all 0.5s ease-out;
overflow: hidden;
}
.slideDown-enter, .slideDown-leave-to /* .fade-leave-active below version 2.1.8 */ {
height: 0;
}
.slideDown-enter-to,
.slideDown-leave {
height: 50px;
}
</style>
In the following example, I have a header with logo-container on the left, menu in the middle and a button on the right. In the example, the menu has 5 top-level items, and 2 sub-menus.
<div class="container">
<div class="logo_container">
<img src="logo.png" />
</div>
<div id="top-navigation">
<div id="top-menu-nav">
<ul id="top-menu">
<li class="top-item">Top Item 1</li>
<li class="top-item">Top Item 2
<ul>
<li class="sub-item">Sub-Item 1</li>
<li class="sub-item">Sub-Item 2</li>
<li class="sub-item">Sub-Item 3</li>
<li class="sub-item">Sub-Item 4</li>
</ul>
</li>
<li class="top-item">Top Item 3
<ul>
<li class="sub-item">Sub-Item 5</li>
<li class="sub-item">Sub-Item 6</li>
<li class="sub-item">Sub-Item 7</li>
<li class="sub-item">Sub-Item 8</li>
</ul>
</li>
<li class="top-item">Top Item 4</li>
<li class="top-item">Top Item 5</li>
</ul>
</div>
<ul id="menu-button">
<li class="menu-button-cta">Button</li>
</ul>
</div>
</div>
As top-level items might be added or removed, I'd like to change the width of the parent element in accordance with the number of top-level items in menu.
For instance:
<ul id="top-menu"> has 5 <li class="top-item"> = .container {width: 100%;}
<ul id="top-menu"> has 4 <li class="top-item"> = .container {width: 90%;}
<ul id="top-menu"> has 3 <li class="top-item"> = .container {width: 80%;}
Is there a way to do it in CSS or jQuery?
You can do this in jquery using .children().length, eg:
$("ul.top-menu").each(function() {
$(this).addClass(".container-" + $(this).children(".top-item").length);
});
then css:
.container-5 { width: 100%; }
.container-4 { width: 90%; }
.container-5 { width: 80%; }
Here is my solution with jQuery. First, calculating length of children, then applying style accordingly.
var lengthOfChildren = $("#top-menu").children().length;
switch (lengthOfChildren) {
case 3:
$(".container").css("width", "80%");
break;
case 4:
$(".container").css("width", "90%");
break;
default:
$(".container").css("width", "100%");
}
I want to manually scroll a UL list with two button.
First time 4 items will show and when user click on up or down button list will scroll 1 item a time.
$(document).ready(function() {
$(".down").on("click", function() {
$(".scroll").scrollTop(
$(".listitem:nth-child(4)").offset().top -
$(".listitem:nth-child(3)").offset().top
);
});
});
.scroll {
height: 100px;
width: 200px;
overflow-y: hidden;
overflow-x: hidden;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<div class="container"><div class="row">
<div class="col-md-12">
<ul class="scroll">
<li class="listitem">List Item 1</li>
<li class="listitem">List Item 2</li>
<li class="listitem">List Item 3</li>
<li class="listitem">List Item 4</li>
<li class="listitem">List Item 5</li>
<li class="listitem">List Item 6</li>
<li class="listitem">List Item 7</li>
<li class="listitem">List Item 8</li>
</ul>
<button class="btn up">Up</button>
<button class="btn down">Down</button>
</div></div>
Now i am stuck i don't how i can dynamically pass the next and previous li to make it work . i want to do it exactly like that as i show in down button list scroll to next item.
You can use scrollIntoView() method to scrolls the element into the visible area. Note that :not(:nth-last-child(3)) would be the n - 1 visible list.
$(document).ready(function() {
$(".down").on("click", function() {
var $active = $(".listitem.active");
var $target = $active.next();
if ($target.get(0) && $target.is(':not(:nth-last-child(3))')) {
$active.removeClass('active');
$target.addClass('active').get(0).scrollIntoView({
behavior: 'instant',
block: 'start',
inline: 'nearest',
});
}
});
$(".up").on("click", function() {
var $active = $(".listitem.active");
var $target = $active.prev();
if ($target.get(0)) {
$active.removeClass('active');
$target.addClass('active').get(0).scrollIntoView({
behavior: 'instant',
block: 'start',
inline: 'nearest',
});
}
});
});
.scroll {
height: 100px;
width: 200px;
overflow-y: hidden;
overflow-x: hidden;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<div class="container">
<div class="row">
<div class="col-md-12">
<ul class="scroll">
<li class="listitem active">List Item 1</li>
<li class="listitem">List Item 2</li>
<li class="listitem">List Item 3</li>
<li class="listitem">List Item 4</li>
<li class="listitem">List Item 5</li>
<li class="listitem">List Item 6</li>
<li class="listitem">List Item 7</li>
<li class="listitem">List Item 8</li>
</ul>
<button class="btn up">Up</button>
<button class="btn down">Down</button>
</div>
</div>
</div>
I'm using jQuery.mmenu https://mmenu.frebsite.nl. My menu looks like this (Desktop menu):
<nav id="navigation">
<ul class="navigation list-inline d-flex justify-content-center">
<li>
Menu 1
<div class="list-inline d-flex justify-content-center">
<div class="container">
<div class="row">
<div class="col navigation-column pt-1 pb-4">
<h3>Kategoria</h3>
<ul>
<li>Submenu 1</li>
<li>Submenu 2</li>
</ul>
</div>
</div>
</div>
</div>
</li>
<li>Menu 2</li>
</ul>
</nav>
How can I get (mobile menu) plugin jQuery.mmenu:
<nav id="navigation">
<ul class="navigation list-inline d-flex justify-content-center">
<li>
Menu 1
<h3>Kategoria</h3>
<ul>
<li>Submenu 1</li>
<li>Submenu 2</li>
</ul>
</li>
<li>Menu 2</li>
</ul>
</nav>
Please help. I've been trying 2 days but I can not improve it.
For example I have menu with items:
1)
When I change browser's size, two last items merged two 1 dropdown
2)
3)
I need something like this. I have got Contacts_France | Contacts_Germany and when size will be decreased they are should be merged to one dropdown with name Contacts
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MAx-nax</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active">Home</li>
<li>About</li>
<li>Contact_France</li>
<li>Contact_Germany</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
</body>
</html>
A JSfiddle to easy resizable ability. Right now, there's some crazy switching in the more menu during the resize event. I have no idea why it's happening...
EDIT: Able to solve with #Santiago Rebella's solution here.
JSfiddle
var tele = document.getElementById('teleporter'),
rec = document.getElementById('receiver');
window.onresize = resize;
resize();
function resize() {
const rChildren = rec.children;
let numW = 0;
[...rChildren].forEach(item => {
item.outHTML = '';
tele.appendChild(item);
})
const teleW = tele.offsetWidth,
tChildren = tele.children;
[...tChildren].forEach(item => {
numW += item.offsetWidth;
if (numW > teleW) {
item.outHTML = '';
rec.appendChild(item);
}
});
}
#teleporter {
height: 20px;
overflow: hidden;
box-sizing: border-box;
padding: 0;
}
li {
float: left;
padding: 0 10px;
box-sizing: border-box;
list-style: none;
}
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div id="navbar" class="collapse navbar-collapse">
<ul id="teleporter">
<li>List item 0</li>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
<li>List item 4</li>
<li>List item 5</li>
<li>List item 6</li>
<li>List item 7</li>
<li>List item 8</li>
<li>List item 9</li>
<li>List item 10</li>
<li>List item 11</li>
<li>List item 12</li>
<li>List item 13</li>
</ul>
<div>More:
<ul id="receiver"></ul>
</div>
</div>
</div>
</nav>
<header>
<nav class="navbar navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
Logo
<button data-toggle="collapse" data-target="#navbar-collapse" class="navbar-toggle collapsed">
<span class="icon-bar top-bar"></span>
<span class="icon-bar middle-bar"></span>
<span class="icon-bar bottom-bar"></span>
</button>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav">`enter code here`
<li role="presentation">Germany</li>
<li role="presentation">France</li>
<li role="presentation">Poland</li>
<li role="presentation">Czech Republic</li>
<li role="presentation">Romania</li>
<li role="presentation">Moldova</li>
<li role="presentation" class="dropdown hidden-xs hidden-lg hidden-md">
<a class="dropdown-toggle" data-toggle="dropdown">Western Europe<b class="caret"></b></a>
<ul class="dropdown-menu hidden-xs hidden-lg hidden-md">
<li role="presentation">Germany</li>
<li role="presentation">France</li>
</ul>
</li>
<li role="presentation" class="dropdown hidden-xs hidden-lg hidden-md">
<a class="dropdown-toggle" data-toggle="dropdown">East Europe<b class="caret"></b> </a>
<ul class="dropdown-menu hidden-xs hidden-lg hidden-md">
<li role="presentation">Poland</li>
<li role="presentation">Czech Republic</li>
<li role="presentation">Romania</li>
<li role="presentation">Moldova</li>
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li role="presentation">Name</li>
<li role="presentation"></li>
<li role="presentation"><i class="glyphicon glyphicon-wrench"></i> </li>
<li role="presentation"><i class="glyphicon glyphicon-log-out"></i> </li>
<li role="presentation">Setting</li>
<li role="presentation">Log out</li>
</ul>
</div>
</div>
</nav>
<br/>
</header>
How to use this code for it.
var tele = document.getElementById('teleporter'),
rec = document.getElementById('receiver');
window.onresize = resize;
resize();
function resize() {
const rChildren = rec.children;
let numW = 0;
[...rChildren].forEach(item => {
item.outHTML = '';
tele.appendChild(item);
})
const teleW = tele.offsetWidth,
tChildren = tele.children;
[...tChildren].forEach(item => {
numW += item.offsetWidth;
if (numW > teleW) {
item.outHTML = '';
rec.appendChild(item);
}
});
}
#teleporter {
height: 20px;
overflow: hidden;
box-sizing: border-box;
padding: 0;
}
li {
float: left;
padding: 0 10px;
box-sizing: border-box;
list-style: none;
}
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div id="navbar" class="collapse navbar-collapse">
<ul id="teleporter">
<li>List item 0</li>
<li>List item 1</li>
<li>List item 2</li>
<li>List item 3</li>
<li>List item 4</li>
<li>List item 5</li>
<li>List item 6</li>
<li>List item 7</li>
<li>List item 8</li>
<li>List item 9</li>
<li>List item 10</li>
<li>List item 11</li>
<li>List item 12</li>
<li>List item 13</li>
</ul>
<div>More:
<ul id="receiver"></ul>
</div>
</div>
</div>
</nav>