Reputation: 11315
I'm using the jQuery UI Accordion (which does not allow more than one item open at a time) on a project. Using accordion is appropriate since I usually do only want one panel open at a time.
However, I need to offer an "Expand All" link which switches to "Collapse All" when clicked. I don't want to custom write near-identical accordion functionality around this one requirement, so I'd like some JS that will achieve this whilst keeping the Accordion component in use.
Question: What JavaScript/jQuery is required to achieve this whilst still using the jQuery UI "Accordion" component to power the standard functionality?
Here's a fiddle: http://jsfiddle.net/alecrust/a6Cu7/
Upvotes: 37
Views: 134849
Reputation: 125
If you are ok with each panel being independent then just put each panel in it's own accordion:
$(".accordion-panel").accordion({
collapsible: true,
multiple: true,
active: 0
});
Then in the html you can create each section as it's own accordion.
<div class="accordion-panel">
<h3 class="accordion-header">Section 1</h3>
<div>
<p>
Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer
ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit
amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut
odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.
</p>
</div>
</div>
<div class="accordion-panel">
<h3 class="accordion-header">Section 2</h3>
<div>
<p>
Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet
purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor
velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In
suscipit faucibus urna.
</p>
</div>
</div>
Upvotes: 1
Reputation: 587
I tried the old-fashioned version, of just adjusting aria-* and CSS attributes like many of these older answers, but eventually gave up and just did a conditional fake-click. Works a beaut':
HTML:
<a href="#" onclick="expandAll();"
title="Expand All" alt="Expand All"><img
src="/images/expand-all-icon.png"/></a>
<a href="#" onclick="collapseAll();"
title="Collapse All" alt="Collapse All"><img
src="/images/collapse-all-icon.png"/></a>
Javascript:
async function expandAll() {
let heads = $(".ui-accordion-header");
heads.each((index, el) => {
if ($(el).hasClass("ui-accordion-header-collapsed") === true)
$(el).trigger("click");
});
}
async function collapseAll() {
let heads = $(".ui-accordion-header");
heads.each((index, el) => {
if ($(el).hasClass("ui-accordion-header-collapsed") === false)
$(el).trigger("click");
});
}
(The HTML newlines are placed in those weird places to prevent whitespace in the presentation.)
Upvotes: 0
Reputation: 11315
In the end I found this to be the best solution considering the requirements:
// Expand/Collapse all
$('.accordion-expand-collapse a').click(function() {
$('.accordion .ui-accordion-header:not(.ui-state-active)').next().slideToggle();
$(this).text($(this).text() == 'Expand all' ? 'Collapse all' : 'Expand all');
$(this).toggleClass('collapse');
return false;
});
Updated JSFiddle Link: http://jsfiddle.net/ccollins1544/r8j105de/4/
Upvotes: 7
Reputation: 11
Using an example about for Taifun, I modified to allow expand and collapse.
... // hook up the expand/collapse all
var expandLink = $('.accordion-expand-all');
expandLink.click(function () {
$(".ui-accordion-content").toggle();
});
Upvotes: 0
Reputation: 17956
A lot of these seem to be overcomplicated. I achieved what I wanted with just the following:
$(".ui-accordion-content").show();
Upvotes: 22
Reputation: 9429
As discussed in the jQuery UI forums, you should not use accordions for this.
If you want something that looks and acts like an accordion, that is fine. Use their classes to style them, and implement whatever functionality you need. Then adding a button to open or close them all is pretty straightforward. Example
HTML
By using the jquery-ui classes, we keep our accordions looking just like the "real" accordions.
<div id="accordion" class="ui-accordion ui-widget ui-helper-reset">
<h3 class="accordion-header ui-accordion-header ui-helper-reset ui-state-default ui-accordion-icons ui-corner-all">
<span class="ui-accordion-header-icon ui-icon ui-icon-triangle-1-e"></span>
Section 1
</h3>
<div class="ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom">
Content 1
</div>
</div>
Roll your own accordions
Mostly we just want accordion headers to toggle the state of the following sibling, which is it's content area. We have also added two custom events "show" and "hide" which we will hook into later.
var headers = $('#accordion .accordion-header');
var contentAreas = $('#accordion .ui-accordion-content ').hide();
var expandLink = $('.accordion-expand-all');
headers.click(function() {
var panel = $(this).next();
var isOpen = panel.is(':visible');
// open or close as necessary
panel[isOpen? 'slideUp': 'slideDown']()
// trigger the correct custom event
.trigger(isOpen? 'hide': 'show');
// stop the link from causing a pagescroll
return false;
});
Expand/Collapse All
We use a boolean isAllOpen
flag to mark when the button has been changed, this could just as easily have been a class, or a state variable on a larger plugin framework.
expandLink.click(function(){
var isAllOpen = $(this).data('isAllOpen');
contentAreas[isAllOpen? 'hide': 'show']()
.trigger(isAllOpen? 'hide': 'show');
});
Swap the button when "all open"
Thanks to our custom "show" and "hide" events, we have something to listen for when panels are changing. The only special case is "are they all open", if yes the button should be a "Collapse all", if not it should be "Expand all".
contentAreas.on({
// whenever we open a panel, check to see if they're all open
// if all open, swap the button to collapser
show: function(){
var isAllOpen = !contentAreas.is(':hidden');
if(isAllOpen){
expandLink.text('Collapse All')
.data('isAllOpen', true);
}
},
// whenever we close a panel, check to see if they're all open
// if not all open, swap the button to expander
hide: function(){
var isAllOpen = !contentAreas.is(':hidden');
if(!isAllOpen){
expandLink.text('Expand all')
.data('isAllOpen', false);
}
}
});
Edit for comment: Maintaining "1 panel open only" unless you hit the "Expand all" button is actually much easier. Example
Upvotes: 54
Reputation: 189
This my solution:
Working in real project.
$(function () {
$("#accordion").accordion({collapsible:true, active:false});
$('.open').click(function () {
$('.ui-accordion-header').removeClass('ui-corner-all').addClass('ui-accordion-header-active ui-state-active ui-corner-top').attr({'aria-selected':'true','tabindex':'0'});
$('.ui-accordion-header .ui-icon').removeClass('ui-icon-triangle-1-e').addClass('ui-icon-triangle-1-s');
$('.ui-accordion-content').addClass('ui-accordion-content-active').attr({'aria-expanded':'true','aria-hidden':'false'}).show();
$(this).hide();
$('.close').show();
});
$('.close').click(function () {
$('.ui-accordion-header').removeClass('ui-accordion-header-active ui-state-active ui-corner-top').addClass('ui-corner-all').attr({'aria-selected':'false','tabindex':'-1'});
$('.ui-accordion-header .ui-icon').removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e');
$('.ui-accordion-content').removeClass('ui-accordion-content-active').attr({'aria-expanded':'false','aria-hidden':'true'}).hide();
$(this).hide();
$('.open').show();
});
$('.ui-accordion-header').click(function () {
$('.open').show();
$('.close').show();
});
});
http://jsfiddle.net/bigvax/hEApL/
Upvotes: 18
Reputation: 186
I found AlecRust's solution quite helpful, but I add something to resolve one problem: When you click on a single accordion to expand it and then you click on the button to expand, they will all be opened. But, if you click again on the button to collapse, the single accordion expand before won't be collapse.
I've used imageButton, but you can also apply that logic to buttons.
/*** Expand all ***/
$(".expandAll").click(function (event) {
$('.accordion .ui-accordion-header:not(.ui-state-active)').next().slideDown();
return false;
});
/*** Collapse all ***/
$(".collapseAll").click(function (event) {
$('.accordion').accordion({
collapsible: true,
active: false
});
$('.accordion .ui-accordion-header').next().slideUp();
return false;
});
Also, if you have accordions inside an accordion and you want to expand all only on that second level, you can add a query:
/*** Expand all Second Level ***/
$(".expandAll").click(function (event) {
$('.accordion .ui-accordion-header:not(.ui-state-active)').nextAll(':has(.accordion .ui-accordion-header)').slideDown();
return false;
});
Upvotes: 0
Reputation: 177
I second bigvax comment earlier but you need to make sure that you add
jQuery("#jQueryUIAccordion").({ active: false,
collapsible: true });
otherwise you wont be able to open the first accordion after collapsing them.
$('.close').click(function () {
$('.ui-accordion-header').removeClass('ui-accordion-header-active ui-state-active ui-corner-top').addClass('ui-corner-all').attr({'aria-selected':'false','tabindex':'-1'});
$('.ui-accordion-header .ui-icon').removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e');
$('.ui-accordion-content').removeClass('ui-accordion-content-active').attr({'aria-expanded':'false','aria-hidden':'true'}).hide();
}
Upvotes: 2
Reputation: 21
Here's the code by Sinetheta converted to a jQuery plugin: Save below code to a js file.
$.fn.collapsible = function() {
$(this).addClass("ui-accordion ui-widget ui-helper-reset");
var headers = $(this).children("h3");
headers.addClass("accordion-header ui-accordion-header ui-helper-reset ui-state-active ui-accordion-icons ui-corner-all");
headers.append('<span class="ui-accordion-header-icon ui-icon ui-icon-triangle-1-s">');
headers.click(function() {
var header = $(this);
var panel = $(this).next();
var isOpen = panel.is(":visible");
if(isOpen) {
panel.slideUp("fast", function() {
panel.hide();
header.removeClass("ui-state-active")
.addClass("ui-state-default")
.children("span").removeClass("ui-icon-triangle-1-s")
.addClass("ui-icon-triangle-1-e");
});
}
else {
panel.slideDown("fast", function() {
panel.show();
header.removeClass("ui-state-default")
.addClass("ui-state-active")
.children("span").removeClass("ui-icon-triangle-1-e")
.addClass("ui-icon-triangle-1-s");
});
}
});
};
Refer it in your UI page and call similar to jQuery accordian call:
$("#accordion").collapsible();
Looks cleaner and avoids any classes to be added to the markup.
Upvotes: 2
Reputation: 51
Yes, it is possible. Put all div in separate accordion class as follows:
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery-ui.js"></script>
<script type="text/javascript">
$(function () {
$("input[type=submit], button")
.button()
.click(function (event) {
event.preventDefault();
});
$("#tabs").tabs();
$(".accordion").accordion({
heightStyle: "content",
collapsible: true,
active: 0
});
});
function expandAll()
{
$(".accordion").accordion({
heightStyle: "content",
collapsible: true,
active: 0
});
return false;
}
function collapseAll()
{
$(".accordion").accordion({
heightStyle: "content",
collapsible: true,
active: false
});
return false;
}
</script>
<div class="accordion">
<h3>Toggle 1</h3>
<div >
<p>text1.</p>
</div>
</div>
<div class="accordion">
<h3>Toggle 2</h3>
<div >
<p>text2.</p>
</div>
</div>
<div class="accordion">
<h3>Toggle 3</h3>
<div >
<p>text3.</p>
</div>
</div>
Upvotes: 0
Reputation: 2842
I don't believe you can do this with an accordion since it's specifically designed preserve the property that at most one item will be opened. However, even though you say you don't want to re-implement accordion, you might be over estimating the complexity involved.
Consider the following scenario where you have a vertical stack of elements,
++++++++++++++++++++
+ Header 1 +
++++++++++++++++++++
+ +
+ Item 1 +
+ +
++++++++++++++++++++
+ Header 2 +
++++++++++++++++++++
+ +
+ Item 2 +
+ +
++++++++++++++++++++
If you're lazy you could build this using a table, otherwise, suitably styled DIVs will also work.
Each of the item blocks can have a class of itemBlock
. Clicking on a header will cause all elements of class itemBlock to be hidden ($(".itemBlock").hide()
). You can then use the jquery slideDown()
function to expand the item below the header. Now you've pretty much implemented accordion.
To expand all items, just use $(".itemBlock").show()
or if you want it animated, $(".itemBlock").slideDown(500)
. To hide all items, just use $(".itemBlock").hide()
.
Upvotes: 3