I am trying to shrink right side div when left navigation menu is open. Here is my code example,
HTML :
<div id="home" class="full"><p>Home</p></div>
<div id="about" class="full"><p>About</p></div>
<div id="portfolio" class="full"><p>Portfolio</p></div>
<div id="skills" class="full"><p>Skills</p></div>
<div id="contact" class="full"><p>Contact</p></div>
Css :
.full{height: 100vh;color: #fff;}
.PageShrink{margin-left: 30vh;}
JS:
const fullPage = document.querySelector('.full');
let menuOpen = false;
menuBtn.addEventListener('click', () => {
if(!menuOpen) {
fullPage.classList.add('PageShrink');
menuOpen = true;
} else {
fullPage.classList.remove('PageShrink');
menuOpen = false;
}
});
The issue is, when I click on menuBtn to slide in from left, Home shrinks 30vh according to code, but after that if I click on About,portfolio etc its not shrinking even though they have same class. Not sure whats wrong but it just works on 1st div i.e, home.
Please assist,
Thank you in advance.
Your issue is within the JS. You use querySelector which only targets the first element with that class. So you have to use querySelectorAll. However this only gives you an array of all elements and the JS change is only applied to the first element still. So you need to cycle through all elements with that class by using: fullPage.forEach(el => el.classList.add('PageShrink'));
const fullPage = document.querySelectorAll('.full');
let menuOpen = false;
menuBtn.addEventListener('click', () => {
if(!menuOpen) {
fullPage.forEach(el => el.classList.add('PageShrink'));
menuOpen = true;
} else {
fullPage.forEach(el => el.classList.remove('PageShrink'));
menuOpen = false;
}
});
For demonstration and testing:
function caseA() {
document.querySelector(".test").style.color = "red";
}
function caseB() {
document.querySelectorAll(".test").style.color = "green";
}
function caseC() {
document.querySelectorAll(".test").forEach(el => el.style.color = "blue");
}
<ul>
<li class="test">Test</li>
<li class="test">Test</li>
<li class="test">Test</li>
<li class="test">Test</li>
<li class="test">Test</li>
</ul>
<button onclick="caseA()">querySelector</button>
<button onclick="caseB()">querySelectorAll</button>
<button onclick="caseC()">querySelectorAll + forEach</button>
You see that "Case A" is onyl targetign the first element. "Case B" will cause an error as querySelectorAll returns an array of elements not an element itself. If you use forEach you can apply your JS comamnd to all the elements as it cycles through the array and applies it to every element of the array.
Related
I'm trying to make it so that when you scroll on my HTML page when you reach a certain section, that respective section should become active,
for example:
<nav class="scrollmenu">
<ul>
<li class="starters">STARTERS</li>
<li class="ramen">RAMEN</li>
</ul>
</nav>
starters should become active when you reach this section on the page:
<section id="starters" class="foodgrid">...</section>
I'm trying to do this with this JS code:
const sections = document.querySelectorAll("section");
const navLi = document.querySelectorAll("nav ul li");
window.addEventListener("scroll", () => {
let current = " ";
sections.forEach((section) => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (scrollY >= sectionTop) {
current = section.getAttribute("id");
}
});
navLi.forEach((li) => {
li.classList.remove("active");
if (li.classList.contains(current)) {
li.classList.add("active");
}
});
});
I'm still going to expand on this JS code to make it work better but if I'm not mistaken it should already make the class visually active when I'm on the right section.
In case you are wondering my CSS looks like this for the time being:
nav ul li:active {
background-color: blue;
}
what happens here is that you are attaching a class name which is active and you are not declaring that class, you just have the event :active that is triggered when you click on the element, so you must do this
.active {
background-color: blue;
}
and that will work when you set the class and remove it, also leaving the css code as you have it will make that when the html li element is clicked it changes it background and then changes to the normal color, try it and let me know if it works, if it doesn't is something about your js and then ill check it
Hi sorry in advance if this has already been asked but I can't find the answer.
I have a set of links that trigger certain ids to show onclick, it works but the one link is suppose to trigger 2 ids to show. My javascript knowledge is not great. Thanks for any help.
Here is my codepen https://codepen.io/louise-fourie/pen/abVdwyZ
<li>
Fitness & Wellness
</li>
<li>
Business
</li>
<li>
Arts & Entertainment
</li>
</ul>
<div class="articles">
<div id="el-57d5b6f71db32029">fitness</div>
<div id="el-e881a23a64890108">business</div>
<div id="el-65ebd7b2380005a1">art</div>
</div>
<script>
var divs = ["el-57d5b6f71db32029", "el-e881a23a64890108", "el-65ebd7b2380005a1"];
var visibleId = null;
function show(id) {
for(i = 0; i < divs.length; i++) {
if(visibleId !== id) {
visibleId = id;
}
}
hide();
}
function hide() {
var div, i, id;
for(i = 0; i < divs.length; i++) {
id = divs[i];
div = document.getElementById(id);
if(visibleId === id) {
div.style.display = "block";
} else {
div.style.display = "none";
}
}
}
</script>
There is something fishy with your show-hide logic.
Check out my suggestion below, where you can pass an array of IDs, which you want to show. All other elements will be hidden.
function onClick(elements) {
document.querySelectorAll('.articles div').forEach(articleDiv => articleDiv.style.display = 'none');
elements.forEach(element => {
const domEl = document.querySelector('#' + element)
domEl.style.display = 'block';
})
}
<ul>
<li>
Fitness & Wellness
</li>
<li>
Business
</li>
<li>
Arts & Entertainment
</li>
</ul>
<div class="articles">
<div id="fitness">fitness</div>
<div id="business">business</div>
<div id="art">art</div>
</div>
<script>
</script>
Your function seems to be running, but every time you receive an ID in your function, you hide everything else with the "hide" function, so in the end, the last ID sent, is the only one that will show, try this:
Call the function once, but pass the IDs as one string separated by commas
Business
Then change your "show" function like this:
function show(ids) {
let idArr = ids.split(",");
divs.forEach( x => {
div = document.getElementById(x);
div.style.display = (idArr.includes(x) ? "block" : "none");
});
}
What this does, is that it will create an array of IDs based on the parameter you send, and for each item it will check if the ID was sent and show/hide it.
Please let me know if this helps or if you need more details.
EDIT: Formatting in the JavaScript code and simplifying it. Please also note that here I am not validating if the an element with the ID exists, it is only to show you how it can work. It will need additional validations
You can try it
<ul>
<li>
Fitness & Wellness
</li>
<li>
Business
</li>
<li>
Arts & Entertainment
</li>
</ul>
<div class="articles">
<div id="el-57d5b6f71db32029" style="display:none;">fitness</div>
<div id="el-e881a23a64890108" style="display:none;">business</div>
<div id="el-65ebd7b2380005a1" style="display:none;">art</div>
</div>
<script>
document.querySelectorAll('.article-btn').forEach(item => {
item.addEventListener('click', event => {
show((item.getAttribute('data-id')).split(";"));
})
})
const show = (id) => {
document.querySelectorAll('.articles>div').forEach(item => {
if(id.includes(item.getAttribute('id'))){
item.style["display"] = "block";
}else{
item.style["display"] = "none";
}
});
}
</script>
Inline JavaScript is generally discouraged these days, so here's a solution that removes that dependency. It puts the ids of the list items into the dataset instead. You can then create an array from that dataset, iterate over the articles, and if the id is included in the array of ids either hide or show it.
// Cache the list and the articles
const ul = document.querySelector('ul');
const articles = document.querySelectorAll('.articles div');
// Add an event listener to the list
ul.addEventListener('click', handleClick, false);
function handleClick(e) {
// Get the node name and the dataset
// from the element that was clicked
const { nodeName, dataset } = e.target;
// Check that it was an anchor
if (nodeName === 'A') {
// Get an array of ids from the dataset
const ids = dataset
.ids.split(',')
.map(id => id.trim());
// Hide all the articles
articles.forEach(article => {
article.classList.add('hide');
});
// Show the articles where the id is in
// the list of ids
articles.forEach(div => {
if (ids.includes(div.id)) {
div.classList.remove('hide');
}
});
}
}
.hide { display: none; }
ul li:hover { cursor: pointer; color: red; }
<ul>
<li>
<a data-ids="el-57d5b6f71db32029">Fitness & Wellness</a>
</li>
<li>
<a data-ids="el-57d5b6f71db32029, el-e881a23a64890108">Business</a>
</li>
<li>
<a data-ids= "el-65ebd7b2380005a1">Arts & Entertainment</a>
</li>
</ul>
<div class="articles">
<div id="el-57d5b6f71db32029" class="hide">fitness</div>
<div id="el-e881a23a64890108" class="hide">business</div>
<div id="el-65ebd7b2380005a1" class="hide">art</div>
</div>
hope everyone is keeping safe and coding a lot.
I am having a problem and have tried every solution but none are working. I do have a HAMBURGER MENU made of DIV's Elements, when open this menu will display all the MENU OPTIONS. So far it is working great but once the SECOND MENU is open I want to prevent the user to use the HAMBURGER MENU until the second menu is closed.
In my last try I have used POINTER-EVENTS: nome but believe it or not, it does stop the second menu from working and not the first one as it was supposed.
I will post here the code in JS and will be so grateful for any tips that will help me:
document.querySelector('.hamburger-menu').addEventListener('click', () => {
document.querySelector('.nav-wrapper').classList.toggle('change');
document.querySelector('#home-menu').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "block";
document.getElementById("#hamburger-menu").style['pointer-events'] = 'none';
});
document.querySelector('#close-window').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "none";
document.getElementById('.hamburger-menu').style.pointerEvents = "auto";
});
});
It seems the issue is because you are adding other event listener inside the top one. You may need to separate them
document.querySelector('.hamburger-menu').addEventListener('click', () => {
document.querySelector('.nav-wrapper').classList.toggle('change');
});
document.querySelector('#close-window').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "none";
document.getElementById('.hamburger-menu').style.pointerEvents = "auto";
});
document.querySelector('#home-menu').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "block";
document.getElementById(".hamburger-menu").style.pointerEvents = 'none';
});
I think you should leave the practice to control elements' behavior based on information gained from the DOM.
It would be much easier to control the behavior of your menu if you just toggled a boolean value in JS:
const btnFirst = document.getElementById('first')
const ddMenu = document.getElementById('second')
const ddItems = document.querySelectorAll('.dd-item')
const textArea = document.getElementById('third')
// this variable controls the behavior of the main menu
let btnFirstIsEnabled = true
btnFirst.addEventListener('click', function(e) {
if (btnFirstIsEnabled) {
btnFirstIsEnabled = false
ddMenu.classList.remove('hidden')
btnFirst.classList.add('disabled')
} else {
textArea.innerHTML = 'Cannot open dropdown again!'
}
})
ddItems.forEach(e => {
e.addEventListener('click', function(e) {
textArea.innerHTML = `Clicked: ${e.target.getAttribute('data-val')}`
ddMenu.classList.add('hidden')
btnFirstIsEnabled = true
btnFirst.classList.remove('disabled')
})
})
.main {
cursor: pointer;
}
.main.disabled {
color: #eaeaea;
}
.hidden {
display: none;
}
.dd-item {
cursor: pointer;
}
<div id="third">Clicked:</div>
<div id="first" class="main">OPEN</div>
<div id="second" class="hidden">
<ul>
<li class="dd-item" data-val="dd 1">Click dropdown item 1</li>
<li class="dd-item" data-val="dd 2">Click dropdown item 2</li>
<li class="dd-item" data-val="dd 3">Click dropdown item 3</li>
</ul>
</div>
I just added some coloring to the main menu in the snippet to show that it's not active, but the behavior is controlled by setting a Boolean variable in JS.
I think it would make your code much simpler if you made it work like this.
I want to add a class to an element that shares the same data attribute value using vanilla JS. The class is added on mouseenter.
My current setup only applies the class on hover to the first element and ignores the rest.
let section = document.querySelector('.section');
let links = document.querySelectorAll('.links a');
let triggerVal;
let linkedVal;
links.forEach(function(link, index) {
link.addEventListener('mouseenter', function() {
triggerVal = this.dataset.triggerValue;
linkedVal = section.dataset.linkedValue;
if (linkedVal === triggerVal) {
section.classList.add('is-active');
} else {
section.classList.remove('is-active');
}
});
});
<ul class="links">
<li>
<a data-trigger-value="red" href="#">Red</a>
</li>
<li>
<a data-trigger-value="yellow" href="#">Yellow</a>
</li>
<li>
<a data-trigger-value="blue" href="#">Blue</a>
</li>
</ul>
<div class="wrapper">
<div class="section" data-linked-value="red">
<h2>Red</h2>
</div>
<div class="section" data-linked-value="yellow">
<h2>Yellow</h2>
</div>
<div class="section" data-linked-value="blue">
<h2>Blue</h2>
</div>
</div>
Here's a Codepen: https://codepen.io/abbasarezoo/pen/7378e190ed6ad117faca968b634520b0
I've got a feeling it's to do with the .section element but I've tried a few things and nothing seems to give me what I need.
Any suggestions as to what I need to do to get the rest of the elements working?
You need to change two things:
First, get all sections:
const section = document.querySelectorAll('.section');
Then, inside your handler, you need to iterate over the NodeList returned by querySelectorAll():
for (const section of sections) {
linkedVal = section.dataset.linkedValue;
if (linkedVal === triggerVal) {
section.classList.add('is-active');
} else {
section.classList.remove('is-active');
}
}
This is your new JS:
const sections = document.querySelectorAll('.section');
const links = document.querySelectorAll('.links a');
let triggerVal;
let linkedVal;
links.forEach(function(link, index){
link.addEventListener('mouseenter', (e) => {
triggerVal = e.target.dataset.triggerValue;
for (const section of sections) {
linkedVal = section.dataset.linkedValue;
if (linkedVal === triggerVal) {
section.classList.add('is-active');
} else {
section.classList.remove('is-active');
}
}
});
});
You need to use document.querySelectorAll for sections and then forEach. And use toggle instead of add/remove for this case. https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
let sections = document.querySelectorAll('.section');
let links = document.querySelectorAll('.links a');
let triggerVal;
let linkedVal;
links.forEach(function(link, index) {
link.addEventListener('mouseenter', function() {
triggerVal = this.dataset.triggerValue;
sections.forEach(
section => section.classList.toggle(
'is-active',
section.dataset.linkedValue === triggerValue
)
);
});
});
I have a menu that contains 3 items. How can I change that the text to what it's selected from the menu?
HTML
<div id="dropdown-container">
<div id="index-tab" onclick="toggleMenu()">1</div>
<ul id="dropdown">
<li ><span class="current-browse">1</span>
<ul class="dropdown-items">
<li class="dropdown-item">2</li>
<li class="dropdown-item"">3</li>
</ul>
</li>
</ul>
</div>
JavaScript
function toggleMenu() {
var dropDown = document.getElementById('dropdown');
if(dropdown.style.display == "block") {
dropdown.style.display = "none";
} else {
dropdown.style.display = "block";
}
}
For example the menu currently showing:
1
1
2
3
If selected 2, it will show:
2
2
1
3
If selected 3 it will show :
3
3
1
2
Are you wanting to set the text of index-tab or the text of the current-browse span? Either way you need some click handlers on the li items that gets the element with the id or class of whichever one you want to set (Will need to get the anchor child of the index-tab div if it is used). Then replace the text element of the anchor or span. jQuery will make it a bit easier, but can be done either way. The jQuery example given will need to get the anchor child to then set text, and doing it when showing the menu is not what you want since no item is clicked yet.
Added toggleSelection() function:
event.preventDefault(); used to prevent links default action which is jumping to a location.
selectedItem references event.target (i.e. the element that was actually clicked), which is either the one of the .dropdown-items.
There's multiple exchanges between elements based on the .textContent of the selectedItem. At this point, #current-browse, and #index-tab a (the link in #index-tab) have a new .textContent and selectedItem has the previous item number.
All of that will not happen until #dropdown is clicked on and the event.target is determined. this is possible by the eventListener:
dropDown.addEventListener('click', toggleSelection, false);
SNIPPET
function toggleMenu() {
var dropDown = document.getElementById('dropdown');
if (dropDown.style.display == "block") {
dropDown.style.display = "none";
} else {
dropDown.style.display = "block";
}
}
function toggleSelection(event) {
event.preventDefault();
var selectedItem = event.target;
var targetItem = selectedItem.textContent;
var currentItem = document.getElementById('current-browse');
var prevItem = currentItem.textContent;
var extLink = document.querySelector('#index-tab a');
currentItem.textContent = targetItem;
extLink.textContent = targetItem;
selectedItem.textContent = prevItem;
}
var dropDown = document.getElementById('dropdown');
dropDown.addEventListener('click', toggleSelection, false);
<div id="dropdown-container">
<div id="index-tab" onclick="toggleMenu()">1
</div>
<ul id="dropdown">
<li><span id="current-browse">1</span>
<ul class="dropdown-items">
<li class="dropdown-item">2
</li>
<li class="dropdown-item">3
</li>
</ul>
</li>
</ul>
</div>
REFERENCE
addEventListener vs. onclick
'this' and EventHandlers
you need to write the toggleMenu() in a way that it changes the text of index-tab based on the value of dropdown:
$("#index-tab").text($(".dropdown-item option:selected").text())