이것이 가능하고 그렇지 않습니다!
Zurb는 기능으로 멀티 앵커 드롭 다운 추구하지 않기로 결정했다 ... https://github.com/zurb/foundation-sites/pull/7732을
를 ... 그러나 (토마스 히긴스)을 앞서 제안하고 드롭 다운의 수정 된 버전을 만든 사용자 모듈 자체 :
http://codepen.io/TJHiggins/pen/JGEPLK
기초가 업데이트 될 때 덮어 쓰기가 두려워서 개인적으로 사용하지는 않았지만 Zomb stock 모듈을 식인하거나 교체하는 것이 효과적 일 수 있습니다.
(플러그인)
/**
* Dropdown module.
* @module foundation.dropdown
* @requires foundation.util.keyboard
* @requires foundation.util.box
*/
!function($, Foundation){
'use strict';
/**
* Creates a new instance of a dropdown.
* @class
* @param {jQuery} element - jQuery object to make into an accordion menu.
* @param {Object} options - Overrides to the default plugin settings.
*/
function Dropdown(element, options){
this.$element = element;
this.options = $.extend({}, Dropdown.defaults, this.$element.data(), options);
this._init();
Foundation.registerPlugin(this, 'Dropdown');
Foundation.Keyboard.register('Dropdown', {
'ENTER': 'open',
'SPACE': 'open',
'ESCAPE': 'close',
'TAB': 'tab_forward',
'SHIFT_TAB': 'tab_backward'
});
}
Dropdown.defaults = {
/**
* Amount of time to delay opening a submenu on hover event.
* @option
* @example 250
*/
hoverDelay: 250,
/**
* Allow submenus to open on hover events
* @option
* @example false
*/
hover: false,
/**
* Don't close dropdown when hovering over dropdown pane
* @option
* @example true
*/
hoverPane: false,
/**
* Number of pixels between the dropdown pane and the triggering element on open.
* @option
* @example 1
*/
vOffset: 1,
/**
* Number of pixels between the dropdown pane and the triggering element on open.
* @option
* @example 1
*/
hOffset: 1,
/**
* Class applied to adjust open position. JS will test and fill this in.
* @option
* @example 'top'
*/
positionClass: '',
/**
* Allow the plugin to trap focus to the dropdown pane if opened with keyboard commands.
* @option
* @example false
*/
trapFocus: false,
/**
* Allow the plugin to set focus to the first focusable element within the pane, regardless of method of opening.
* @option
* @example true
*/
autoFocus: false,
/**
* Allows a click on the body to close the dropdown.
* @option
* @example true
*/
closeOnClick: false
};
/**
* Initializes the plugin by setting/checking options and attributes, adding helper variables, and saving the anchor.
* @function
* @private
*/
Dropdown.prototype._init = function(){
var $id = this.$element.attr('id');
this.$anchors = $('[data-toggle="' + $id + '"]') || $('[data-open="' + $id + '"]');
this.$anchors.attr({
'aria-controls': $id,
'data-is-focus': false,
'data-yeti-box': $id,
'aria-haspopup': true,
'aria-expanded': false
// 'data-resize': $id
});
this._setCurrentAnchor(this.$anchors.first());
this.options.positionClass = this.getPositionClass();
this.counter = 4;
this.usedPositions = [];
this.$element.attr({
'aria-hidden': 'true',
'data-yeti-box': $id,
'data-resize': $id
});
this._events();
};
/**
* Helper function to set the current anchor
* @function
* @private
*/
Dropdown.prototype._setCurrentAnchor = function($anchor){
this.$current_anchor = $anchor;
this.$element.attr({
'aria-labelledby': this.$current_anchor.attr('id') || Foundation.GetYoDigits(6, 'dd-anchor')
});
};
/**
* Helper function to determine current orientation of dropdown pane.
* @function
* @returns {String} position - string value of a position class.
*/
Dropdown.prototype.getPositionClass = function(){
var position = this.$element[0].className.match(/(top|left|right)/g);
position = position ? position[0] : '';
return position;
};
/**
* Adjusts the dropdown panes orientation by adding/removing positioning classes.
* @function
* @private
* @param {String} position - position class to remove.
*/
Dropdown.prototype._reposition = function(position){
this.usedPositions.push(position ? position : 'bottom');
//default, try switching to opposite side
if(!position && (this.usedPositions.indexOf('top') < 0)){
this.$element.addClass('top');
}else if(position === 'top' && (this.usedPositions.indexOf('bottom') < 0)){
this.$element.removeClass(position);
}else if(position === 'left' && (this.usedPositions.indexOf('right') < 0)){
this.$element.removeClass(position)
.addClass('right');
}else if(position === 'right' && (this.usedPositions.indexOf('left') < 0)){
this.$element.removeClass(position)
.addClass('left');
}
//if default change didn't work, try bottom or left first
else if(!position && (this.usedPositions.indexOf('top') > -1) && (this.usedPositions.indexOf('left') < 0)){
this.$element.addClass('left');
}else if(position === 'top' && (this.usedPositions.indexOf('bottom') > -1) && (this.usedPositions.indexOf('left') < 0)){
this.$element.removeClass(position)
.addClass('left');
}else if(position === 'left' && (this.usedPositions.indexOf('right') > -1) && (this.usedPositions.indexOf('bottom') < 0)){
this.$element.removeClass(position);
}else if(position === 'right' && (this.usedPositions.indexOf('left') > -1) && (this.usedPositions.indexOf('bottom') < 0)){
this.$element.removeClass(position);
}
//if nothing cleared, set to bottom
else{
this.$element.removeClass(position);
}
this.classChanged = true;
this.counter--;
};
/**
* Sets the position and orientation of the dropdown pane, checks for collisions.
* Recursively calls itself if a collision is detected, with a new position class.
* @function
* @private
*/
Dropdown.prototype._setPosition = function(){
if(this.$current_anchor.attr('aria-expanded') === 'false'){ return false; }
var position = this.getPositionClass(),
$eleDims = Foundation.Box.GetDimensions(this.$element),
$anchorDims = Foundation.Box.GetDimensions(this.$current_anchor),
_this = this,
direction = (position === 'left' ? 'left' : ((position === 'right') ? 'left' : 'top')),
param = (direction === 'top') ? 'height' : 'width',
offset = (param === 'height') ? this.options.vOffset : this.options.hOffset;
if(($eleDims.width >= $eleDims.windowDims.width) || (!this.counter && !Foundation.Box.ImNotTouchingYou(this.$element))){
this.$element.offset(Foundation.Box.GetOffsets(this.$element, this.$current_anchor, 'center bottom', this.options.vOffset, this.options.hOffset, true)).css({
'width': $eleDims.windowDims.width - (this.options.hOffset * 2),
'height': 'auto'
});
this.classChanged = true;
return false;
}
this.$element.offset(Foundation.Box.GetOffsets(this.$element, this.$current_anchor, position, this.options.vOffset, this.options.hOffset));
while(!Foundation.Box.ImNotTouchingYou(this.$element) && this.counter){
this._reposition(position);
this._setPosition();
}
};
/**
* Adds event listeners to the element utilizing the triggers utility library.
* @function
* @private
*/
Dropdown.prototype._events = function(){
var _this = this;
this.$element.on({
'open.zf.trigger': this.open.bind(this),
'close.zf.trigger': this.close.bind(this),
'toggle.zf.trigger': this.toggle.bind(this),
'resizeme.zf.trigger': this._setPosition.bind(this)
});
if(this.options.hover){
this.$anchors.off('mouseenter.zf.dropdown mouseleave.zf.dropdown')
.on('mouseenter.zf.dropdown', function(event){
clearTimeout(_this.timeout);
_this.timeout = setTimeout(function(){
_this.open(event, [this]);
$(this).data('hover', true);
}.bind(this), _this.options.hoverDelay);
}).on('mouseleave.zf.dropdown', function(){
clearTimeout(_this.timeout);
_this.timeout = setTimeout(function(){
_this.close();
_this.$anchors.data('hover', false);
}, _this.options.hoverDelay);
});
if(this.options.hoverPane){
this.$element.off('mouseenter.zf.dropdown mouseleave.zf.dropdown')
.on('mouseenter.zf.dropdown', function(){
clearTimeout(_this.timeout);
}).on('mouseleave.zf.dropdown', function(){
clearTimeout(_this.timeout);
_this.timeout = setTimeout(function(){
_this.close();
_this.$anchors.data('hover', false);
}, _this.options.hoverDelay);
});
}
}
this.$anchors.add(this.$element).on('keydown.zf.dropdown', function(e) {
var $target = $(this),
visibleFocusableElements = Foundation.Keyboard.findFocusable(_this.$element);
Foundation.Keyboard.handleKey(e, 'Dropdown', {
tab_forward: function() {
if (_this.$element.find(':focus').is(visibleFocusableElements.eq(-1))) { // left modal downwards, setting focus to first element
if (_this.options.trapFocus) { // if focus shall be trapped
visibleFocusableElements.eq(0).focus();
e.preventDefault();
} else { // if focus is not trapped, close dropdown on focus out
_this.close();
}
}
},
tab_backward: function() {
if (_this.$element.find(':focus').is(visibleFocusableElements.eq(0)) || _this.$element.is(':focus')) { // left modal upwards, setting focus to last element
if (_this.options.trapFocus) { // if focus shall be trapped
visibleFocusableElements.eq(-1).focus();
e.preventDefault();
} else { // if focus is not trapped, close dropdown on focus out
_this.close();
}
}
},
open: function() {
if ($target.is(_this.$anchors)) {
_this.open();
_this.$element.attr('tabindex', -1).focus();
e.preventDefault();
}
},
close: function() {
_this.close();
_this.$anchors.focus();
}
});
});
};
/**
* Adds an event handler to the body to close any dropdowns on a click.
* @function
* @private
*/
Dropdown.prototype._addBodyHandler = function(){
var $body = $(document.body).not(this.$element),
_this = this;
$body.off('click.zf.dropdown')
.on('click.zf.dropdown', function(e){
if(_this.$current_anchor.is(e.target) || _this.$current_anchor.find(e.target).length) {
return;
}
if(_this.$element.find(e.target).length) {
return;
}
_this.close();
$body.off('click.zf.dropdown');
});
};
/**
* Opens the dropdown pane, and fires a bubbling event to close other dropdowns.
* @function
* @fires Dropdown#closeme
* @fires Dropdown#show
*/
Dropdown.prototype.open = function(event, togglers){
if (togglers) { this._setCurrentAnchor($(togglers).first()); }
// var _this = this;
/**
* Fires to close other open dropdowns
* @event Dropdown#closeme
*/
this.$element.trigger('closeme.zf.dropdown', this.$element.attr('id'));
this.$current_anchor.addClass('hover')
.attr({'aria-expanded': true});
// this.$element/*.show()*/;
this._setPosition();
this.$element.addClass('is-open')
.attr({'aria-hidden': false});
if(this.options.autoFocus){
var $focusable = Foundation.Keyboard.findFocusable(this.$element);
if($focusable.length){
$focusable.eq(0).focus();
}
}
if(this.options.closeOnClick){ this._addBodyHandler(); }
/**
* Fires once the dropdown is visible.
* @event Dropdown#show
*/
this.$element.trigger('show.zf.dropdown', [this.$element]);
//why does this not work correctly for this plugin?
// Foundation.reflow(this.$element, 'dropdown');
// Foundation._reflow(this.$element.attr('data-dropdown'));
};
/**
* Closes the open dropdown pane.
* @function
* @fires Dropdown#hide
*/
Dropdown.prototype.close = function(){
if(!this.$element.hasClass('is-open')){
return false;
}
this.$element.removeClass('is-open')
.attr({'aria-hidden': true});
this.$anchors.removeClass('hover')
.attr('aria-expanded', false);
if(this.classChanged){
var curPositionClass = this.getPositionClass();
if(curPositionClass){
this.$element.removeClass(curPositionClass);
}
this.$element.addClass(this.options.positionClass)
/*.hide()*/.css({height: '', width: ''});
this.classChanged = false;
this.counter = 4;
this.usedPositions.length = 0;
}
this.$element.trigger('hide.zf.dropdown', [this.$element]);
// Foundation.reflow(this.$element, 'dropdown');
};
/**
* Toggles the dropdown pane's visibility.
* @function
*/
Dropdown.prototype.toggle = function(event, togglers){
if(this.$element.hasClass('is-open')){
if(this.$current_anchor.data('hover')) return;
this.close();
}else{
this.open(event, togglers);
}
};
/**
* Destroys the dropdown.
* @function
*/
Dropdown.prototype.destroy = function(){
this.$element.off('.zf.trigger').hide();
this.$anchors.off('.zf.dropdown');
Foundation.unregisterPlugin(this);
};
Foundation.plugin(Dropdown, 'Dropdown');
}(jQuery, window.Foundation);
편집 : - 키보드 입력 하나되는 나는 또한 저자가 언급 알려진 버그가 있다는 것을 말해야한다.