I can expand my div, but how do I shrink it? - javascript

I've got a series of divs that, when clicked, expand a div immediately beneath each one of them. When another div is clicked, any previously expanded div is closed and the one below the clicked div is expanded. This is working really nicely. My problem is that clicking on the already open div should shrink the div beneath it, but it doesn't.
My code is based on this example...
https://wpbeaches.com/create-expandcollapse-faq-accordion-collapse-click/
This example works perfectly - clicking any div closes any other div, including its own if it's already open.
My code works well, but clicking a div doesn't close the div below it if it's already open.
CSS:
/* Style the element that is used to open and close the accordion class */
div.accordion {
background-color: #fff;
color: #444;
cursor: pointer;
padding: 0px;
width: 100%;
text-align: left;
border: none;
outline: none;
transition: 0.4s;
margin-bottom:10px;
opacity: 0.70;
filter: alpha(opacity=70);
}
/* Add a background color to the accordion if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
div.accordion.active, div.accordion:hover {
background: rgb(255,255,255);
background: linear-gradient(90deg, rgba(255,255,255,1) 0%, rgba(232,231,222,1) 50%, rgba(255,255,255,1) 100%);
opacity: 1;
filter: alpha(opacity=100);
}
/* Style the element that is used for the panel class */
div.panel {
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: 0.4s ease-in-out;
opacity: 0;
margin-bottom:10px;
}
div.panel.show {
opacity: 1;
max-height: 500px; /* Whatever you like, as long as its more than the height of the content (on all screen sizes) */
}
JAVASCRIPT:
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var acc = document.getElementsByClassName("accordion");
var panel = document.getElementsByClassName('panel');
for (var i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
var setClasses = !this.classList.contains('active');
setClass(acc, 'active', 'remove');
setClass(panel, 'show', 'remove');
if (setClasses) {
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
}
function setClass(els, className, fnName) {
for (var i = 0; i < els.length; i++) {
els[i].classList[fnName](className);
}
}
});
</script>
HTML:
My html is just like the code in the wpbeaches.com example above.

Add removing logic in your else case or remove the if condition since you are using toggle function. In your case, the toggle is working similar to the add method. Just check with the code
if (setClasses) {
this.classList.add("active");
this.nextElementSibling.classList.add("show");
} else {
this.classList.remove("active");
this.nextElementSibling.classList.remove("show");
}

