Skip to content

Instantly share code, notes, and snippets.

@stewartknapman
Last active December 22, 2015 17:08
Show Gist options
  • Save stewartknapman/6503861 to your computer and use it in GitHub Desktop.
Save stewartknapman/6503861 to your computer and use it in GitHub Desktop.
jQuery plugin for expanding and collapsing an element
/*
PM Collapse
Expand and collapse a target element but still show the first line of elements child content when its collapsed.
basic example:
<div class="example1">
<a href="#" data-collapse="example-1-target" class="collapse-action">Expand / Collapse</a>
<div id="example-1-target">
...
</div>
</div>
jQuery('.example1').pmCollapse();
not so basic example:
<div class="example2">
<a href="#" class="collapse-all-action">Expand all</a>
<a href="#" data-collapse="example-2a-target" class="another-collapse-action">Expand</a>
<div id="example-2a-target">
...
</div>
<a href="#" data-collapse="example-2b-target" class="another-collapse-action">Expand</a>
<div id="example-2b-target">
...
</div>
<a href="#" data-collapse="example-2c-target" class="another-collapse-action">Expand</a>
<div id="example-2c-target">
...
</div>
</div>
jQuery('.example2').pmCollapse({
actionLink: '.another-collapse-action', // define class name for action links
collapseAll: true, // collapse all elements on load
onCollapse: function($link, $element){ // Callback function is run when element is collapsed
$link.text('Expand'); // e.g. do something link update the current links text
},
onExpand: function($link, $element){ // Callback function is run when element is expanded
$link.text('Collapse');
},
globalActionLink: '.collapse-all-action', // define a global link to toggle all elements
onGlobalCollapse: function($link){ // Callback function is run when all elements are collapsed
$link.text('Expand all');
},
onGlobalExpand: function($link){ // Callback function is run when all elements are expanded
$link.text('Collapse all');
}
});
*/
(function($){
var PMCollapseClass = function(el, opts){
this.container = $(el);
var options = opts;
var allCollapsed = false;
var actionLink = options.actionLink;
var collapseCallback = options.onCollapse;
var expandCallback = options.onExpand;
var globalActionLink = options.globalActionLink;
var golbalCollapseCallback = options.onGlobalCollapse;
var globalExpandCallback = options.onGlobalExpand;
this.init = function(){ // collapse or expand all on load based on allCollapse setting
if(options.collapseAll){
this.toggleCollapseAll('collapse');
} else {
this.toggleCollapseAll('expand');
}
};
this.toggleCollapseAll = function(action){
$(actionLink).each(function(){
var $link = $(this);
var $target = getTarget($link);
if(action === 'collapse'){
collapse($link, $target, collapseCallback);
} else if(action === 'expand'){
expand($link, $target, expandCallback);
}
});
if(action === 'collapse'){
golbalCollapseCallback($(globalActionLink));
} else if(action === 'expand'){
globalExpandCallback($(globalActionLink));
}
};
this.toggleCollapse = function($link){
var $target = getTarget($link);
if($target.hasClass('collapse-expanded')){
collapse($link, $target, collapseCallback);
} else if($target.hasClass('collapse-collapsed')){
expand($link, $target, expandCallback);
}
};
var toggleCollapse = this.toggleCollapse;
this.container.on('click', actionLink, function(e){
toggleCollapse($(this));
e.preventDefault();
});
if(globalActionLink){
var toggleCollapseAll = this.toggleCollapseAll;
this.container.on('click', globalActionLink, function(e){
if(allCollapsed){
toggleCollapseAll('expand');
} else {
toggleCollapseAll('collapse');
}
e.preventDefault();
});
}
var collapse = function($link, $target, callback){
var height = calculateHeight($target);
$target.addClass('collapse-collapsed').removeClass('collapse-expanded').height(height);
$target.parent().css({'overflow':'hidden'});
if(globalActionLink){ setAllCollapsed(); }
callback($link, $target);
};
var expand = function($link, $target, callback){
$target.addClass('collapse-expanded').removeClass('collapse-collapsed').height('auto');
$target.parent().css({'overflow':'visible'});
if(globalActionLink){ setAllCollapsed(); }
callback($link, $target);
};
var getTarget = function($link){
return $(document.getElementById($link.data('collapse')));
};
var calculateHeight = function($target){
var $content = $target.children().first();
var lineHeight = parseInt($content.css('line-height'), 10);
var marginTop = parseInt($content.css('margin-top'), 10);
return lineHeight + marginTop + 2; // add extra for decenders
};
var setAllCollapsed = function(){
var collection = [];
var collapsed = [];
$(actionLink).each(function(){
var $target = getTarget($(this));
collection.push($target);
if($target.hasClass('collapse-collapsed')){
collapsed.push($target);
}
});
if(collection.length === collapsed.length){
allCollapsed = true;
golbalCollapseCallback($(globalActionLink));
} else {
allCollapsed = false;
globalExpandCallback($(globalActionLink));
}
};
};
/*------*/
// jQuery Function
$.fn.pmCollapse = function( options ){
var opts = $.extend( {}, $.fn.pmCollapse.defaults, options );
return this.each(function(){
var instance = new PMCollapseClass($(this), opts);
instance.init();
});
};
$.fn.pmCollapse.defaults = {
collapseAll: false,
actionLink: '.collapse-action',
onCollapse: function(){}, // $link, $target
onExpand: function(){}, // $link, $target
globalActionLink: null,
onGlobalCollapse: function(){}, // $globalLink
onGlobalExpand: function(){} // $globalLink
};
}(jQuery));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment