JavaScript Accordion - Disable closing the panel - javascript

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")

Related

Collapse/close a button only by clicking on it

I'm trying to code a responsive button which can be placed multiple times in the same line, showing its content always below the line containing the button.
In the snippet there is a working code, but it has a small flaw: since the pseudo-class focus is used, once the button is opened it's enough to click anywhere on the screen to close it.
The usual behaviour for a button is that to close it you have to click on it, so is it possibile to get this behaviour also for this one?
I used other pseudo-classes but without success, I guess only a javascript can do the job.
.container {
position: relative;
margin: 2em;
}
.details {
display: none;
}
.collapsible:focus {
margin-bottom: 1.5em;
pointer-events: none;
}
.collapsible:focus + .details
{
display: block;
margin-top: -1.15em;
position: absolute;
left: 0;
right: 0;
background: yellow;
}
<div class=container>
You can <button class="collapsible">place</button><span class=details>yes</span> more than <button class="collapsible">one</button><span class=details>nice</span> per line, they are responsive and the content is always shown just <button class="collapsible">below</button><span class=details>cool</span> the line containing the button.
But once opened, you can close it with a click <button class="collapsible">everywhere</button><span class=details>not good</span> on the screen
</div>
Javascript for further customization
<script type="text/javascript">
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.parentElement.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
</script>
Implementation of the idea was a bit more complicated so I'll just answer.
This uses an old trick whereby a label, associated with a hidden checkbox, is used as the click target. Since clicking on a label checks or unchecks the checkbox, and there is a pseudo-class for the checked state of the checkbox, we can use that to persist the state of our styles. Credit to TylerH for his answer to the similar question Can I have an onclick effect in CSS?.
I've implemented it here by using a partial attribute selector, so in this example any checkboxes have to have an ID that begins with "demo". The checkboxes do have to have an ID for the for attribute of the label to hook onto.
.container {
position: relative;
margin: 2em;
}
.collapsible:focus {
margin-bottom: 1.5em;
pointer-events: none;
}
label {
display: inline-block;
background: lightgrey;
}
[id^="demo"] {
display: none;
}
/* details that are next to labels that are next to unchecked checkboxes are hidden */
[id^="demo"]:not(:checked)+label+.details {
display: none;
}
/* details that are next to labels that are next to checked checkboxes are displayed */
[id^="demo"]:checked+label+.details {
display: block;
position: absolute;
left: 0;
right: 0;
background: yellow;
}
/* labels that are next to unchecked checkboxes have a different color
so you can track which ones are "on" */
[id^="demo"]:checked+label {
background: blue;
color: white;
}
<div class=container>
You can <input type="checkbox" id="demo01" /><label for="demo01" >place</label><span class=details>yes</span> more than <input type="checkbox" id="demo02" /><label for="demo02">one</label><span class=details>nice</span> per line, they are responsive and the content is always shown just <input type="checkbox" id="demo03" /><label for="demo03">below</label><span class=details>cool</span> the line containing the button. But once opened, you can close
it with a click <input type="checkbox" id="demo04" /><label for="demo04">everywhere</label><span class=details>not good</span> on the screen
</div>

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

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() {

On button click display list items, dropdown list should not hide until button is clicked again

I have looked everywhere possible as I am trying to develop a drop down button, but instead of options then the buttons display unordered list items, but when a user click off the button the the button does not close, but in order to close the button, then the button needs to be click again.
Down below you will find the way the button is when not clicked and the way the button appears when it has been clicked.
If you also go to the following website you will see an example of the button in action by click "See our list of websites"
Button on a website for example
Any help would be greatly appreciated and thanks in advance.
Here you go, key functions I used;
.click() for the a tag or the link, the function inside the .click() will be called.
.slideToggle() the ul after the click, this would hide or show the target element depending on its state.
Then add positon:absolute to the ul so that it wouldn't affect inline elements.
$(document).ready(function() {
$(".toggle-button").click(function() {
$(this).parent().find("ul").slideToggle(function() {
// Animation complete.
});
});
})
.links-unordered {
display: inline-block;
position: relative;
}
.links-unordered {
margin-top: 20px;
min-height: 30px;
}
.links-unordered .toggle-button {
text-decoration: none;
padding: 12px 16px 12px 16px;
transition: 0.2s;
border: 1px solid black;
}
.links-unordered .toggle-button:hover,
.links-unordered .toggle-button:active,
.links-unordered .toggle-button:focus,
.links-unordered .toggle-button:visited {
text-decoration: none;
color: black;
}
.links-unordered .toggle-button:hover {
border-width: 2px;
}
.links-unordered ul {
position: absolute;
top: 10px;
margin-top: 25px;
padding-inline-start: 20px;
}
.links-unordered ul li {
line-height: 25px;
padding-left: 15px;
}
.links-unordered a {
text-decoration: none;
color: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="links-unordered">
<a class="toggle-button" href="#">SEE OUR LIST OF WEBSITES</a>
<ul style="display:none;">
<li>cdn.sc.rockstargames.com</li>
<li>lifeinvader.com</li>
<li>rockstargames.com</li>
<li>socialclub.rockstargames.com</li>
</ul>
</div>
<div class="links-unordered">
<a class="toggle-button" href="#">SEE OUR LIST OF WEBSITES</a>
<ul style="display:none;">
<li>cdn.sc.rockstargames.com</li>
<li>lifeinvader.com</li>
<li>rockstargames.com</li>
<li>socialclub.rockstargames.com</li>
</ul>
</div>
I'm not sure if I understand what you want. But here's a sample of what you asked, a button that when you click, show a list. And when you click on an item, the list goes out and you have the item. Hope this helps you. It's a simple code, but if you have questions, go ahead and ask!!
function closeList(e) {
var site = e.target.innerText;
alert(site + ' clicked!!');
document.querySelector('#dvSites').style.display = 'none';
}
function showList() {
var dvSites = document.querySelector('#dvSites');
if (dvSites.style.display === '')
return; // already visible
dvSites.style.display = '';
}
// Add eventListener to close the div
var lis = document.querySelector('#dvSites').querySelectorAll('li');
for(var i = 0; i < lis.length; i++) {
lis[i].addEventListener('click', closeList);
}
// Add eventListener to open the div
document.querySelector('#btnShow').addEventListener('click', showList);
<button id="btnShow">Show sites!!</button>
<div id="dvSites" style="display: none">
<ul>
<li>stackoverflow.com</li>
<li>www.google.com</li>
<li>www.sipmann.com</li> <!-- :) -->
</ul>
</div>

Remove CSS hover functionality using Javascript

I need to remove CSS hover functionality using JavaScript.
I have a button on a form which submits data to our db server. Using OnClientClick() of an ASP.NET Button control I would like to change the element's text to 'Submitting..' using getElementById(), change the background color of the button to Light Grey and more importantly disable the following .button:hover effect I have in my CSS.
.button:hover,
.content-form input.button:hover,
#comment-form #submit:hover
{
background-color: #333;
}
All I am really interested in is the Javascript to remove/disable the above CSS
e.g. OnClientClick="getElementByID('ButtonName').blahblahblah;"
Working fiddle: https://jsfiddle.net/bm576q6j/17/
var elms = document.getElementsByClassName("test");
var n = elms.length;
function changeColor(color) {
for(var i = 0; i < n; i ++) {
elms[i].style.backgroundColor = color;
}
}
for(var i = 0; i < n; i ++) {
elms[i].onmouseover = function() {
changeColor("gray");
};
}
Edit: Sorry for not noticing last part of your question before I answered :)
There are a lot of solutions for solving your problem.
For example:
1- Using HTML disabled attribute.
OnClientClick="getElementByID('ButtonName').disabled=true;
2- Add a class which overrides the previous style.
.button:hover,
.content-form input.button:hover,
#comment-form #submit:hover
{
background-color: #333;
}
.button.submitted:hover
{
background-color: gray;
}
Js:
OnClientClick="getElementByID('ButtonName').className = "submitted";
and etc
In this case it removes the class attribute eliminating all defined classes, but then adds that should not be removed.
On jsfiddle
function disableHover(elem) {
elem.removeAttribute('class');
elem.setAttribute('class', 'another');
}
.button:hover {
background-color: #333;
}
.another {
background-color: lightgray;
}
<button class="button" onclick="disableHover(this);">hover</button>
But the best way of doing this is so, simple and works well.
function disableHover(elem) {
elem.classList.remove('button');
elem.classList.add('another');
}
.button:hover {
background-color: #333;
}
.another {
background-color: lightgray;
}
<button class="button" onclick="disableHover(this);">hover</button>
On jsfiddle
First of all your css is wrong. It should be:
.button:hover, .content-form input.button:hover, #comment-form, #submit:hover {
background-color: #333;
}
and you are adding css with id and class. You should not do that. Just add with class and use document.getElementById('submit').removeAttribute('class')

