채택완료

토글 슬라이드 속도문제(스크립트소스좀 봐주세요)

2016-03-14 (월) 16:32:50 4,218
Copy
/** * jQuery Collapsable - jQuery plugin for collapsable boxes  * http://zipper.github.io/jquery.collapsable/ * * @author Radek Šerý <[email protected]> * @copyright Copyright (c) 2014-2016 Radek Šerý * @license MIT * * @version 2.0.2 */;(function($) { // @feature: díky předávání collapsableEvent do expand.collapsable (atd.) je možné použít e.collapsableEvent.preventDefault() místo defaults.preventDefault! cool, ne?! /**  * Collapsable defaults  * @type {{control: string, box: string, event: string, fx: boolean, fxDuration: number, accordion: boolean, collapsableAll: boolean, preventDefault: boolean, extLinks: {selector: null, preventDefault: boolean, activeClass: string}, classNames: {expanded: string, collapsed: string, defaultExpanded: string}}}  */ var defaults = {  control: '.ca-control', // CSS selector for control element  box: '.ca-box',         // CSS selector for hideable element (box)  event: 'click',         // event triggering the expand/collapse  preventDefault: true,   // whether prevenDefault should be called when specified event occurs on control; even if false, e.collapsableEvent.preventDefault() may be used inside collapsable event handlers  fx: null,               // effect for expanding/collapsing, [ false | toggle | slide | fade | {Object} ]  fxDuration: 0,          // duration of the effect, affects delay between `expand.collapsable`(`collapse.collapsable`) and `expanded.collapsable` (`collapsed.collapsable`) evetns are triggered; default value is 500 when fx set to slide  accordion: false,       // determines, if there could be more than one expanded box in same time; related to jQuery set on which initialized  collapsableAll: true,   // possibility of collapsing all boxes from set  extLinks: {             // external links for operating collapsable set, can be anywhere else in DOM   selector: '',       // CSS selector for external links; it has to be anchors; the click event is binded   preventDefault: false // whether preventDefault is called on extLinks click  },  classNames: {            // CSS class names to be used on collapsable box; they are added to element, on which collapsable has been called   expanded: 'ca-expanded',   collapsed: 'ca-collapsed',   defaultExpanded: 'ca-default-expanded',   extLinkActive: 'ca-ext-active'  }  // callbacks are no more available, use event handlers instead }; /**  * Public methods available via jQuery adapter  * @type {{init: methods.init, expandAll: methods.expandAll, collapseAll: methods.collapseAll, destroy: methods.destroy}}  */ var methods = {  init: function (options) {   return new Collapsable(this, options);  },  expandAll: function (data) {   handlePublicMethods.call(this, 'expandAll', data);  },  collapseAll: function (data) {   handlePublicMethods.call(this, 'collapseAll', data);  },  destroy: function (data) {   handlePublicMethods.call(this, 'destroy', data);  } }; /**  * Last used uid index  * @type {number}  * @private  */ var caUid = 0; /**  * Generates unique id for new Collapsable object  * @returns {String}  New uid  * @private  */ function getUid() {  return 'ca-uid-' + caUid++; } /**  * Handles public method called on jQuery object using adapter  * @param {String} action - collapseAll|expandAll|destroy  * @param {Object} data - Data passed by user  * @private  */ function handlePublicMethods(action, data) {  var processed = [];  this.each(function () {   var instance = $(this).data('collapsable');   if (instance) {    var uid = instance.parent.uid;    if (processed.indexOf(uid) === -1) {     processed.push(uid);     instance.parent[action](data);    }   }  }); } /**  * Finds ext links specified in options and bind click event to them for opening  * @this Collapsable  * @todo create possibility for extLinks would only open the boxes? for now, it might be achieved using event handlers and e.preventDefault  * @private  */ function handleExtLinks() {  var opts = this.opts;  if ((this.$extLinks = $(opts.extLinks.selector).filter('a')).length) {   this.$extLinks.on('click.collapsable', function (event) {    if (opts.extLinks.preventDefault)     event.preventDefault();    var collapsable = $($(this).attr('href')).data('collapsable');    if (collapsable) {     collapsable.$controlLink.first().trigger(opts.event + '.collapsable', event);    }   });  } }   /**  * Prepare default expanded item. Checks the necessity of expanded item (collapsableAll set to false && accordion set  * to true) and limits amount of default expanded items to 1 when accordion option set to true. Also when there's  * fragment in url targeting existing collapsable item, default expanded set using class in DOM will be overridden.  *  * @summary Sets the defaultExpanded flag to appropriate CollapsableItems  * @this Collapsable  * @private  */ function prepareDefaultExpanded() {  var fragment = window.location.href;  var i = fragment.search(/#/);  var defaultExpanded = -1;  var defaultExpandedFromUrl = -1;  var $items = $($.map(this.items, function (item) {   return item.$collapsable.get();  }));  // search for #hash in url  if (i !== -1) {   fragment = fragment.substring(i + 1);   defaultExpandedFromUrl = $items.index($('#' + fragment));   if (defaultExpandedFromUrl !== -1) {    this.items[defaultExpandedFromUrl].defaultExpanded = true;    // max 1, we can return now    if (this.opts.accordion) {     return;    }   }  }  // max 1 expanded item  if (this.opts.accordion) {   defaultExpanded = $items.index($('.' + this.opts.classNames.defaultExpanded));   // max 1, we can return now   if (defaultExpanded !== -1) {    this.items[defaultExpanded].defaultExpanded = true;    return;   }  }  // not accordion, we add flag to all items with class  else {   var that = this;   $items.each(function (i) {    if ($(this).hasClass(that.opts.classNames.defaultExpanded)) {     defaultExpanded = i; // for later use it is sufficient to have last index only     that.items[i].defaultExpanded = true;    }   });  }  // if we need one and none was found yet, we take the first  if (defaultExpandedFromUrl === -1 && defaultExpanded === -1 && !this.opts.collapsableAll) {   this.items[0].defaultExpanded = true;  } } /**  * Expand or collapse item based on flags set in prepareDefaultExpanded method; called on initialization within the  * context of Collapsable  * @todo When !opts.collapseAll && opts.accordion, we now force-open the first item with defaultExpanded flag, regardless how it got it (hash in url or class); potentially force-open the one from URL instead of first, if hash set? or maybe try to open some without forcing and only if failed, do force-open (would require two passes)?  * @this Collapsable  * @private  */ function handleDefaultExpanded() {  var event = $.Event('init.collapsable');  var opts = this.opts;  var items = this.items;  // save fx so it can be set back  var fx = opts.fx;  // for initialization, we don't want to use any effect  if (opts.fx)   opts.fx = 'toggle';  var l = items.length;  var force = !opts.collapsableAll; // if we can't collapse all, we force expanding the first one chosen in prepareDefaultExpanded  for (var i = 0; i < l; i++) {   if (items[i].defaultExpanded) {    items[i].expand(event, null, force);    force = false;   } else {    items[i].collapse(event, null, true); // on init, we want to force-close all - if false returned, than the class might have been set in first place (so it would go into if statement above)   }  }  opts.fx = fx; } /**  * It is possible to set the `fx` options to `slide` or `fade` - these are shortcuts for objects with  * `{ expand: 'slideDown', collapse: 'slideUp' }` or `{ expand: 'fadeIn', collapse: 'fadeOut' }`. This function  * converts those strings into objects. It also sets default duration for these effects to 500ms.  * @this Collapsable  * @private  */ function prepareFxOpt() {  var opts = this.opts;  // default fxDuration in case of slide function  if (opts.fx === 'slide' || opts.fx === 'fade') {   opts.fxDuration = opts.fxDuration || 0;  }  // convert alias into objects  if (opts.fx === 'slide') {   opts.fx = {    expand: 'slideDown',    collapse: 'slideUp'   };  } else if (this.opts.fx === 'fade') {   opts.fx = {    expand: 'fadeIn',    collapse: 'fadeOut'   };  } } /**  * Represents set of collapsable elements which were initialized by one call with same options  * @name Collapsable  * @param {jQuery} $boxSet - Set of object to be initilized  * @param {Object} options - See plugin defaults  * @returns {Collapsable}  * @constructor  */ var Collapsable = function ($boxSet, options) {  this.opts = $.extend(true, {}, $.fn.collapsable.defaults, options);  this.items = [];  var that = this;  this.uid = getUid();  this.promiseOpen = false;  handleExtLinks.call(this);  prepareFxOpt.call(this);  $boxSet.each(function () {   var collapsable = new CollapsableItem(that, this);   if (collapsable.$box.length && collapsable.$control.length) {    that.items.push(collapsable);   }  });  prepareDefaultExpanded.call(this);  handleDefaultExpanded.call(this);  return this; }; /**  * Returns indexes of expanded items in Collapsable.items  * @returns {Array}  */ Collapsable.prototype.getExpanded = function () {  var expanded = [];  var l = this.items.length;  for (var i = 0; i < l; i++) {   if (this.items[i].isExpanded) {    expanded.push(i);   }  }  return expanded; }; /**  * Expands all collapsed items  * @param {Object} data - Data to be passed to triggered event  */ Collapsable.prototype.expandAll = function (data) {  // if accordion, we only want to expand one (first) box, or none if already expanded  if (this.opts.accordion && this.getExpanded().length) {   return;  }  var event = $.Event('expandAll.collapsable');  var l = this.items.length;  for (var i = 0; i < l; i++) {   if (! this.items[i].isExpanded) {    var expanded = this.items[i].expand(event, data);    if (this.opts.accordion && expanded) {     break;    }   }  } }; /**  * Collapses all expanded items  * @param {Object} data - Data to be passed to triggered event  */ Collapsable.prototype.collapseAll = function (data) {  var event = $.Event('collapseAll.collapsable');  var expandedItems = this.getExpanded();  var l = expandedItems.length;  for (var i = 0; i < l; i++) {   this.items[expandedItems[i]].collapse(event, data);  } }; /**  * Destroy collapsable and reverts DOM changes to state prior initialization  */ Collapsable.prototype.destroy = function () {  var opts = this.opts;  var l = this.items.length;  for (var i = 0; i < l; i++) {   var item = this.items[i];   // remove classes and event handlers from main element   item.$collapsable    .removeClass(opts.classNames.collapsed + ' ' + opts.classNames.expanded)    .removeData('collapsable');   // revert control element   item.$control.find('[data-ca-created]').each(function () {    $(this).parent().html($(this).html());   });   item.$control    .find('.ca-link').addBack('.ca-link')    .off(opts.event + '.collapsable')    .removeClass('ca-link')    .removeAttr('aria-controls')    .removeAttr('aria-expanded');   // revert box element   var style = item.$box.data('ca-pre-init-style') || '';   item.$box    .attr('style', style)    .removeData('ca-pre-init-style')    .removeAttr('aria-hidden');   item.$collapsable.trigger('destroy.collapsable');  }  // remove  this.$extLinks.off('click.collapsable'); }; /**  * Prepares DOM structure for collapsable element. Each ca-control element is tested for being anchor. If there's  * match, the element (anchor) is added class `ca-link`. If no match, we try to find anchor inside and add class  * to each of them as well. If ca-control is not an anchor and contains no anchor, we wrap inside to anchor with  * appropriate class and custom data attribute for potential destroy handle. It also stores default style attribute  * of box element for later use in destroy handle.  * @summary Prepares DOM structure for collapsable element  * @this CollapsableItem  * @private  */ function prepareCollapsableDOM() {  var collapsableItem = this;  if (! collapsableItem.id) {   collapsableItem.id = getUid();   collapsableItem.$collapsable.attr('id', this.id);  }  var boxId = collapsableItem.$box.attr('id') || collapsableItem.id + '-ca-box';  collapsableItem.$box   .attr('id', boxId)   .data('ca-pre-init-style', collapsableItem.$box.attr('style'));  collapsableItem.$control.each(function() {   var $el = $(this);   var $a;   // a.ca-control -> add class .ca-link   if ($el.is('a')) {    $a = $el;   }   // .ca-control a -> add class .ca-link   else if (($a = $el.find('a')).length) {   }   // no anchor found, create custom   else {    $el.wrapInner('<a data-ca-created href="#' + collapsableItem.id + '"></a>');    $a = $el.find('a');   }   $a.addClass('ca-link');   $a.attr('aria-controls', boxId);  }); } /**  * Single item in Collapsable object, represents one collapsable element in page  * @param {Collapsable} parent - Reference to group of Collapsable elements initialized in same time, sharing same options  * @param {jQuery} element     - One instance of collapsable element  * @returns {CollapsableItem}  * @constructor  */ var CollapsableItem = function(parent, element) {  this.parent = parent;  this.$collapsable = $(element);  this.id = this.$collapsable.attr('id');  this.$control = this.$collapsable.find(parent.opts.control);  this.$box     = this.$collapsable.find(parent.opts.box);  if (this.$control.length == 0 || this.$box.length == 0)   return;  var that = this;  var opts = this.parent.opts; // shortcut  prepareCollapsableDOM.call(this);  this.$controlLink = this.$control.find('.ca-link').addBack('.ca-link');  // collapsableEvent contains arguments passed when trigger is called, used for passing event that triggered opening (eg. extLink click)  this.$controlLink.on(opts.event + '.collapsable', function(event, collapsableEvent) {   var passEvent = collapsableEvent ? collapsableEvent : event;   if (opts.preventDefault) {    event.preventDefault();   }   if (that.isExpanded) {    that.collapse(passEvent);   }   else {    that.expand(passEvent);   }  });  this.$collapsable.data('collapsable', this);  return this; }; /**  * Handling common parts of expanding and collapsing  * @param {String} action - Either `expand` or `collapse`  * @param {Object} data - Data passed to triggered event  * @param {Event} collapsableEvent - Event to be passed to event handlers  * @returns {boolean}  */ function handleExpandCollapse(action, collapsableEvent, data) {  var opts = this.parent.opts;  var that = this;  var trigger = 'expanded';  var addClass = opts.classNames.expanded;  var removeClass = opts.classNames.collapsed;  // capitalize first letter  if (action === 'collapse') {   trigger = 'collapsed';   addClass = opts.classNames.collapsed;   removeClass = opts.classNames.expanded;  }  var event = $.Event(trigger + '.collapsable', { customData: data, collapsableEvent: collapsableEvent });  this.isExpanded = action === 'expand';  // update extLinks  this.parent.$extLinks   .filter('[href="#' + this.id + '"]')   [action === 'expand' ? 'addClass' : 'removeClass'](opts.classNames.extLinkActive);  // aria support  this.$controlLink.attr('aria-expanded', action === 'expand');  this.$box.attr('aria-hidden', action !== 'expand');  // actually toggle the box state  if(opts.fx && typeof opts.fx === 'object') {   this.$box[opts.fx[action]](opts.fxDuration, function () {    that.$collapsable.trigger(event);   });  }  else {   if(opts.fx === 'toggle') {    this.$box[action === 'expand' ? 'show' : 'hide']();   }   setTimeout(function () {    that.$collapsable.trigger(event);   }, opts.fxDuration);  }  // update classes on collapsable element itself  this.$collapsable   .removeClass(removeClass)   .addClass(addClass);  return true; } /**  * Expands single CollapsableItem; could be prevented by `preventDefault` called on `expand.collapsable` event  * @param {Object} collapsableEvent  - Event passed to triggered event  * @param {Object} data - Data passed to triggered event  * @param {Boolean} force - Forcing CollapsableItem to expand regardless on onExpand return value, should be used only on initilization (force open default expanded item when collapsableAll === false)  * @returns {Boolean}     - Returns if CollapsableItem has been expanded or not  */ CollapsableItem.prototype.expand = function(collapsableEvent, data, force) {  var opts = this.parent.opts;  var expandedItem = this.parent.getExpanded(); // accordion -> max one expanded item  this.parent.promiseOpen = true; // allows us to collapse expanded item even if there might be collapseAll === false option  if (opts.accordion) {   // before expanding, we have to collapse previously opened item, if accordion element hasn't collapsed, we can't continue   if (expandedItem.length && this.parent.items[expandedItem[0]].collapse(collapsableEvent, data, force) === false) {    this.parent.promiseOpen = false;    return false;   }  }  this.parent.promiseOpen = false;  var event = $.Event('expand.collapsable', { customData: data, collapsableEvent: collapsableEvent });  this.$collapsable.trigger(event);  if (event.isDefaultPrevented() && ! force) {   // collapsableAll === false && accordion === true -> if the box has not opened, we must make sure something is opened, therefore we force-open previously opened box (opts.accordion is true means we tried to collapse something), simulating it has never closed in first place   if (! opts.collapsableAll && opts.accordion) {    this.parent.items[expandedItem[0]].expand(collapsableEvent, data, true);   }   return false;  }  return handleExpandCollapse.call(this, 'expand', collapsableEvent) }; /**  * Collapses single CollapsableItem; could be prevented by `preventDefault` called on `collapse.collapsable` event  * @param {Object} collapsableEvent  - Event passed to triggered event  * @param {Object} data - Data passed to triggered event  * @param {Boolean} force - Forcing CollapsableItem to collapse regardless on onCollapse return value  * @returns {Boolean}     - Returns if CollapsableItem has been collapsed or not  */ CollapsableItem.prototype.collapse = function(collapsableEvent, data, force) {  var opts = this.parent.opts;  // if we can't collapse all, we are not promised to open something and there is only one opened box, then we can't continue  if (! opts.collapsableAll && ! this.parent.promiseOpen && this.parent.getExpanded().length < 2) {   return false;  }  var event = $.Event('collapse.collapsable', { customData: data, collapsableEvent: collapsableEvent });  this.$collapsable.trigger(event);  if (event.isDefaultPrevented() && !force) {   return false;  }  return handleExpandCollapse.call(this, 'collapse', collapsableEvent); }; /**  * The jQuery plugin namespace.  * @external "jQuery.fn"  * @see {@link http://learn.jquery.com/plugins The jQuery Plugin Guide}  */ /**  * jQuery adapter for Collapsable object, returns elements on which it was called, so it's chainable  * @function external:"jQuery.fn".collapsable  * @param {Object} options - Options to override plugin defaults  * @returns {jQuery}  */ $.fn.collapsable = function(options) {  if (methods[options]) {   return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));  }  else if (typeof options === 'object' || !options) {   methods.init.apply(this, arguments);   return this;  }  else {   $.error('Method ' + options + ' does not exist on jQuery.collapsable');  } }; $.fn.collapsable.defaults = defaults;})(jQuery);
 
|

답변 1개

채택된 답변
+20 포인트

소스가 길어 다 보지는 못했지만 속도문제는

다음을 수정하면 될 듯 보이네요.

var defaults 객체의 

fxDuration: 0 을 1초면 1000 0.5초면 500 등 밀리세컨드로 수정하셔 시도해 보세요. 

답변을 작성하려면 로그인이 필요합니다.