What I understood is clicking on answer link doesn't close the question but if we click on already open question, it will close the respective answer. It's because you applied the onClick function only on accordian "p" element and not on panel "div" element.
You can apply 1 common class to both accordian and panel element and apply onCLick function on that class.
acc[i].onclick = function() {

Related

Setting up an .active class to a dynamically created link JS

I built a navbar a few weeks back and just realised I did not set an .active class on it. Now, I built the navbar and the links dynamically in JS and would now like to give whichever one is active the according CSS.
This is how I built the navbar in JS:
let womensNav = document.createElement("ul");
womensNav.classList.add("womensNav");
const el1 = document.createElement("li");
el1.innerHTML = "<a>Jeans</a>";
el1.addEventListener("click", (e) => {
document.location.href =
"https://www.martadolska.com/product-category/women/womens-jeans";
});
womensNav.appendChild(el1);
document.querySelector(".ast-woocommerce-container").appendChild(womensNav);
I have more than one link, but for this purpose I don't need to show it all. So now the goal is to build a generic function that gives the active element within the navbar the according class.
document.querySelectorAll("#womensNav li").forEach(function (ele) {
ele.addEventListener("click", function (e) {
e.preventDefault();
document
.querySelectorAll("#womensNav li a.active")
.forEach((ele) => ele.classList.remove("active"));
ele.parentNode.classList.toggle("active");
});
});
And this is what my CSS looks like:
.womensNav li a:hover {
color: var(--main-text-color);
text-decoration: line-through darkred solid;
}
.womensNav li a::before {
content: "";
position: absolute;
width: 100%;
height: 2px;
bottom: 7px;
left: 0;
background-color: #b22222;
visibility: hidden;
transform: scaleX(0);
transition: all 0.3s ease-in-out 0s;
}
.womensNav li a:hover::before {
visibility: visible;
transform: scaleX(1);
}
.womensNav li a:active::before {
content: "";
position: absolute;
width: 100%;
height: 2px;
bottom: 10px;
left: 0;
background-color: #b22222;
}
// up until this point everything works
.active {
text-decoration: line-through darkred solid;
}
I am guessing there is something missing/not completely right in the second snippet of the JS code since nothing is happening when my link is active. I get the animation that I would like to get, but then it disappears once the user is redirected to that specific link, so you wouldn't know which sub-page you are on.
this is wrong
ele.parentNode.classList.toggle("active");
"ele" is the <li>, you are adding the "active" class to the <ul> via the parentNode, might be better to use the "e" event from the click and use e.target and then try and set the active class on the <a> or use childNode/children to get at your <a>

Transitioning Visibility/Opacity w/in JS

I have an alert box that I want to use sessionStorage so that it only appears once. When the user clicks to close the alert, I want the box to disappear (display:none) but fade-out.
I read that you have to use two different functions - one that is activated when clicked and starts the transition and another the adds the 'display' style once transitioned. However, I can't get that to work:
<style>
.ddAlert {
padding: 20px;
box-sizing: border-box;
background-color: #f0ad4e;
color: #fff;
opacity: 1;
transition: opacity 1s;
}
.hide {
opacity: 0;
display: none;
}
</style>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
let dismissed = sessionStorage.getItem("dismissed");
let alertDiv = document.getElementById("alert");
let dismissButton = document.getElementById("dismiss");
if (!dismissed) {
alertDiv.classList.remove("hide");
}
alertDiv.addEventListener("click", function() {
this.style.display = "block";
}.bind(alertDiv));
alertDiv.addEventListener("transitionend", function() {
if (this.className == "hide") {
this.style.display = "none";
}
sessionStorage.setItem("dismissed", true);
}.bind(alertDiv));
});
</script>
<div class="ddAlert hide" id="alert">
SOME ANNOYING ALERT HERE!
<button type="button" id="dismiss">X</button>
</div>
You are on the right track. Instead of listening on click on the alert, use the button as I assume it is there for that reason. When clicking the button the .hide class should be added to the alert. This will start the transition from opacity: 1; to opacity: 0;.
I suggest that instead of using inline-styles, that you stick to classes. Inline styles are hard to overwrite and prevents you from utilizing the full power of CSS. So I've added some classes in there to help you out.
Try out the example below.
<div class="ddAlert hidden" id="alert">
SOME ANNOYING ALERT HERE!
<button type="button" id="dismiss">X</button>
</div>
.ddAlert {
display: block;
transition: opacity 1s;
}
.hide {
opacity: 0;
}
.hidden {
display: none;
}
document.addEventListener("DOMContentLoaded", function() {
let dismissed = sessionStorage.getItem("dismissed");
let alertDiv = document.getElementById("alert");
let dismissButton = document.getElementById("dismiss");
if (!dismissed) {
alertDiv.classList.remove("hidden");
}
dismissButton.addEventListener("click", function() {
alertDiv.classList.add("hide");
});
alertDiv.addEventListener("transitionend", function({ target }) {
if (target.classList.contains("hide")) {
target.classList.add("hidden");
}
sessionStorage.setItem("dismissed", true);
});
});
This answer greatly lends from this SO question titled CSS3 Transition - Fade out effect which notes
When showing the element (by switching to the visible class), we want
the visibility:visible to kick in instantly, so it’s ok to transition
only the opacity property. And when hiding the element (by switching
to the hidden class), we want to delay the visibility:hidden
declaration, so that we can see the fade-out transition first. We’re
doing this by declaring a transition on the visibility property, with
a 0s duration and a delay.
I chose not to mark this question as a duplicate because it also involves the transitionend event. Additionally, I've focused only on the essence of the transition, with a minimal illustration.
The crucial element is the .dismissed-saved class.
let alertDiv = document.getElementById("alert");
let dismissButton = document.getElementById("dismiss");
dismissButton.addEventListener("click", function() {
// kick in the transition
alertDiv.classList.add("dismissed-saved");
// *this is where state should be committed
});
alertDiv.addEventListener("transitionend", function({
target
}) {
if (target === alertDiv) {
// clean up and show a nifty text message illustrating the event handler.
target.classList.add("hidden");
target.classList.remove("dismissed-saved");
document.getElementById("dismissed").classList.remove('hidden');
}
});
.ddAlert {
padding: 20px;
box-sizing: border-box;
background-color: #f0ad4e;
color: #fff;
opacity: 1;
}
.hidden {
display: none;
}
.dismissed-saved {
visibility: hidden;
opacity: 0;
transition: visibility 0s 2s, opacity 2s linear;
}
<div class="ddAlert" id="alert">
SOME ANNOYING ALERT HERE!
<button type="button" id="dismiss">X</button>
</div>
<div id="dismissed" class="hidden">
Dismissed!
</div>
Good luck!