Forcing mobile devices to activate :hover CSS properties on first touch and activate link on second touch

: )
So, I'm trying to solve a hover effect issue. I have tooltips on some of my links. Code looks like this:
<a href="https://wikipedia.org/wiki/Space_Shuttle_Atlantis">
<h6 class="has-tip">Space Shuttle
<p class="tip">The space shuttle was invented by Santa Claus</p>
</h6>
</a>
And the CSS is a bit more involved:
.tip {
display: block;
position: absolute;
bottom: 100%;
left: 0;
width: 100%;
pointer-events: none;
padding: 20px;
margin-bottom: 15px;
color: #fff;
opacity: 0;
background: rgba(255,255,255,.8);
color: coal;
font-family: 'Ubuntu Light';
font-size: 1em;
font-weight: normal;
text-align: left;
text-shadow: none;
border-radius: .2em;
transform: translateY(10px);
transition: all .25s ease-out;
box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.28);
}
.tip::before {
content: " ";
display: block;
position: absolute;
bottom: -20px;
left: 0;
height: 20px;
width: 100%;
}
.tip::after { /* the lil triangle */
content: " ";
position: absolute;
bottom: -10px;
left: 50%;
height: 0;
width: 0;
margin-left: -13px;
border-left: solid transparent 10px;
border-right: solid transparent 10px;
border-top: solid rgba(255,255,255,.8) 10px;
}
.has-tip:hover .tip {
opacity 1;
pointer-events auto;
transform translateY(0px);
}
Now, on desktop this works wonderfully. You hover over the tiny title and you get a pretty looking tooltip, then if you click anywhere on the title or tooltip (unless you decide to put yet another link in the paragraph which works separately and nicely) you activate the link. Yay : )
Now on mobile, the whole thing gets funky. Touching just activates the link. If you have slow internet, or iOS, you might glimpse the tooltip just as the next page loads.
I would like the following behavior:
User taps on tiny title (h6) which has class (has-tip)
If this is the first tap, the tooltip shows, and nothing else happens. 3)
If the tooltip is already showing when they tap (as in a subsequent
tap) then the link is activate and the new page loads.
Any ideas how I might implement this? No jQuery if possible.
One way to do it is to save a reference to the last clicked has-tip link and to apply a class to it which forces the tip to show. When you click on a link and it matches the the last one clicked, you let the event pass.
EDIT: oh, I forgot to mention you might need a classList shim for old IE.
JSFiddle link.
HTML
<a href="http://jsfiddle.net/1tc52muq/5/" class="has-tip">
JSFiddle<span class="tip">Click for some fun recursion</span>
</a><br />
<a href="http://google.com" class="has-tip">
Google<span class="tip">Click to look for answers</span>
</a>
JS
lastTip = null;
if(mobile) {
var withtip = document.querySelectorAll(".has-tip");
for(var i=0; i<withtip.length; ++i) {
withtip[i].addEventListener("click", function(e) {
if(lastTip != e.target) {
e.preventDefault();
if(lastTip) lastTip.classList.remove("force-tip");
lastTip = e.target;
lastTip.classList.add("force-tip");
}
});
}
}
CSS
.has-tip {
position: abolute;
}
.tip {
display: none;
position: relative;
left: 20px;
background: black;
color: white;
}
.has-tip:hover .tip, .force-tip .tip {
display: inline-block;
}
Edit: Just wanted to say that Jacques' approach is similar, but much more elegant.
On touch devices, you'll need to make a click/tap counter:
1) On first tap of any link, store the link and display the hover state.
2) On another tap, check to see if it's the same as the first, and then perform the normal tap action if it is. Otherwise, clear any existing hovers, and set the new tap target as the one to count.
3) Reset / clear any hovers if you tap on non-links.
I've made a rudimentary JSFiddle that console.logs these actions. Since we're not using jQuery, I didn't bother with adding/removing CSS classes on the elements.
Also, sorry about not writing taps instead of clicks.
var clickTarget;
var touchDevice = true;
if(touchDevice) {
var links = document.getElementsByTagName('a');
for(var i=0; i<links.length; i++) {
links[i].onclick = function(event) {
event.stopPropagation();
event.preventDefault(); // this is key to ignore the first tap
checkClick(event);
};
};
document.onclick = function() {
clearClicks();
};
}
var checkClick = function(event) {
if(clickTarget === event.target) {
// since we're prevent default, we need to manually trigger an action here.
console.log("Show click state and also perform normal click action.");
clearClicks();
} else {
console.log("New link clicked / Show hover");
clickTarget = event.target;
}
}
var clearClicks = function() {
console.log("Clearing clicks");
clickTarget = undefined;
};
http://jsfiddle.net/doydLt6v/1/

Categories

Resources