I can't seem to get this to work. I don't wanna put jQuery in yet. Just doing plain javascript. When I click on the image nothing happens. I need it to dropdown the navigation when I click the image. Edited my Javascript code. I added alert to show the current status of what class the toggle is using. But still I cant get to change it from header_navigation_mobile to header_navigation_mobile.is_open
This is my HTML CODE for the Clickable Image
<a href="#" onclick="toggleMenu()">
<img class="mobile_navigation_button" src="{{site.baseurl}}/assets/img/menu.svg"/>
</a>
This is the HTML for the drop down navigation
<div class="header_navigation_mobile">
<ul>
<li>HOME</li>
<li>ABOUT</li>
<li>CONTACT</li>
<li> </li>
<li> </li>
</ul>
</div>
Then my CSS For the Clickable Image to show the navigation
.header_navigation_mobile.is_open{
display: block;
transform: translateY(0%);
}
This is the CSS for the Clickable Image first state which is to Hide it
.header_navigation_mobile{
display: none;
position: absolute;
width: 290px;
background: #484547;
transform: translateY(-100%);
transition: all 0.3s ease-in-out;
}
Then finally my Javascript
function toggleMenu(){
var mobileNav = document.getElementById('mobile_Nav');
var mobileNav_toggle = mobileNav.getAttribute("class");
if(mobileNav_toggle == "header_navigation_mobile") {
mobileNav_toggle == "header_navigation_mobile.is_open";
}
else {
mobileNav_toggle == "header_navigation_mobile";
}
alert(mobileNav_toggle);
}
I would give the menu an ID so it's easier to target, then just toggle a class that you use to hide/show the menu.
.header_navigation_mobile {
display: none;
}
.open {
display: block;
}
toggle
<div class="header_navigation_mobile" id="mobilenav">
<ul>
<li>HOME</li>
<li>ABOUT</li>
<li>CONTACT</li>
<li>
</li>
<li>
</li>
</ul>
</div>
<script>
function toggleMenu(){
var nav = document.getElementById('mobilenav');
nav.classList.toggle('open');
}
</script>
In your JS try like this.use querySelector for selecting elements.And for setting and getting css properties use selector.style.property_name.
function toggleMenu(){
var mobileNav_Hide = document.querySelector('.header_navigation_mobile');
var mobileNav_Show = document.querySelector('.header_navigation_mobile.is_open')
if(mobileNav_Hide.style.display == "none"){
mobileNav_Show.stylr.display == "block";
}
else{
mobileNav.style.display = "none";
}
}
I know this is not a specific answer to your question but may be a handy solution as well. I put together a jsfiddle of a hidden menu you can easily edit to your needs. Here is the link.
#Btn{
position: fixed;
right: 20px;
width: 20px;
height: 24px;
background: linear-gradient(#FFF,#DDD);
border: #AAA 1px solid;
border-radius: 2px;
padding: 2px 5px;
cursor: pointer;
transition: border 0.3s linear 0s;
}
#Btn:hover{
border: #06F 1px solid;
}
#Btn:hover div{
background: #06F;
}
#Btn > div{
width: 20px;
height: 4px;
background: #333;
margin: 3px 0px;
border-radius: 4px;
transition: background 0.3s linear 0s;
}
#hidden{
position: fixed;
right: -300px;
top: 60px;
width: 260px;
height: 0px;
background: #333;
color:#ededed;
padding:15px;
transition: right 0.3s linear 0s;
}
<script>
function toggleBTN(){
var hidden = document.getElementById("hidden");
hidden.style.height = window.innerHeight - 60+"px";
if(hidden.style.right == "0px"){
hidden.style.right = "-300px";
} else {
hidden.style.right = "0px";
}
}
</script>
<div id="Btn" onclick="toggleBTN()">
<div></div>
<div></div>
<div></div>
</div>
<div id="hidden">
<ul>
<li>MENU ITEM 1</li>
<li>MENU ITEM 2</li>
<li>MENU ITEM 3</li>
</ul>
</div>
<div id="page_content">
<script>
for(var i = 0; i < 10; i++){ document.write("<h2>"+(i+1)+". Dummy page content ... </h2>"); }
</script>
</div>
Hope this helps :)
There are a few things in the else statement where is mobileNav coming from there is no instance of that anywhere in your code. Taking a step back a minute from your solution does the adding and removal of the classname is_open define the entire show hide behaviour you desire?
Your process of getting the same element with the subclass present/not present and then trying to set the display property is confusing.
You have an equality comparer when setting the display property when it should be just a single equals which along with my first statement i think is your main problem.
Instead of the way you are doing it i would just look at toggling the is_open class this link Toggle class on HTML element without jQuery shows how to do that and also demonstrates a few other ways of toggling styles including without javascript
Finally just check because im being lazy that display is valid as a property and that it shouldnt instead be style.display as others have indicated
js
let c = document.getElementById("dropdownlist");
let e = document.getElementById("dropdownicon");
let d = e.classList.toggle('fa-angle-down');
if(d===true) {
if(c.style.display==='none'){
c.style.display = 'block';
}
else{
c.style.display ='block';
}
}
else{
c.style.display = 'none';
}
Related
I have a javascript project that I'm trying to convert into Angular and I'm having trouble understanding why the classes some of my are making the menu disappear even after reading the documentation again. I want the menu I have to rotate into and X only if .menu-btn is clicked on.
Here is the javascript code I'm trying to convert:
// Select DOM Items
const menuBtn = document.querySelector(".menu-btn");
const menu = document.querySelector(".menu");
const menuNav = document.querySelector(".menu-nav");
const menuBranding = document.querySelector(".menu-branding");
const navItems = document.querySelectorAll(".nav-item");
// Set Initial State Of Menu
let showMenu = false;
menuBtn.addEventListener("click", toggleMenu);
function toggleMenu() {
if (!showMenu) {
menuBtn.classList.add("close");
menu.classList.add("show");
menuNav.classList.add("show");
menuBranding.classList.add("show");
navItems.forEach((item) => item.classList.add("show"));
// Set Menu State
showMenu = true;
} else {
menuBtn.classList.remove("close");
menu.classList.remove("show");
menuNav.classList.remove("show");
menuBranding.classList.remove("show");
navItems.forEach((item) => item.classList.remove("show"));
// Set Menu State
showMenu = false;
}
}
app.component.html
<header>
<div class="container">
<div class="menu-btn" [class.menu-btn.close]="check" (click)="myfunction()">
<div class="btn-line"></div>
<div class="btn-line"></div>
<div class="btn-line"></div>
</div>
<!-- Overlay that comes up when you click menu -->
<!-- Profile Image put in through CSS -->
<nav class="menu" [class.menu-btn.show]="check" (click)="myfunction()">
<div class="menu-branding" [class.menu-btn.show]="check" (click)="myfunction()">
<div class="portrait"></div>
</div>
<!-- Pages -->
<ul class="menu-nav" [class.menu-btn.show]="check" (click)="myfunction()">
<li class="nav-item current" [class.menu-btn.show]="check" (click)="myfunction()">
Home
</li>
<li class="nav-item" [class.menu-btn.show]="check" (click)="myfunction()">
About
</li>
<li class="nav-item" [class.menu-btn.show]="check" (click)="myfunction()">
Work
</li>
<li class="nav-item" [class.menu-btn.show]="check" (click)="myfunction()">
Contact
</li>
</ul>
</nav>
</div>
</header>
app.component.scss
$primary-color: red;
$secondary-color: blue;
#mixin easeOut {
transition: all 0.5s ease-out;
}
.container{
background-color: grey;
height:100px;
box-sizing: border-box;
}
a{
text-decoration: none;
color:white;
}
.btn-line{
color:blue;
}
header{
position: fixed;
z-index:2;
width:100%;
}
.menu-btn{
position: absolute;
z-index:3;
right:35px;
top:35px;
cursor: pointer;
#include easeOut;
.btn-line{
width: 28px;
height: 3px;
margin: 0 0 5px 0;
background: white;
#include easeOut;
}
//Rotate Into X with Menu Lines
&.close {
transform: rotate(180deg);
.btn-line {
// Line 1 - Rotate
&:nth-child(1) {
transform: rotate(45deg) translate(5px, 5px);
}
// Line 2 - Hide
&:nth-child(2) {
opacity: 0;
}
// Line 3 - Rotate
&:nth-child(3) {
transform: rotate(-45deg) translate(7px, -6px);
}
}
}
}
.btn-line{
color:black;
}
app.component.ts
export class AppComponent {
title = 'menu';
check:boolean=true;
myfunction(){
this.check=true;
}
}
I believe the simple answer is that you cannot add two classes at once with the [class.xxx.yyy] directive...
If you want both classes applied you have to do it in two separate [class...] directives, or use an ngClass directive, e.g. [ngClass]="{ xxx: check, yyy: check }"
EDIT (following comment) your template has stuff such as [class.menu-btn.show]="check" I must have guessed wrong when I assumed that you wanted to add both the show and the menu-btn class based on the value of the variable check - if that wasn't your intention, I'm sorry. But at any rate, the class you wish to add must be the sole name in that directive (after class.), so if you want to add the show class, do [class.show]="check". And you can use multiple such directives, each for a different class.
I'm new to coding, I am have a made a decent looking website (https://garibpathshala.in/) with a toggle nav menu for mobiles.
is there any way so that if we click outside the menu it'll close the menu.
Please have a look at my code and help me :)
HTML
<ul class="header-nav-links">
<li class="active">HOME</li>
<li>PROJECTS</li>
<li>TEAM</li>
<li>ABOUT</li>
<li>GALLERY</li>
<li>CONTACT</li>
<li>DONATE</li>
<li>JOIN US</li>
</ul>
<div class="burger">
<div line1></div>
<div line2></div>
<div line3></div>
</div>
JS
const burger = document.querySelector(".burger");
const navLinks = document.querySelector(".header-nav-links");
const links = document.querySelectorAll(".header-nav-links li");
//Toggle Nav
burger.addEventListener("click", () => {
navLinks.classList.toggle("open");
//Animate Links
links.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = ""
}else{
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7+0.2}s`;
}
});
});
Here is a screenshot of the nav menu
You could remove "open" class from the menu if the event.CurrentTarget is not the hamburger menu and anything else in the document (html or body) is clicked.
You would also need to stopImmediatePropagation on the .hamburger and navLinks itself to stop those from being registered as clicks to the body, since they are children of the body and the event would otherwise bubble up to the body.
MDN reference: https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles
const burger = document.querySelector(".burger");
const navLinks = document.querySelector(".header-nav-links");
const links = document.querySelectorAll(".header-nav-links li");
const body = document.querySelector('html');
//Toggle Nav
burger.addEventListener("click", (e) => {
navLinks.classList.toggle("open");
e.stopImmediatePropagation();
//Animate Links
links.forEach((link, index) => {
if (link.style.animation) {
link.style.animation = "";
}else{
link.style.animation = `navLinkFade 0.5s ease forwards ${index / 7+0.2}s`;
}
});
});
navLinks.addEventListener("click", (eve) => {
eve.stopImmediatePropagation();
});
body.addEventListener("click", (ev) => {
if (ev.currentTarget != burger) {
navLinks.classList.remove("open");
}
});
.burger {
display: block;
cursor:pointer;
}
.header-nav-links {
display: block;
}
.header-nav-links.open {
transform: translateX(0%);
}
.header-nav-links {
right: 0;
position: fixed;
height: 92vh;
top: 16vh;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
flex-direction: column;
align-items: center;
width: 50%;
transform: translateX(100%);
transition: transform 0.5s ease-in;
}
.header-nav-links li {
list-style-type: none;
}
.header-nav-links li:hover {
border: 1px solid #fff;
border-radius: 6pc;
background-color: #007bff;
}
.header-nav-links a {
color: whitesmoke;
text-decoration: none;
font-family: Arial, sans-serif;
font-weight: normal;
font-size: 16px;
border: 0px solid white;
transition: 400ms;
padding: 5px 15px;
border-radius: 19px;
}
<ul class="header-nav-links">
<li class="active">HOME</li>
<li>PROJECTS</li>
<li>TEAM</li>
<li>ABOUT</li>
<li>GALLERY</li>
<li>CONTACT</li>
<li>DONATE</li>
<li>JOIN US</li>
</ul>
<div class="burger">
BURGER
<div line1></div>
<div line2></div>
<div line3></div>
</div>
You could add a click listener on body or document and rely on event delegation to take the appropriate action, as in the sample code below.
(See the in-code comments for further clarification.)
// Selects some DOM elements and converts the collection to an array
const listItems = Array.from(document.querySelectorAll(".header-nav-links > li"));
// Calls `handleMenuDisplay` when anything is clicked
document.addEventListener("click", handleMenuDisplay);
// Defines `handleMenuDisplay`
function handleMenuDisplay(event){ // Listeners can access their triggering events
const clickedThing = event.target; // The event's `target` property is useful
// Depending on what was clicked, takes an appropriate action
if(listItems.includes(clickedThing)){ // Arrays have an `includes` method
openMenu(clickedThing);
}
else{
closeMenu();
}
}
function openMenu(clickedLi){
demo.textContent = clickedLi.dataset.demoText;
}
function closeMenu(){
demo.textContent = "Menu is closed";
}
li{ margin: 7px; padding: 3px; border: 1px solid grey; }
#demo{ margin-left: 2ch; font-size: 1.5em; font-weight: bold; }
<ul class="header-nav-links">
<li data-demo-text="Home menu is open">HOME</li>
<li data-demo-text="Projects menu is open">PROJECTS</li>
<li data-demo-text="Team menu is open">TEAM</li>
<li data-demo-text="About menu is open">ABOUT</li>
</ul>
<p id="demo">Menu is closed</p>
Note: My use of custom data-attributes was just to make the sample code a bit cleaner -- it's not part of event delegation, and the display text for each li could just have easily been written out manually in the script.
Based on the other solutions:
An even simpler way is to use the focus and blur states of DOM elements to handle the state for your menu.
document.querySelectorAll('.menu').forEach((menu) => {
const items = menu.querySelector('.menu-items');
menu.addEventListener('click', (e) => {
items.classList.remove("hide");
menu.focus(); // Probably redundant but just in case!
});
menu.addEventListener('blur', () => {
items.classList.add("hide");
});
});
.menu {
cursor: pointer;
display: inline-block;
}
.menu > span {
user-select: none;
}
.menu:focus {
outline: none;
border: none;
}
.hide {
display: none;
}
.menu-items > * {
user-select: none;
}
<div class="menu" tabindex="0">
<span>Menu +</span>
<div class="menu-items hide">
<div>Item 0</div>
<div>Item 1</div>
<div>Item 2</div>
</div>
</div>
The secret here is to give the .menu div a tabindex so that it's focusable by the browser.
First things first for the JS:
Search the page for any instance of the menu class document.querySelectorAll
Get the items element that's currently hidden by .hide
When somebody clicks on the menu remove the .hide class from the items
This should focus the div, but just in case menu.focus is called!
Then whenever somebody loses focus of the div, AKA clicks away etc. Add the .hide class back.
This could be expanded to change the text of the button, do all sorts of other things. It also keeps your code super clean because you're relying on the browsers own internal state management, so you don't have to do any checks.
Handle a second click close
Right so it works great, but in order to make it function as most people expect UI we need to close it when the menu div is clicked again too (replace span with any class you need):
...
menu.querySelector('span').addEventListener('click', (e) => {
e.stopPropagation();
menu.blur();
});
menu.addEventListener('click', (e) => {
items.classList.remove("hide");
menu.focus(); // Probably redundant but just in case!
});
menu.addEventListener('blur', () => {
items.classList.add("hide");
});
...
I am trying to create a nav that contains 2 elements (left and right). I would like both elements to move in from their respective edges of the screen, meeting in the middle to create a full screen nav. Could anyone shed some light on where I'm going wrong?
So far I have managed to get each box to move individually but not both at the same time. I gave them both separate ID's within their nav class thinking this would allow me to manipulate their postioning in different ways when the function is called that toggles 'expanded' but again this hasnt worked.
This is my HTML Structure:
<div class="nav-toggle">
<div class="nav-toggle-bar"></div>
</div>
<nav id="left" class="nav">
</nav>
<nav id="right" class="nav">
<ul>
<li>Portfolio</li>
<li>Services</li>
<li>Contact</li>
</ul>
</nav>
Here is an part of my SCSS:
.nav {
background: $nav-background;
color: $nav-color;
cursor: pointer;
font-size: 2rem;
height: 100vh;
padding: 6rem 2rem 2rem 2rem;
position: fixed;
top: 0;
width: $nav-width;
z-index: $z-nav;
ul {
#include center-vertically;
list-style: none;
margin: 0;
padding: 0;
}
}
#right{
#include transition(right 0.5s ease);
right: -$nav-width;
&.expanded { right: 0; }
}
#left{
#include transition(left 0.5s ease);
left: -$nav-width;
&.expanded { left: 0; }
}
And this is my JS:
(function() {
var hamburger = {
navToggle: document.querySelector('.nav-toggle'),
nav: document.querySelector('nav'),
doToggle: function(e) {
e.preventDefault();
this.navToggle.classList.toggle('expanded');
this.nav.classList.toggle('expanded');
}
};
hamburger.navToggle.addEventListener('click', function(e) { hamburger.doToggle(e); });
}());
This is the codepen I have been working on: https://codepen.io/matthewoproctor/pen/YmxZPQ
Thanks in advance for any help.
Your approach with the two IDs and the shared classname is perfect, as is your SCSS. The problem here is actually pretty simple: hamburger.nav is using document.querySelector, which only selects the first element with the nav class. Instead, you should use document.querySelectorAll. Then you just have to tweak the doToggle function to loop through all elements with the nav class.
(function() {
var hamburger = {
navToggle: document.querySelector('.nav-toggle'),
nav: document.querySelectorAll('nav'),
doToggle: function(e) {
e.preventDefault();
this.navToggle.classList.toggle('expanded');
this.nav.forEach(n => n.classList.toggle('expanded'));
}
};
hamburger.navToggle.addEventListener('click', function(e) {
hamburger.doToggle(e);
});
}());
I am trying to do an overview page on my website so that when I hover over a div on the overview page different sections of that div show different images. Essentially a slideshow but the image changes depending on where the cursor is.
I have managed to find some code that does what I want but it uses an a href to pull in the images which means if you click it, it goes to the link of the image.
Currently I just have placeholder images in but when finished each one will have specific project images in. As each div will just be one project the whole div should go to one html link and not just a specific image link of the image the user is hovering over.
All I want is the user to click and it go to a html link and not an img link.
Here is the code I am using:
The coding savvy people out there will probably have a much better solution for what I would like to achieve, I am interested to see any better solutions.
HTML
<div class="multi">
<ul class="rotator-nav fifth clearfix">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<div class="imgcontent">
<ul class="rotator-icons fifth">
<span class="img1 active"></span>
<span class="img2"></span>
<span class="img3"></span>
<span class="img4"></span>
<span class="img5"></span>
</ul>
<img src="/img/FoI.jpg" class="currentimg">
</div>
</div>
CSS
.multi {
display: block;
float:left;
position: relative;
width: 30.8%;
height: 20%;
padding: 0px;
margin:0% 1% 2% 1%;
overflow: hidden;
}
.multi .imgcontent {
display: block;
position: absolute;
top: 0px;
}
.imgcontent img {
display:block;
width: 100%;
height: auto;
margin-left: auto;
margin-right: auto;
}
.rotator-nav {
display: block;
position: relative;
width: 100%;
height: 100%;
z-index: 9;
}
.rotator-nav li {
display: block;
float: left;
height: 100%;
}
.rotator-nav.fourth li {
width: 25%;
}
.rotator-nav.fifth li {
width: 20%;
}
.rotator-nav li a {
display: block;
float: left;
width: 100%;
height: 100%;
border-bottom:0px solid #fff
}
.clearfix:after { content: "."; display: block; clear: both; visibility: hidden; line-height: 0; height: 0; }
.clearfix { display: inline-block; }
html[xmlns] .clearfix { display: block; }
* html .clearfix { height: 1%; }
JS
$(function(){
var $rotators = $('.multi');
var $imglinks = $('.rotator-nav a');
$imglinks.on('mouseenter', function(e){
var imgclass = '.'+$(this).attr('class');
var imglink = $(this).attr('href');
// update main image src
$(this).parent().parent().parent().find('.currentimg').attr('src',imglink);
// update current rotator icon
var $rotators = $(this).parent().parent().parent().find('.rotator-icons');
if($rotators.children(imgclass).hasClass('active')) {
// already active icon -- do nothing
} else {
// remove active class then add to new icon
$rotators.children('span').removeClass('active');
$rotators.children(imgclass).addClass('active');
}
});
});
Any help would be greatly appreciated!
Thanks,
Mark
I think you could best use a data attribute for this instead (if I understand the intention correctly) :
var imglink = $(this).data('image');
<div class="multi">
<ul class="rotator-nav fifth clearfix">
<li>
<a data-image="/img/FoI.jpg" href="#" class="img1"></a>
</li>
<li>
<a data-image="/images/card.jpg" href="#" class="img2"></a>
</li>
<li>
<a data-image="/images/amareal.jpg" href="#" class="img3"></a>
</li>
<li>
<a data-image="/images/edeva.jpg" href="#" class="img4"></a>
</li>
<li>
<a data-image="/images/amacover2.gif" href="#" class="img5"></a>
</li>
</ul>
...
If you'd still like to see the image over the original div, a pseudo element could be used. Advantage there is that they are not actual DOM elements and will not register clicks :
Demo
Now it would be great if the data attribute could be directly used for the content of the pseudo element as well but that doesn't seem possible. And you can't target them with JavaScript so each image would have to be defined with nth-of-type() in the stylesheet additionally.
You don't need to use .parent().parent()
Just use the parent's class to find the item.
Your $(this).parent() * 3 is the $(".multi")
So your $rotators can't find .rotator-icons,
you need to use one more parent or use siblings
And I suggest do not use class if there are no need to do one thing to lots of items.
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