I have these accordions that open and close. I'd also like the accordion to scroll to the top of the page when it's clicked.
HTML:
<button class="accordion"><h2>Title</h></button>
<div class="panel">
<p>some text</p>
</div>
Javascript that works to open and close the accordion:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
/* Toggle between adding and removing the "active" class,
to highlight the button that controls the panel */
this.classList.toggle("active");
/* Toggle between hiding and showing the active panel */
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}
Javascript I tried to add to make it scroll to the top but it doesn't work:
$('button').click(function() {
$(this).animate({
scrollTop:0
}, 1000);
});
So, I think you can change your approach.
Considering you're using jQuery, your HTML can be like this:
...
<div class="accordion">
<div class="accordion-header">Your accordion header</div>
<div class="accordion-content">Your Accordion Content</div>
</div>
...
your Javascript like this:
function scrollToElement($el, time = 500){
$([document.documentElement, document.body]).animate({
scrollTop: $el.offset().top
}, time);
}
function setupAccordion(){
$('.accordion .accordion-header').click(function(){
let $parent = $(this).parent();
$parent.toggleClass('active');
scrollToElement($parent);
});
}
$(document).ready(function(){
setupAccordion();
});
And your CSS like this:
.accordion-header{
border-bottom:1px solid #ccc;
padding:1em;
cursor:pointer;
font-weight:bold;
}
.accordion-content{
max-height:0;
opacity:0;
visibility:hidden;
transition:all 0.2s ease;
padding:0;
}
.accordion.active .accordion-content{
max-height:500px;
visibility:visible;
opacity:1;
padding:1em;
}
Playground: https://jsfiddle.net/m0fbq2j4/
Related
I'm trying to create a sidebar toggle which would change a couple of things in the CSS but I can't quite get it to work.
I have a DIV which is currently changing it's input on hover from SITENAME.COM to MENU. I want the div #site-name to toggle the sidebar on the left.
The DIV #site-name is inside a container/wrapper DIV called #body.
It toggles correctly, but it will not reset the body margin back to 0. After opening the sidebar, the wrapper div keeps the margin set by the open function.
I haven't coded for a while so any help would be appreciated.
<div id="site-name" onclick='openNav();'>MAN&WOLF.MEDIA</div>
<script type="text/javascript">
$(document).ready( function()
{
var cart_visible = false;
$('#site-name').on('click', function()
{
if ( cart_visible )
$('#mySidebar').css('margin-left', '-250px');
else
$('#mySidebar').css('margin-left', '0px');
$('#body').css('margin-left', '0px');
cart_visible = !cart_visible;
});
});
</script>
You did not set the margin-left attribute to #body when opening up the side bar.
if ( cart_visible ) {
$('#mySidebar').css('margin-left', '-250px');
$('#body').css('margin-left', '0px');
} else {
$('#mySidebar').css('margin-left', '0px');
$('#body').css('margin-left', '250px');
}
I think the problem is that you didn't wrap your else block in brackets. So the line: $('#body').css('margin-left', '0px'); is called whether you're opening or closing the menu.
$(document).ready(function() {
var cart_visible = false;
$('#site-name').on('click', function() {
if (cart_visible) {
$('#mySidebar').css('width', '0');
$('#body').css('margin-left', '0');
} else {
$('#mySidebar').css('width', '250px');
$('#body').css('margin-left', '250px');
}
cart_visible = !cart_visible;
});
});
body {
margin: 0;
}
#body {
background-color: lightblue;
height: 100vh;
}
#mySidebar {
width: 0px;
height: 100vh;
background-color: coral;
margin-left: -250px;
position: absolute;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="body">
<div id="site-name">MAN&WOLF.MEDIA</div>
<div id="mySidebar">
Sidebar content
</div>
</div>
For my accordion menu, I am having difficulty trying to figure out how to keep my accordion panels open when the browser loads. I've tried removing overflow:hidden; for the panels but I run into errors. I want to be able to have my panels open when the browser loads, but let the user open/close the links afterwards.
HTML
<div class="sideMenu">
<div class="logo">TS<div>
<div class="smallSpacer"></div>
<div class="accordion">code</div>
<div class="panel">
<p>uhn redesign </p>
<p>180 websites </p>
</div>
<div class="bigSpacer"></div>
<div class="accordion">design</div>
<div class="panel">
<p>gigloo </p>
<p>space is the place</p>
<p>tyler the creator </p>
<p>nike concepts</p>
<p>portraits/fashion</p>
</div>
<div class="bigSpacer"></div>
<div class="accordion">personal</div>
<div class="panel">
<p>product</p>
<p>animations</p>
<p>comics</p>
</div>
</div>
CSS
body{
font-family: 'Roboto Mono', monospace;
}
.sideMenu{
background-color:black;
width:250px;
height:100%;
position:fixed;
margin:20px;
text-align:center;
}
.logo{
color:white;
}
.smallSpacer{
margin-bottom:15px;
}
.bigSpacer{
margin-bottom:20px;
}
.accordion {
color: white;
cursor: pointer;
transition: 0.4s;
font-weight:500;
}
.active, .accordion:hover {
}
.panel {
font-weight:300;
max-height: 0;
overflow: hidden;
transition: max-height 0.1s ease-out;
}
JAVASCRIPT
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
Here is my Codepen. https://codepen.io/taniasharma98/pen/KKwQXBK
Here is my solution:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
//this is where magic happens
if(i==0){
acc[0].classList.toggle("active");
var panel = acc[0].nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
}
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.maxHeight) {
panel.style.maxHeight = null;
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
});
}
As you can see, all I did was expand the accordion for the first element.
I have copied and pasted the same code that the event listener would have executed and instead attached it to the first element directly at startup. This also helps to retain the collapsibility as the event listeners have been added irrespective of the other code.
Hope this helps!
I have 15 buttons, each button has content, but when I clicked button1 and followed by button 2, the button 1 is still open. How can I close the button 1 if I click button2?
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
div.panel {
position: absolute;
max-width: 0;
overflow: hidden;
opacity: 0;
}
div.panel.show {
opacity: 1;
max-width: 900px;
}
<button class="accordion">The Ball</button>
<div class="panel">
<h1>The Ball</h1>
</div>
<button class="accordion">The Cat</button>
<div class="panel">
<h1>The Cat</h1>
</div>
<button class="accordion">The Dog</button>
<div class="panel">
<h1>The Dog</h1>
</div>
You need to remove active and show classes from previous div before adding them to clicked div.
Your JS Code will be:
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
var previous = document.querySelector(".active"); //select previous button
if (previous) { // check because when first time no button has active class
previous.classList.remove("active");
previous.nextElementSibling.classList.remove("show");
}
this.classList.add("active");
this.nextElementSibling.classList.add("show");
};
}
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
var previous = document.querySelector(".active");
if (previous) {
previous.classList.remove("active");
previous.nextElementSibling.classList.remove("show");
}
this.classList.add("active");
this.nextElementSibling.classList.add("show");
};
}
div.panel {
position: absolute;
max-width: 0;
overflow: hidden;
opacity: 0;
}
div.panel.show {
opacity: 1;
max-width: 900px;
}
<button class="accordion">The Ball</button>
<div class="panel">
<h1>The Ball</h1>
</div>
<button class="accordion">The dog</button>
<div class="panel">
<h1>The dog</h1>
</div>
<button class="accordion">The cat</button>
<div class="panel">
<h1>The cat</h1>
</div>
So select the active one. If it is active remove it. If the current one is not the active class add it.
// select all the buttons
var btns = document.querySelectorAll('.accordion')
// loop over
btns.forEach(function (btn) {
// bind the click
btn.addEventListener('click', function (evt) {
// stop button click
evt.preventDefault()
// find the active one
var active = document.querySelector('.accordion.active')
// see if active button is the clicked button
var isSame = active == btn
// if we have an active button, remove the class
if (active) active.classList.remove('active')
// if the current button was not the active one add the class
if (!isSame) btn.classList.add('active')
})
})
button {
display: block;
}
button + div {
max-height: 0;
transition: max-height 0.25s ease-out;
overflow: hidden;
}
button.active {
background-color: green;
}
button.active + div {
max-height: 500px;
transition: max-height 0.5s ease-in;
}
<button class="accordion">The Ball 1</button>
<div><p>Ball 1</p><p>bounce bounce poounce</p></div>
<button class="accordion">The Ball 2</button>
<div><p>Ball 2</p><p> bounce pop</p></div>
<button class="accordion">The Ball 3</button>
<div><p>Ball 3</p><p>bounce bounce over the fence</p></div>
An alternative using JQuery.
I have changed the CSS to remove max-width and opacity and have set .panel elements to be display: none; initially:
div.panel {
position: absolute;
display: none;
}
And using JQuery, whenever we click an .accordion element, we hide the .panel elements (in case any others are showing), and then show the one that relates to the specifically clicked .accordion element.
// register clicks on accordion elements
$('.accordion').click(function() {
// hide each .panel related to every .accordion element
$('.accordion').next().hide();
// show the .panel next to our clicked .accordion element
$(this).next().show();
})
I'm using the W3C accordion on my website, and only want the accordion to be active when the page is under 768px. So far, I have the script adjusted so that the "panel" div's will toggle, but they initially are displayed, rather than hidden. Is there a line I can add to the code to initially hide the panel div's when the resolution is under 768px? I've tried adding display:none to the element in the css sheet, but the toggle script won't override it.
Hope this makes sense!
<script>
if (screen.width < 768) {
var acc = document.getElementsByClassName("filterAccordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
this.classList.toggle("active");
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}
}
</script>
I have a feeling you can manage this in CSS..
#media only screen and (max-width: 768px) {
.filterAccordion {
display: none;
}
}
As for the accordian and mentioned by Quentin.. W3Schools is probably not the best source of information.. For general modules like this perhaps Bootstrap(3/4) might be a more ideal framework if you are open to use one at all.
Edit : MediaQueries
Here's a working solution. Please note that I have commented out some code to demonstrate it in desktop view. Please uncomment that and your code should work in "mobile only" scenario.
document.querySelector('.accordion').addEventListener('click', function (e) {
/*if (screen.width < 768) {*/
var currentTarget = e.target;
if (e.target.classList.contains('accordion-title')) {
var allDesc = Array.prototype.slice.call(document.querySelectorAll('.accordion-description'));
allDesc.forEach(function (el) {
el.classList.add('hidden-xs');
});
e.target.nextElementSibling.classList.remove('hidden-xs');
}
/*}*/
});
/*#media only screen and (max-width: 767px) {
.hidden-xs {
display: none;
}
}*/
/* Comment below code and uncomment above */
/* For demo purpose only */
.hidden-xs {
display: none;
}
.accordion-title,
.accordion-description {
padding: 20px;
margin-bottom: 10px;
}
.accordion-title {
background-color: #ccc;
border-radius: 3px;
border: 1px solid;
}
<div class="accordion">
<div class="accordion-title">Title 1</div>
<div class="accordion-description hidden-xs">Some description</div>
<div class="accordion-title">Title 2</div>
<div class="accordion-description hidden-xs">Some description</div>
</div>
How can I get each menu item highlighted to it's own color as I scroll to it's section in my WordPress site?
Eg: If I scroll down to about section, the about menu item should change color. If I scroll to contacts section, the menu item should change to a different color and obviously ABOUT should dehighlight immediately.
At the moment, when I scroll down 150px, the menu get fixed to the top. Then as I scroll down to about section, the menu item gets bold, when go to another section, the respective menu item will turn bold immediately and obviously the about menu item dehighlights.
var num = 150; //number of pixels before modifying styles
$(window).bind('scroll', function () {
if ($(window).scrollTop() > num) {
$('nav#site-navigation').addClass('fixed');
} else {
$('nav#site-navigation').removeClass('fixed');
}
});
$("nav ul li a").addClass("marker");
var navLinks = $('nav ul li a'),
navH = $('nav').height(),
section = $('section'),
documentEl = $(document);
documentEl.on('scroll', function() {
var currentScrollPos = documentEl.scrollTop();
section.each(function() {
var self = $(this);
if (self.offset().top < (currentScrollPos + navH ) && (currentScrollPos + navH) < (self.offset().top + self.outerHeight())) {
var targetClass = '.' +self.attr('class') + 'marker';
navLinks.removeClass('active');
$(targetClass).addClass('active');
}
});
});
Now for the colors, I added this snippet to the first function but the colors only change when I click on each menu item more than once.
$("a.marker.active:contains(About)").addClass('item-2');
$("a.marker.active:contains(Products)").addClass('item-3');
$("a.marker.active:contains(Scent)").addClass('item-4');
$("a.marker.active:contains(Clients)").addClass('item-5');
$("a.marker.active:contains(Contact)").addClass('item-6');
Any different way of solving this?
$(window).bind('scroll', function() {
$('.toggle-menu').each(function() {
var post = $(this);
var position = post.position().top - $(window).scrollTop();
if (position <= 0) {
post.addClass('selected');
var theID = $(this).attr('id');
$('nav li a').removeClass('active');
$('#nav-'+theID).addClass('active');
} else {
post.removeClass('selected');
}
});
});
.active {
color: red;
}
#nav-about.active {
color: green;
}
#nav-products.active {
color: gray;
}
#nav-scent.active {
color: yellow;
}
header {
position: relative;}
nav {
position: absolute;
display:block;
top:0;
left:0;
width:100%;
}
nav ul {
list-style: none;}
nav li {
display: inline-block;
padding: 5px 10px;}
section {
padding:150px;
border-top:1px solid red;}
.selected {
font-weigt: 600;
color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header>
<nav>
<ul>
<li><a class="active" id="nav-about" href="#about">About</a></li>
<li><a id="nav-products" href="#products">Products</a></li>
<li><a id="nav-scent" href="#scent">Scent</a></li>
<li><a id="nav-clients" href="#clients">Clients</a></li>
</ul>
</nav>
</header>
<section class="toggle-menu" id="about">
<div class="wrap">
About
</div>
</section>
<section class="toggle-menu" id="products">
<div class="wrap">
Products
</div>
</section>
<section class="toggle-menu" id="scent">
<div class="wrap">
Scent
</div>
</section>
<section class="toggle-menu" id="clients">
<div class="wrap">
Clients
</div>
</section>
Something like this?