Multiple Accordion Nesting Using Vanilla JavaScript - javascript

I am trying to achieve multiple accordion lists with dynamic height concepts, I am debugging for hours and not able to understand where I went wrong. The code I have tried so far.
The scenario I was trying on is when the main categories are clicked with the dynamic height it will show off and that same scenario will happen for its sub-categories also. I am stuck on how to take the inner sibling method.
Note: I am trying to achieve this in vanilla javascript.
Any help will be appreciated !!
var headers = document.querySelectorAll('.first-ul-1 .ul, .sub-ul-1-a ul');
window.addEventListener("DOMContentLoaded", () => {
for (var i = 0; i < headers.length; i++) {
headers[i].addEventListener('click', openAccordion);
}
function openAccordion(e) {
var parent = this.parentElement;
var article = this.nextElementSibling;
if (!parent.classList.contains('open')) {
parent.classList.add('open');
article.style.maxHeight = article.scrollHeight + 'px';
} else {
parent.classList.remove('open');
article.style.maxHeight = '0px';
}
}
.drop-down-menu,
.sub-ul-2-a{
max-height: 0px;
overflow: hidden;
transition: max-height 350ms ease-in-out;
}
<nav id="navigation" class="main-nav">
<ul class="main-nav-list first-ul-1">
<li>
<p href="#">Types</p>
<div class="drop-down-menu">
<div class="menu-container">
<ul class="main-nav-list sub-ul-1-a">
<li>
<a href="#">Type
of season</a>
<ul class="main-nav-list sub-ul-2-a">
<li>
<a href="#">Summer
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</li>
</ul>
</nav>

Related

how to get a specific string in an href in javascript

I am trying to create an onload event where a function compares the current URL to an href and displays content according to the href that is shown. I want to accomplish this by selecting a child from a parent, though I am unsure as to how to get the contents within the href specifically. Here is a bit of the code I have written:
html
<ul id="main-nav">
<li class="nav active">Shipping</li>
<li class="nav">returns</li>
<li class="nav">Custom Orders</li>
<li class="nav">Replacements/ Warranty</li>
<li class="nav">Most Frequently Asked Questions</li>
<li class="nav">RAD Principles</li>
<li class="nav">Environmental Stewardship</li>
<li class="nav">MADE in the USA</li>
</ul>
js
var href = $("#main-nav").children('a');
$("div.faq-container").hide();
if (window.location.href.includes(href)) {
$("div.faq-container").eq("0").show();
} else (window.location.href.includes(href)) {
$("div.faq-container").eq("1").show();
}
the main issue I have is that I want to write the line that has
var href = $("#main-nav").children('a');
so that it grabs the contents within the href alone, and nothing else outside of that, so that it would have either "#shipping", "#returns", etc. as its value.
You took the problem by the wrong end. What you need to know is the location.hash to target the right div to display.
This should be closer:
var hash = window.location.hash
// Hide all .faq-container
$("div.faq-container").hide()
// If there is a hash
if(hash){
$("#"+hash).show()
}
Here's a vanilla JavaScript example which will make the selected section visible.
It uses the hashchange event to detect when the hash has changed.
const sections = [...document.querySelectorAll('.section')];
function showSelected () {
sections.forEach(section => section.classList.remove('show'));
if(location.hash.length) {
const section = document.querySelector(location.hash);
if (section) {
section.classList.add('show');
}
}
}
window.addEventListener('hashchange', showSelected);
.section {
margin: 0.5rem;
padding: 0.5rem;
background-color: #e0e0e0;
width: 10rem;
display: none;
}
.show {
display: block;
}
<ul id="main-nav">
<li class="nav active">Shipping</li>
<li class="nav">returns</li>
<li class="nav">Custom Orders</li>
<li class="nav">Replacements/ Warranty</li>
<li class="nav">Most Frequently Asked Questions</li>
<li class="nav">RAD Principles</li>
<li class="nav">Environmental Stewardship</li>
<li class="nav">MADE in the USA</li>
</ul>
<div id="shipping" class="section">#shipping</div>
<div id="returns" class="section">#returns</div>
<div id="custom" class="section">#custom</div>
<div id="replacements" class="section">#replacements</div>
<div id="mostFAQs" class="section">#mostFAQs</div>
<div id="RAD" class="section">#RAD</div>
<div id="environmental" class="section">#environmental</div>
<div id="USA" class="section">#USA</div>

JavaScript event-handler: Toggle visibility of adjacent element. Code not working

I have nested unordered-lists. Each unordered-list has an h1 tag as its 'previous-sibling' tag. My goal is to click on an h1 tag and toggle the visibility of the unordered-list that comes right after it.
I am also trying to also assign classNames to each of unordered lists, based on the their title (h1) tag.
Is anyone able to help me understand why my code is not working?
Here is the code:
window.onload = function() {
let titles = document.getElementsByTagName('h1');
for (let i = 0 ; i < titles.length ; i++) {
let title = titles[i];
//grab the text in the h1 element, and add that text as a class to the h1:
let className = title.innerHTML.toLowerCase();
title.className += title.className.length ? ' ' + className : className;
let section = document.querySelectorAll(`.${className} + ul`);
if(section.length) {
section[0].className += section[0].className.length
?
` ${className} section`
:
`${className} section`;
//ADD EVENT HANDLER TO THE TITLE.
//SHOULD HIDE AND SHOW THE ADJASCENT UL ELEMENT:
title.onclick = function(e) {
section[0].classList.toggle('hide');
};
}
}
};
/* trying to toggle visibility with this class*/
.hide {
display: none;
}
/*basic styles to separate elements:*/
h1 {
color: olive;
}
ul {
border: solid orange 1px;
}
li {
//border: solid magenta 1px;
}
<div id="foods">
<h1>Food</h1>
<ul>
<li>
<h1>Fruit</h1>
<ul>
<li>
<h1>tropical</h1>
<ul>
<li>banana</li>
<li>pineapple</li>
<li>mango</li>
</ul>
</li>
<li>
<h1>stone</h1>
<ul>
<li>peach</li>
<li>pear</li>
<li>appricot</li>
</ul>
</li>
</ul>
</li>
<li>
<h1>Vegetables</h1>
<ul>
<li>
<h1>leafy</h1>
<ul>
<li>lettuce</li>
<li>spinach</li>
<li>kale</li>
</ul>
</li>
<li>
<h1>root</h1>
<ul>
<li>carrot</li>
<li>turnip</li>
<li>potato</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
The chief issue is you're using the wrong CSS property. You want display, not visibility:
.hide {
display: none;
}
If you were using visibility, the value would be hidden, but it would continue to take up room in the layout, which I'm sure isn't what you want.
But separately, the code to hook up those event handlers and add those classes (you've said you're using them for things later) can be a bit simpler:
window.onload = function() {
function toggleNext() {
this.nextElementSibling.classList.toggle("hide");
}
let titles = document.getElementsByTagName('h1');
for (let i = 0 ; i < titles.length ; i++) {
const title = titles[i];
const section = title.nextElementSibling;
const titleClass = title.innerHTML.toLowerCase();
title.classList.add(titleClass);
section.classList.add(titleClass, "section");
titles[i].addEventListener("click", toggleNext);
}
};
Live Example:
window.onload = function() {
function toggleNext() {
this.nextElementSibling.classList.toggle("hide");
}
let titles = document.getElementsByTagName('h1');
for (let i = 0 ; i < titles.length ; i++) {
const title = titles[i];
const section = title.nextElementSibling;
const titleClass = title.innerHTML.toLowerCase();
title.classList.add(titleClass);
section.classList.add(titleClass, "section");
titles[i].addEventListener("click", toggleNext);
}
};
/* trying to toggle visibility with this class*/
.hide {
display: none;
}
/*basic styles to separate elements:*/
h1 {
color: olive;
}
ul {
border: solid orange 1px;
}
li {
//border: solid magenta 1px;
}
<div id="foods">
<h1>Food</h1>
<ul>
<li>
<h1>Fruit</h1>
<ul>
<li>
<h1>tropical</h1>
<ul>
<li>banana</li>
<li>pineapple</li>
<li>mango</li>
</ul>
</li>
<li>
<h1>stone</h1>
<ul>
<li>peach</li>
<li>pear</li>
<li>appricot</li>
</ul>
</li>
</ul>
</li>
<li>
<h1>Vegetables</h1>
<ul>
<li>
<h1>leafy</h1>
<ul>
<li>lettuce</li>
<li>spinach</li>
<li>kale</li>
</ul>
</li>
<li>
<h1>root</h1>
<ul>
<li>carrot</li>
<li>turnip</li>
<li>potato</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
Side note: I would recommend not using window.load unless you really need this code to wait for all images, stylesheets, etc., to fully load before running. Instead, just make sure the code is in a script tag at the end of the document, just prior to the closing </body> tag.

How to select link of current page

I am trying to have my links in a menu styled when I mouse over them.
Then when the mouse leaves the link (without clicking on it) I want the current link to go back to being styled.
html:
<div id="header">
<div id="title"><h1>Title<span id="Subtitle">Subtitle</span></h1></div>
<nav class="cf" id="menu">
<ul>
<li>ABOUT</li>
<li>GALLERY</li>
<li>BIO</li>
<li>CONTACT</li>
<li>HOME</li>
</ul>
</nav>
</div>
css
.current { color: #FFBB3F;}
js
$( "nav a" ).on('mouseover', function(){
$( "nav a.current" ).removeClass("current");
$(this).addClass("current");
});
$( "nav a" ).on('mouseleave', function(){
$(this).removeClass("current");
var pageURL = $(location).attr("href");
$('a[href="pageURL"]').addClass("current");
});
but this is not working. if I do an alert
alert(pageURL);
it gives me the path to the current page, and if I paste just an href
$('a[href="index.html"]').addClass("current");
it does style that link, but obviously I would want the current link to be styled. First time I try this. Any help appreciated. Thanks
This will add and remove the current class as well as style links being hovered over:
var anchors = document.querySelectorAll("#menu a");
// Assign mouseover and mouseout event handlers to each anchor
for(var i = 0; i < anchors.length; i++){
anchors[i].addEventListener("mouseover", styleIn);
anchors[i].addEventListener("mouseout", styleOut);
}
// This will hold a reference to whichever anchor has the current class
var currentAnchor = null;
function determineCurrent(){
for(var i = 0; i < anchors.length; i++){
if(anchors[i].classList.contains("current")){
currentAnchor = anchors[i];
}
}
}
function styleIn(e){
determineCurrent();
e.target.classList.add("hover");
currentAnchor.classList.remove("current");
}
function styleOut(e){
e.target.classList.remove("hover");
currentAnchor.classList.add("current");
}
a { text-decoration:none; }
.current { color: #FFBB3F; }
.hover { text-decoration:underline;}
<div id="header">
<div id="title"><h1>Title<span id="Subtitle">Subtitle</span></h1></div>
<nav class="cf" id="menu">
<ul>
<li>ABOUT</li>
<li>GALLERY</li>
<li>BIO</li>
<li>CONTACT</li>
<li>HOME</li>
</ul>
</nav>
</div>
Using just CSS, a combination of rules can get really close (perhaps close enough depending on how #menu is), see comments in CSS section:
/*
1. Color the current one if the menu isn't being hovered
2. Color the current link if being hovered
*/
#menu:not(:hover) .current, /* 1 */
#menu a:hover { /* 2 */
color: #FFBB3F;
}
<div id="header">
<div id="title"><h1>Title<span id="Subtitle">Subtitle</span></h1></div>
<nav class="cf" id="menu">
<ul>
<li>ABOUT</li>
<li>GALLERY</li>
<li>BIO</li>
<li>CONTACT</li>
<li>HOME</li>
</ul>
</nav>
</div>
That CSS-only version has the issue that if you're not hovering a link but you are hovering the #menu, nothing is highlighted. I can't think of a pure CSS way to handle that, so a bit of JavaScript (see comments):
// Set a class on #menu only when hovering a link
$("#menu")
.on("mouseenter", "a", function() {
$("#menu").addClass("link-hover");
})
.on("mouseleave", "a", function() {
$("#menu").removeClass("link-hover");
});
/*
1. Color the current one if the menu doesn't have the link-hover class
2. Color the current link if being hovered
*/
#menu:not(.link-hover) .current, /* 1 */
#menu a:hover { /* 2 */
color: #FFBB3F;
}
<div id="header">
<div id="title"><h1>Title<span id="Subtitle">Subtitle</span></h1></div>
<nav class="cf" id="menu">
<ul>
<li>ABOUT</li>
<li>GALLERY</li>
<li>BIO</li>
<li>CONTACT</li>
<li>HOME</li>
</ul>
</nav>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

How to show/hide when hover in/out on specific element

I make a secondary menu and I like it to be displayed when user hover a specific one of the main menu items....
I tried this code but it didn't work...
.second-menu {display:none}
ul li #2:hover + .second-menu {display:block}
<ul>
<li id="1">first</li>
<li id="2">second</li>
<li id="3">third</li>
<ul>
<div class="second-menu">
<ul>
<li>page1</li>
<li>page2</li>
<li>page3</li>
</ul>
</div>
any suggestions?....
only by css or javascript....
If you wish to use CSS, you will have to put your sub menu inside the element that you want to hover.
For the CSS, C.Raf.T's answer is perfect.
If for some reason you want to use javascript you could do something like this
document.getElementById('2').addEventListener('mouseenter', function ()
{
document.getElementById('subMenu').style.display = "block";
});
document.getElementById('2').addEventListener('mouseleave', function ()
{
document.getElementById('subMenu').style.display = "none";
});
Note: the above code requires you to add a "subMenu" id to the div containing your menu. If you wish to display serval menus with only one hover event, use a class instead.
But honestly, the CSS answer is the best way to go if all you need is nested sub menus.
If the sub menu has to be outside of the parent, you will need the javascript.
.second-menu{
display:none;
}
li:hover >.second-menu{
display:block;
}
<ul>
<li id="1">first</li>
<li id="2">second
<ul class="second-menu">
<li>page1</li>
<li>page2</li>
<li>page3</li>
</ul>
</li>
<li id="3">third</li>
<ul>
Answer using Javascript,
document.getElementById('hover').onmouseover = function(){
document.getElementById('second-menu').style.display = 'block';
}
document.getElementById('hover').onmouseout = function(){
document.getElementById('second-menu').style.display = 'none';
}
.second-menu{
display:none;
}
<ul id="hover">
<li id="1">first</li>
<li id="2">second</li>
<li id="3">third</li>
<ul>
<div class="second-menu" id="second-menu">
<ul>
<li>page1</li>
<li>page2</li>
<li>page3</li>
</ul>
</div>
Here is a fiddle
By using pure CSS you have to ensure that your submenu (.second-menu) is a child-node of your hovered HTML-Element. Because CSS unfortunately doesn't know a parent selector.
By using JS you are more flexible. Means placing the submenu wherever you wish.
* { margin: 0; padding: 0; }
.second-menu {display:none; border: 1px solid blue; width: 100%; position: absolute; left: 0; right: 0; }
ul li#two:hover > .second-menu {display:block}
.relative { position: relative; border: 1px solid black; }
li { display: inline-block; }
<ul class="relative">
<li id="one">first</li>
<li id="two">second
<ul class="second-menu">
<li>page1</li>
<li>page2</li>
<li>page3</li>
</ul>
</li>
<li id="three">third</li>
<ul>

Why does replacing class name using Javascript if statement not working?

$(document).ready(checkWidth);
$(window).resize(checkWidth);
function checkWidth() {
var $window = $(window);
$("pre").text($window.width());
if ($window.width() < 321) {
$('.grve-tablet-column-1-4')
.removeClass('grve-tablet-column-1-2').addClass('grve-tablet-column-1');
}
else {
$('.grve-tablet-column-1')
.removeClass('grve-tablet-column-1').addClass('grve-tablet-column-1-2');
}
}
<footer id="grve-footer">
<div class="grve-container">
<div id="grve-footer-area" class="grve-section" data-section-type="fullwidth-background" style="visibility: visible; margin-left: 158px; margin-right: 158px;">
<div class="grve-row">
<div class="grve-column-1-4 grve-tablet-column-1-2"><div id="text-3" class="grve-widget widget widget_text"> <div class="textwidget"><ul class="footerlist">
<li class="maintitle">Home</li>
<li class="title">Features</li>
<li>Venue Setup</li>
<li>Visitor's App</li>
<li class="title">Pricing</li>
<li>Venue Plan Request</li>
<li>Venue Hardware</li>
</ul></div>
</div></div><div class="grve-column-1-4 grve-tablet-column-1-2"><div id="text-2" class="grve-widget widget widget_text"> <div class="textwidget"><ul class="footerlist">
<li class="maintitle">Support</li>
<li> FAQ</li>
<li class="secondtitle">Try Sine for Free<br>Enterprise Login</li>
<li>Privacy Policy</li>
<li>Terms of Use</li>
</ul></div>
</div></div><div class="grve-column-1-4 grve-tablet-column-1-2"><div id="text-4" class="grve-widget widget widget_text"> <div class="textwidget"><ul class="footerlist">
<li class="maintitle">Free Download</li>
<li><img src="http://www.sine.co/wp-content/uploads/2014/11/appstorebutton.png"></li>
<li><img src="http://www.sine.co/wp-content/uploads/2014/11/googleplaybutton.png"></li>
</ul></div>
</div></div><div class="grve-column-1-4 grve-tablet-column-1-2"><div id="text-5" class="grve-widget widget widget_text"> <div class="textwidget"><ul class="footerlist">
<li class="mail">info#sine.co</li>
<li class="skype">sinehelp</li>
<li class="phone">+61 8 8121 5956</li>
<li class="twitter">SineHQ</li>
<li class="linkedin">SineHQ</li>
</ul></div>
</div></div> </div>
</div>
<div id="grve-footer-bar" class="grve-section" data-section-type="fullwidth-element" data-align-center="yes" style="visibility: visible; padding-left: 0px; padding-right: 0px; margin-left: 158px; margin-right: 158px;">
<div class="grve-row">
<div class="grve-column-1-2">
<div class="grve-copyright">
<p style="text-align: center;">Copyright 2015 Sine Group Pty Ltd. Patents Pending.</p> Website by Bugeja Studio
</div>
</div>
</div>
</div>
</div>
</footer>
I would like to find out how i would go replacing class names, using JavaScript for 4 divs in my footer named: class="grve-column-1-4 grve-tablet-column-1-2".
I have tried using this code with no luck:
$(document).ready(function() {
var $window = $(window);
// Function to handle changes to style classes based on window width
function checkWidth() {
if ($window.width() < 321) {
$('.grve-tablet-column-1-2').removeClass('grve-tablet-column-1-2').addClass('grve-tablet-column-1');
}
}
// Execute on load
checkWidth();
// Bind event listener
$(window).resize(checkWidth);
});
What happens is when you access the website on mobile, the footer looks like this:
To fix this issue i can change the classes of these 4 divs to grve-tablet-column-1, and as a result it will organise under a single column like this:
Try this :
// Execute on load
$(document).ready(checkWidth);
// Bind event listener
$(window).resize(checkWidth);
// Function to handle changes to style classes based on window width
function checkWidth() {
var $window = $(window);
$("pre").text($window.width());
if ($window.width() < 321) {
$('.grve-tablet-column-1-2')
.removeClass('grve-tablet-column-1-2').addClass('grve-tablet-column-1');
}
else {
$('.grve-tablet-column-1')
.removeClass('grve-tablet-column-1').addClass('grve-tablet-column-1-2');
}
}
fiddle here: http://jsfiddle.net/u4cn338g/1/
Finally after searching for ages, i found that there was a 30% width placed on those elements which was causing the problem.
When i changed the width to 100% the columns started to work using css.
So turns out i didnt even need the JavaScript, although its some useful information if i need it next time for something :)
Thanks for you help guys.
Alex

Categories

Resources