JavaScript Accordion - Disable closing the panel

I have an accordion based on JavaScript and CSS. When the user clicks on a panel, it opens and the header gets a different color. When the user clicks on another panel, the panel selected before is closed and the new one opened.
What I don't want is the user to be able and close the active panel. This is because the header color does not change back and I also change the color of features on the map. If it cannot be disabled, is there a way to access the closed panel so that I can change back the color of the header?
I tried to make a JS fiddle, but I cannot integrate my code, this does not work. The fiddle: https://jsfiddle.net/s0y639ra/6/
This is my code:
JavaScript:
var acc = document.getElementsByClassName('accordion');
var panel = document.getElementsByClassName('accordion-panel');
for (var i = 0; i < acc.length; i++)
{
(function(index){
acc[i].onclick = function()
{
var setClasses = !this.classList.contains("active");
setClass(acc, 'active', 'remove');
setClass(panel, 'show', 'remove');
if (setClasses){
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
var myIndex = index + 1;
console.log("INDEX " + myIndex);
//set the style of the route
routes.setStyle(style_routes);
routes.getLayer(myIndex).setStyle(style_routeClicked).bringToFront();
//zoom to the selected feature
map.fitBounds(routes.getLayer(myIndex).getBounds(),{padding:[200,200]});
}
})(i);
}
function setClass(els, className, fnName){
for (var i=0; i < els.length; i++){
els[i].classList[fnName](className);
}
}
The CSS:
button.accordion {
margin-left: -10px;
background-color: white;
color: #444;
cursor: pointer;
padding: 18px;
height: 100%;
width: 110%;
text-align: left;
border: 0;
border-bottom: 1px solid;
outline: none;
transition: 0.4s;
}
button.accordion {
background-color: #f8f8f8;
}
button.accordion:hover {
background-color: #426334;
color: white;
}
button.accordion:focus {
background-color: #426334;
color: white;
font-weight: bold;
}
div.accordion-panel {
padding: 0 0px;
background-color: none;
display: none;
}
div.accordion-panel.show {
display: block !important;
}
There are two different solutions to that question. While it is quite hard to answer without an usable sample... could you include an jsfiddle link?
One solution would be your requested behavior (user can not close open panel). To achive this you can just check if the active class of the clicked element is already set and only if not you will execute all the other functions you implemented. (Move everything to your if(setClasses){})
The other solution would be to just search for each header having the "active" class before setting the new one this way you can find the index, if you really need it. A better way in my opinion would be to have an identifyer on each trigger which you can use to identify the corresponding elements (e.g. a data-index="XX")

add :hover state to different elem on hover of other elem

So, basically I want to detect when a user hovers over one element (different div element, not parent nor sibling) and when that hover occurs, add :hover to my other div element. My :hover of other div element state is also below via css. Below is one attempt at achieving this by manually adding that css in jQuery chaining method, but did not work at all.
$('.song-thumb .hover-play').hover(function(){
$('section.suggestedAlbums img').css('transform', 'scale(1.2)');
// },function(){
// $('.flyout').hide();
});
section.suggestedAlbums img:hover {
transform: scale(1.2); z-index: 3; cursor: pointer;
}
I could not get the :hover css state to work but here's what I could do
Check out the demo or look at the snippet below.
Hope this helps.
$('.song-thumb .hover-play').on('mouseenter', function(e) {
var elem = $('section.suggestedAlbums img');
elem.trigger(e.type);
elem.addClass('imgHover');
});
$('.song-thumb .hover-play').on('mouseleave', function(e) {
var elem = $('section.suggestedAlbums img');
elem.trigger(e.type);
elem.removeClass('imgHover');
});
var count = 0;
$('section.suggestedAlbums img').hover(function() {
count++;
$('[remove]').html('<label remove><br>It triggers the hover event(' + count + ') too.<br></label>');
});
.song-thumb {
background: aliceblue;
padding: 1em;
}
.hover-play {
cursor: pointer;
color: green;
}
.imgHover {
transform: scale(1.2);
z-index: 3;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="song-thumb"><span class="hover-play">></span>
</div>
<section class="suggestedAlbums">
<img src="http://placehold.it/350x150">
</section><label remove></label>

After resetting transition, div stays visible; no new transition

I'm trying to make a <div> display and then fade out, on button click.
This works so long as the user waits for the fade to complete between <button> presses.
My problem is, if the <button> is clicked while the fade is ongoing, the <div> needs to immediately reappear, and then fade out.
I've managed to get it to immediately reappear, but now it doesn't fade out again.
To get an easier idea of what I'm doing, take a look at the JSFiddle I've setup.
Can anyone help me get this to fade out if clicked whilst already fading out?
I'm only targeting webkit.
<div id="saved">Saved!</div>
<button id="save">Save</button>
function save()
{
// Little "Saved!" div
var div = document.getElementById('saved');
// If still showing from previous save
if(div.style.visibility === 'visible')
{
resetTransition();
div.style.visibility = 'visible';
//div.style.opacity = 0;
console.log('reset');
}
// On transition end
div.addEventListener('webkitTransitionEnd', resetTransition);
function resetTransition()
{
// Disable transitions
div.className = 'notransition';
// Hide the div and reset the opacity
div.style.visibility = 'hidden';
div.style.opacity = 1;
// Need time to let CSS changes (^) refresh
setTimeout(function()
{
// Re-enable transitions
div.className = '';
// Remove the event listener by way of cloning
var dolly = div.cloneNode(true);
div.parentNode.replaceChild(dolly, div);
}, 1);
}
// Show the div and fade out - on timer due to "if still showing" needing
// to process first
setTimeout(function()
{
div.style.visibility = 'visible';
div.style.opacity = 0;
}, 1);
}
document.getElementById('save').addEventListener('click', save);
div#saved
{
-webkit-transition: opacity 1.25s ease-out;
-webkit-transition-delay: 0.75s;
background-color: #FFC;
/* Courtesy of http://fatcow.com/free-icons */
background-image: url('http://i.imgur.com/JMlclKE.png');
background-position: 3px 4px;
background-repeat: no-repeat;
border: 1px solid #333;
border-radius: 6px;
left: 5px;
opacity: 1;
padding: 10px 4px 10px 52px;
position: absolute;
top: 5px;
visibility: hidden;
width: 68px;
}
.notransition
{
-webkit-transition: none !important;
-webkit-transition-delay: none !important;
}
button
{
position: absolute;
top: 100px;
}
I updated your fiddle, moving the cloning to the top and clearing the timeout.
// Little "Saved!" div
clearTimeout(save.t);
var dolly = document.getElementById('saved');
// Remove the event listener by way of cloning
var div = dolly.cloneNode(true);
dolly.parentNode.replaceChild(div, dolly);
/* etc til */
save.t = setTimeout(/* */);

Categories

Resources