I have this drop down menu that appears when a button is clicked and disappears when clicked again. I'm wondering how to make the menu disappear from the bottom up, instead of just instantly disappear. I'm not asking for the exact code, just need pointed in the right direction. Thank you.
let nav = document.querySelector("nav");
let icon = document.querySelector(".mobile-icon");
console.log(nav);
icon.addEventListener("click", showMenu);
function showMenu() {
if (nav.style.display == "none"){
nav.style.display = "block";
} else {
nav.style.display = "none";
}
}
My solution would be to create a class that sets the hight of the menu to 0px and then toggle this on and off using JavaScript.
I have created a small mockup in JSFiddle with some comments on the important parts.
Link to JSFiddle is - https://jsfiddle.net/gmuz2m98/
Here is the code though:
HTML -
<button> Hide/Show </button>
<ul>
<li> Link 1 </li>
<li> Link 2 </li>
<li> Link 3 </li>
<li> Link 4 </li>
</ul>
CSS -
ul {
/* Give the ul a transition to run smoothly */
transition: all .5s;
list-style:none;
padding:0;
margin:20px 0px;
background-color:#D6D6D6;
/* Make sure overflow is hidden so when the hight is droped to 0px the li elements stay hidden */
overflow:hidden;
/* Give the ul a default hight to revert to, without this the transiton won't work */
height:160px;
}
li {
padding: 10px 20px;
}
/* This is the class that will be added with JavaScript */
.hide {
height:0px;
}
JS -
// Assign the ul and the button to a variable
var ul = document.getElementsByTagName('ul')[0],
btn = document.getElementsByTagName('button')[0];
// Create a function that lets the class 'hide' be toggled on and off the ul
function dropDown(){
ul.classList.toggle('hide');
}
// Assign the function to the button with an EventListener
btn.addEventListener('click',dropDown,false);
Here is my solution, you cannot use transition for height property hence we use max-height. The problem is we need to set height in px for this solution to work, hence please use this workaround and test!
let nav = document.querySelector("nav");
let icon = document.querySelector(".mobile-icon");
icon.addEventListener("click", showMenu);
nav.querySelector("ul").style.margin = "16px 0px";
nav.style.maxHeight = nav.querySelector("ul").clientHeight + 32 + "px";
function showMenu() {
nav.classList.toggle("hideThis");
}
nav{
transition: max-height 0.5s ease-out;
height:auto;
overflow:hidden;
}
nav.hideThis{
max-height:0px !important;
}
<button class="mobile-icon">toggle</button>
<nav>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</nav>
Related
So, I am trying to make navbar swap colors of text and background when scrolled, with the following code it seems that background part is working just fine and it switches to white and back to gray, but when I literally changed font color in the next line, it just doesn't work. All the links are grouped in a list with the id = "nav_links". What's the deal with this? Thanks in advance!
function myFunction() {
if (document.body.scrollTop >= 0) {
console.log(document.body.scrollTop);
nav.style.backgroundColor = "white";
document.getElementById('nav_links').style.color = 'rgb(25,25,25)';
if (document.body.scrollTop === 0) {
nav.style.backgroundColor = 'rgb(25,25,25)';
document.getElementById('nav_links').style.color = "white";
}
}
}
document.addEventListener("scroll", myFunction);
<header id="nav">
<img src="logo.png" alt="logo" id="logo">
<nav>
<ul id="nav_links">
<li>Lifestyle</li>
<li>Fashion</li>
<li>Beauty</li>
<li>Health</li>
</ul>
</nav>
Anchor elements tend to have special treatment when it comes to their default styling by the browser. So we need to be quite specific when selecting them for a color change.
This snippet changes what the JS does. Instead of setting the styles directly it sets nav to have/not have a class 'scrolled' depending on whether scrollTop/pageYoffset is greater than zero or not. (Because scrollTop can't go below zero there is no need for the extra test in the script).
The presence/absence of the scrolled class causes both the background color of #nav and the color of the anchor elements in the li elements to be set in different ways.
A couple of notes on this. It has been assumed that the element with the id of nav is the one in your script which has id nav. This may not matter in practice, but if this is not true and element nav is in fact the actual nav element then you could replace #nav by #nav nav for more specificity. It's also been assumed that you don't want the bullet point suddenly appearing on the list as the background turns to white but I've indicated in the snippet how you can go back to having that if that was intended.
const nav = document.querySelector('#nav');
function myFunction() {
// if (document.body.scrollTop > 0) { //if there is some scrolling
// I ran into some problems making a Stackoverflow snippet because of the introduction of !doctype html
// so I have used this cross-browser test from #ijavid answer at https://stackoverflow.com/questions/2717252/document-body-scrolltop-is-always-0-in-ie-even-when-scrolling
if (typeof window.pageYOffset != 'undefined' ? window.pageYOffset : document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ? document.body.scrollTop : 0) {
nav.classList.add('scrolled');
} else { // there isn't any scrolling
nav.classList.remove('scrolled');
}
}
document.addEventListener("scroll", myFunction);
body {
height: 200vh;
background-color: blue;
}
#nav {
background-color: rgb(25, 25, 25);
}
#nav.scrolled {
background-color: white;
}
#nav #nav_links li>a {
color: white;
}
#nav.scrolled #nav_links li>a {
color: rgb(25, 25, 25);
}
/* if you want the bullet points to show when the background goes white remove the next two settings */
#nav_links>li {
list-style: none;
}
/* this is put in because of a bug in Safari which would mar the accessibility of the list */
/* (Safari would ignore it) see https://developer.mozilla.org/en-US/docs/Web/CSS/list-style */
#nav_links>li::before {
content: "\200B";
}
</style></head><body><!doctype html><html><head><style>body {
height: 200vh;
background-color: blue;
}
#nav {
background-color: rgb(25, 25, 25);
}
#nav.scrolled {
background-color: white;
}
#nav #nav_links li>a {
color: white;
}
#nav.scrolled #nav_links li>a {
color: rgb(25, 25, 25);
}
/* if you want the bullet points to show when the background goes white remove the next two settings */
#nav_links>li {
list-style: none;
}
/* this is put in because of a bug in Safari which would mar the accessibility of the list */
/* (Safari would ignore it) see https://developer.mozilla.org/en-US/docs/Web/CSS/list-style */
#nav_links>li::before {
content: "\200B";
}
<header id="nav">
<img src="logo.png" alt="logo" id="logo">
<nav>
<ul id="nav_links">
<li>Lifestyle</li>
<li>Fashion</li>
<li>Beauty</li>
<li>Health</li>
</ul>
</nav>
</header>
I have three separate paragraphs stored in three list-items with a continue reading image. I want only the first list-item to show. When you click the image, it should fade and show the next list-item in a fade transition. Once you reach the third one, it should fade back to the first one when you click.
My issue is that I am getting weird spacing- where the first list-items fades but still takes up space for a moment and causes the next fading in list item to jump up once the prior LI finally fades.
Opacity works perfectly with the fading but display collapses the fading out li so that list items stack on top of each other with the fading.
I could position the list items absolute with the UL position relative but I don't like the inherit lack of responsiveness with that method. I'd like to avoid it if I can help it.
Code so far-
<ul class="copy-box">
<li class="first active"><p>first</p></li>
<li class="second"><p>second</p></li>
<li class="last"><p>third</p></li>
</ul>
<a class="next-btn"><img src="#"></a>
.active {
opacity:1 !important;
transition: .5s;
display: block !important;
}
.copy-box li {
opacity:0;
list-style: none;
display: none;
}
.copy-box li:first {
display: inline;
}
Jquery
`$('.next-btn').click(function(){
if ( $("li.active").hasClass('last') ) {
$('li.active').removeClass('active');
$('.copy-box li').first('li').addClass('active');
return false;
}
else {
$("li.active").removeClass('active').next().addClass('active');
return false;
}
})`
First thing to keep in mind is - display property is not animatable, so you cannot expect it to work with animation/transition.
As you want fade effect, you just need to change opacity from 0 to 1 and vice versa.
One issue you will now run into is that all paragraphs fade in/out at their respective positions and keep on taking space even though they have faded out. You would normally want them to fade in/out all on same position i.e. overlapping each other. For this, you need to set their position to absolute and adjust position as required.
$('.next-btn').click(function(){
if ( $("li.active").hasClass('last') ) {
$('li.active').removeClass('active');
$('.copy-box li').first('li').addClass('active');
return false;
}
else {
$("li.active").removeClass('active').next().addClass('active');
return false;
}
});
.copy-box
{
position: relative;
height: 50px;
}
.copy-box li {
opacity:0;
list-style: none;
position: absolute;
top: 0;
left: 0;
transition: opacity 0.3s ease;
}
.copy-box li.active {
opacity:1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<ul class="copy-box">
<li class="first active"><p>first</p></li>
<li class="second"><p>second</p></li>
<li class="last"><p>third</p></li>
</ul>
<a class="next-btn"><img src="#"></a>
I am having an issue trying to set a class to the active nav item.
I need some help trying to figure out where i am going wrong.
It is some what working but not correctly, it defaults to the menu item which has a sub menu and i can't see where i have gone wrong.
When clicking on say "contact us" for example - the href takes me to the contact page but the active class does not apply and remains on "Products"
What i want to happen is the active class applies to the item that is clicked and not stuck on "products"
Here is JSFiddle https://jsfiddle.net/2yv92roL/
Thanks for help in advance!
Here is HTML:
<nav class="navigation">
<ul class="mainmenu">
<li>Home</li>
<li>Our Philosophy</li>
<li>Products
<ul class="submenu">
<li>Charbonnier Cookware</li>
<li>Charbonnier Dinnerware</li>
<li>Charbonnier Storageware</li>
</ul>
</li>
<li>Contact us</li>
</ul>
</nav>
Here is my CSS:
/* define a fixed width for the entire menu */
.navigation {
width: 150px;
}
/* reset our lists to remove bullet points and padding */
.mainmenu, .submenu {
list-style: none;
padding: 0;
margin: 0;
}
/* make ALL links (main and submenu) have padding and background color */
.mainmenu a {
display: block;
text-decoration: none;
padding: 10px;
}
/* add hover behaviour */
.mainmenu a:hover {
background-color: #C5C5C5;
}
/* when hovering over a .mainmenu item,
display the submenu inside it.
we're changing the submenu's max-height from 0 to 200px;
*/
.mainmenu li:hover .submenu {
display: block;
max-height: 200px;
}
/*
we now overwrite the background-color for .submenu links only.
CSS reads down the page, so code at the bottom will overwrite the code at the top.
*/
/* hover behaviour for links inside .submenu */
.submenu a:hover {
background-color: #C5C5C5;
}
/* this is the initial state of all submenus.
we set it to max-height: 0, and hide the overflowed content.
*/
.submenu {
overflow: hidden;
max-height: 0;
-webkit-transition: all 0.5s ease-out;
}
.navigation ul li .active {
color: #0080A6;
}
Here is my JQuery
$(function() {
var pgurl = window.location.href.substr(window.location.href.lastIndexOf("/")+1);
$(".navigation ul li a").each(function(){
if($(this).attr("href") == pgurl || $(this).attr("href") == '' )
$(this).addClass("active");
})
});
You need to make a couple of changes:
Remove "$(this).attr("href") == ''" from your if condition, this is the reason your "Products" menu is getting the active class by default
Add "$(".navigation ul li a").removeClass("active");" before your each to remove the active class from the previously selected menu
Add "/" to your pgurl so that it matches the href attribute of your menus
Here is the updated code:
$(function () {
var pgurl = "/" + window.location.href.substr(window.location.href.lastIndexOf("/") + 1);
$(".navigation ul li a").removeClass("active");
$(".navigation ul li a")
.each(function() {
if ($(this).attr("href") == pgurl)
$(this).addClass("active");
});
});
I'm unable to make the popups 'redItem', 'blueItem' and 'greenItem' below visible again after setting their display to 'none'. I'm using a CSS selector to get them visible again when the mouse hovers over a node higher up in the nested list to no avail.
Here's the code:
<ul class="popups" style="vertical-align: bottom; padding: 0px; margin: 0px;">
<li style="width: 165px"><a id="topmostBox" href="#">One_high-up_item</a>
<ul class="popups">
<li>First-lower-item
<ul class="popups">
<li name="redItem" >Red</li>
<li name="blueItem">Blue</li>
<li name="greenItem">Green</li>
</ul>
</li>
</ul>
</li>
</ul>
.popups:hover > li {
display: block;
}
.popups {
background-color: white;
font-family: sans-serif;
font-size: 13.5px;
list-style: none;
position: relative;
border: 1px solid lightgray;
border-width: .05em;
border-top-color: rgb(165,165,165);
line-height: 1.2em;
display: inline-table;
}
function setTopColorAndVis(theNestedPopupAnchor)
{
var theColorName = theNestedPopupAnchor.innerHTML;
var topMenuBox = document.getElementById('topmostBox');
topMenuBox.innerHTML = theColorName ;
theNestedPopupAnchor.parentNode.style.display = "none";
}
What happens is this:
1) I select the color 'Red' (the 1st list item)
2) my call to setTopColorAndVis(this) makes the popup disappear (because the user selected an item, the color "Red", and now the popup is not needed for now)
3) but when I later hover the mouse over the "First-lower-item" list item, the child li that has the ul containing 'redItem', 'greenItem', 'blueItem' does not appear.
So my experience here is that I'm successfully able to hide the list items named 'redItem', 'blueItem' and 'greenItem' -- but when I hover over the "First-lower-item", despite my CSS code:
.popups:hover > li {
display: block;
}
The 'redItem', 'greenItem' and 'blueItem' do NOT reappear.
What am I missing here?
The inline style overrides you style in your css code. you should use onmouseover event and onmouseout instead.
Try
<li name="redItem" >Red</li>
function show(elem){
elem.parentNode.style.display = "block";
}
function hide(elem){
elem.parentNode.style.display = "none";
}
You cannot :hover over an element with display:none as it has no size...
instead of working with display, you can work with visibility - which will leave an area to hover over.
like so:
theNestedPopupAnchor.parentNode.style.visibility = 'hidden'
.popups:hover > li {
visibility: visible;
}
http://www.w3schools.com/cssref/pr_class_visibility.asp
In this fiddle I've implemented a list with a click handler. When an element in the list is clicked the ".selected" class is added to it which changes it's height. When a different list element is subsequently clicked the ".selected" class is removed from the previously clicked element (thus restoring it to its original height) and the ".selected" class is added to the newly clicked element altering it's height.
Notice in the fiddle that when a list element is clicked and the ".selected" element is below it in the list the clicked element stays perfectly still while it expands and the previously selected element contracts. However when a list element is clicked and the ".selected" element is above it in the list, the list scrolls up to compensate for the contracting element causing the newly ".selected" list element to move up a little. I would like some mechanism for smoothly mitigating this behavior so the ".selected" list element stays perfectly still in this circumstance.
Html:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
js:
$(document).ready(function () {
$("li").click(function () {
$(".selected").removeClass("selected");
$(this).addClass("selected");
});
});
css:
li{
width:100%;
border-top:solid black 1px;
height:100px;
background:green;
}
.selected{
height:300px;
background:red;
}
As I see you need offset scroll so newly selected element stays in the view. I suggest the following code:
$(document).ready(function () {
$("li").click(function(){
var $this = $(this),
pos = $this.offset().top,
$doc = $(document);
$(".selected").removeClass("selected");
$this.addClass("selected");
$doc.scrollTop($doc.scrollTop() + $this.offset().top - pos);
console.log($this.offset().top - pos);
});
});
DEMO
To this point:
I would like some mechanism for smoothly mitigating this behavior so
the ".selected" list element stays perfectly still in this
circumstance.
I don't see how you will be able to keep it "perfectly still" in that when the other item contracts if the rest of the list was somehow "fixed" into place then there would be a huge gap in between the items...
A couple of thoughts... this css:
li{
width:100%;
border-top:solid black 1px;
height:100px;
background:green;
transition: height 2s;
-webkit-transition: height 2s;
}
.selected{
height:300px;
background:red;
transition: height 2s;
-webkit-transition: height 2s;
}
Makes for a much less jarring transition that is easy for the eye to follow.
To actually get what you asked for, "perfectly still", you could do something derpy like this:
$(document).ready(function () {
$("li").click(function(){
$(".previouslySelected").removeClass("previouslySelected"); $(".selected").removeClass("selected").addClass("previouslySelected");
$(this).addClass("selected");
});
});
CSS
.previouslySelected {
height:100px;
margin-bottom:200px;
}
Which as you see does keep the one below from "jumping"... cuz I consider this a derpy approach I didn't take the time to check if the newly clicked one was above or below the previously selected one etc... but that would be trivial to add. Make sense?
Why don't you simply add a transition to the <li> elements?
In this way you have a smooth effect between selected/not-selected states.
li{
width:100%;
border-top:solid black 1px;
height:100px;
background:green;
-webkit-transition: all .5s ease-in-out;
-moz-transition: all .5s ease-in-out;
-o-transition: all .5s ease-in-out;
transition: all .5s ease-in-out;
}
.selected{
height:300px;
background:red;
}
DEMO
You can try following jQuery stuff
$(document).ready(function() {
$("li").click(function() {
var selectItemTop = $(this).position().top;
var windowTop = $(window).scrollTop();
var selectItemHeight = $(".selected").height();
var selectionTop = $('li').hasClass('selected') ? $(".selected").position().top : 0;
console.log(selectionTop);
if (selectionTop < selectItemTop) {
selectItemTop = selectItemTop - windowTop;
$(window).scrollTop(windowTop + selectItemTop - selectItemHeight);
}
$(".selected").removeClass("selected");
$(this).addClass("selected");
});
I nearly managed to fulfill your requirement.
you can test example from this link fiddle
$(document).ready(function () {
$("li").click(function(){
var selectItemTop = $(this).position().top;
var windowTop = $(window).scrollTop();
var selectItemHeight = $(".selected").height();
var selectionTop = $('li').hasClass('selected') ? $(".selected").position().top : 0;
console.log(selectionTop);
if(selectionTop < selectItemTop){
selectItemTop = selectItemTop-windowTop;
$(window).scrollTop(windowTop+selectItemTop-selectItemHeight);
}
$(".selected").removeClass("selected");
$(this).addClass("selected");
});
});