mdbootstrap4-pro
Version:
MDBootstrap 4 PRO
330 lines (296 loc) • 11.4 kB
JavaScript
;
/* DROPDOWN */
(function ($) {
// Add posibility to scroll to selected option
// usefull for select for example
$.fn.scrollTo = function (elem) {
$(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem).offset().top);
return this;
};
$.fn.dropdown = function (option) {
var defaults = {
inDuration: 300,
outDuration: 225,
constrain_width: true, // Constrains width of dropdown to the activator
hover: false,
gutter: 0, // Spacing from edge
belowOrigin: false,
alignment: 'left'
};
this.each(function () {
var origin = $(this);
var options = $.extend({}, defaults, option);
var isFocused = false;
// Dropdown menu
var activates = $('#' + origin.attr('data-activates'));
function updateOptions() {
if (origin.data('induration') !== undefined) {
options.inDuration = origin.data('inDuration');
}
if (origin.data('outduration') !== undefined) {
options.outDuration = origin.data('outDuration');
}
if (origin.data('constrainwidth') !== undefined) {
options.constrain_width = origin.data('constrainwidth');
}
if (origin.data('hover') !== undefined) {
options.hover = origin.data('hover');
}
if (origin.data('gutter') !== undefined) {
options.gutter = origin.data('gutter');
}
if (origin.data('beloworigin') !== undefined) {
options.belowOrigin = origin.data('beloworigin');
}
if (origin.data('alignment') !== undefined) {
options.alignment = origin.data('alignment');
}
}
updateOptions();
// Attach dropdown to its activator
origin.after(activates);
/*
Helper function to position and resize dropdown.
Used in hover and click handler.
*/
function placeDropdown(eventType) {
// Check for simultaneous focus and click events.
if (eventType === 'focus') {
isFocused = true;
}
// Check html data attributes
updateOptions();
// Set Dropdown state
activates.addClass('active');
origin.addClass('active');
// Constrain width
if (options.constrain_width === true) {
activates.css('width', origin.outerWidth());
} else {
activates.css('white-space', 'nowrap');
}
// Offscreen detection
var windowHeight = window.innerHeight;
var originHeight = origin.innerHeight();
var offsetLeft = origin.offset().left;
var offsetTop = origin.offset().top - $(window).scrollTop();
var currAlignment = options.alignment;
var gutterSpacing = 0;
var leftPosition = 0;
// Below Origin
var verticalOffset = 0;
if (options.belowOrigin === true) {
verticalOffset = originHeight;
}
// Check for scrolling positioned container.
var scrollOffset = 0;
var wrapper = origin.parent();
if (!wrapper.is('body') && wrapper[0].scrollHeight > wrapper[0].clientHeight) {
scrollOffset = wrapper[0].scrollTop;
}
if (offsetLeft + activates.innerWidth() > $(window).width()) {
// Dropdown goes past screen on right, force right alignment
currAlignment = 'right';
} else if (offsetLeft - activates.innerWidth() + origin.innerWidth() < 0) {
// Dropdown goes past screen on left, force left alignment
currAlignment = 'left';
}
// Vertical bottom offscreen detection
if (offsetTop + activates.innerHeight() > windowHeight) {
// If going upwards still goes offscreen, just crop height of dropdown.
if (offsetTop + originHeight - activates.innerHeight() < 0) {
var adjustedHeight = windowHeight - offsetTop - verticalOffset;
activates.css('max-height', adjustedHeight);
} else {
// Flow upwards.
if (!verticalOffset) {
verticalOffset += originHeight;
}
verticalOffset -= activates.innerHeight();
}
}
// Handle edge alignment
if (currAlignment === 'left') {
gutterSpacing = options.gutter;
leftPosition = origin.position().left + gutterSpacing;
} else if (currAlignment === 'right') {
var offsetRight = origin.position().left + origin.outerWidth() - activates.outerWidth();
gutterSpacing = -options.gutter;
leftPosition = offsetRight + gutterSpacing;
}
// Position dropdown
activates.css({
position: 'absolute',
top: origin.position().top + verticalOffset + scrollOffset,
left: leftPosition
});
// Show dropdown
activates.stop(true, true).css('opacity', 0).slideDown({
queue: false,
duration: options.inDuration,
easing: 'easeOutCubic',
complete: function complete() {
$(this).css('height', '');
}
}).animate({
opacity: 1,
scrollTop: 0
}, {
queue: false,
duration: options.inDuration,
easing: 'easeOutSine'
});
}
function hideDropdown() {
// Check for simultaneous focus and click events.
isFocused = false;
activates.fadeOut(options.outDuration);
activates.removeClass('active');
origin.removeClass('active');
setTimeout(function () {
activates.css('max-height', '');
}, options.outDuration);
}
// Hover
if (options.hover) {
var open = false;
origin.unbind('click.' + origin.attr('id'));
// Hover handler to show dropdown
origin.on('mouseenter', function () {
// Mouse over
if (open === false) {
placeDropdown();
open = true;
}
});
origin.on('mouseleave', function (e) {
// If hover on origin then to something other than dropdown content, then close
var toEl = e.toElement || e.relatedTarget; // added browser compatibility for target element
if (!$(toEl).closest('.dropdown-content').is(activates)) {
activates.stop(true, true);
hideDropdown();
open = false;
}
});
activates.on('mouseleave', function (e) {
// Mouse out
var toEl = e.toElement || e.relatedTarget;
if (!$(toEl).closest('.dropdown-button').is(origin)) {
activates.stop(true, true);
hideDropdown();
open = false;
}
});
// Click
} else {
// Click handler to show dropdown
origin.unbind('click.' + origin.attr('id'));
origin.bind('click.' + origin.attr('id'), function (e) {
if (!isFocused) {
if (origin[0] === e.currentTarget && !origin.hasClass('active') && $(e.target).closest('.dropdown-content').length === 0) {
e.preventDefault(); // Prevents button click from moving window
placeDropdown('click');
}
// If origin is clicked and menu is open, close menu
else if (origin.hasClass('active')) {
hideDropdown();
$(document).unbind('click.' + activates.attr('id') + ' touchstart.' + activates.attr('id'));
}
// If menu open, add click close handler to document
if (activates.hasClass('active')) {
$(document).bind('click.' + activates.attr('id') + ' touchstart.' + activates.attr('id'), function (e) {
if (!activates.is(e.target) && !origin.is(e.target) && !origin.find(e.target).length) {
hideDropdown();
$(document).unbind('click.' + activates.attr('id') + ' touchstart.' + activates.attr('id'));
}
});
}
}
});
} // End else
// Listen to open and close event - useful for select component
origin.on('open', function (e, eventType) {
placeDropdown(eventType);
});
origin.on('close', hideDropdown);
});
}; // End dropdown plugin
$(document).ready(function () {
$('.dropdown-button').dropdown();
});
})(jQuery);
var dropdownSelectors = $('.dropdown, .dropup');
// Custom function to read dropdown data
// =========================
function dropdownEffectData(target) {
// @todo - page level global?
var effectInDefault = 'fadeIn';
var effectOutDefault = 'fadeOut';
var dropdown = $(target);
var dropdownMenu = $('.dropdown-menu', target);
var parentUl = dropdown.parents('ul.nav');
// If parent is ul.nav allow global effect settings
if (parentUl.height > 0) {
effectInDefault = parentUl.data('dropdown-in') || null;
effectOutDefault = parentUl.data('dropdown-out') || null;
}
return {
target: target,
dropdown: dropdown,
dropdownMenu: dropdownMenu,
effectIn: dropdownMenu.data('dropdown-in') || effectInDefault,
effectOut: dropdownMenu.data('dropdown-out') || effectOutDefault
};
}
// Custom function to start effect (in or out)
// =========================
function dropdownEffectStart(data, effectToStart) {
if (effectToStart) {
data.dropdown.addClass('dropdown-animating');
data.dropdownMenu.addClass('animated');
data.dropdownMenu.addClass(effectToStart);
}
}
// Custom function to read when animation is over
// =========================
function dropdownEffectEnd(data, callbackFunc) {
var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend';
data.dropdown.one(animationEnd, function () {
data.dropdown.removeClass('dropdown-animating');
data.dropdownMenu.removeClass('animated');
data.dropdownMenu.removeClass(data.effectIn);
data.dropdownMenu.removeClass(data.effectOut);
// Custom callback option, used to remove open class in out effect
if (typeof callbackFunc === 'function') {
callbackFunc();
}
});
}
// Bootstrap API hooks
// =========================
dropdownSelectors.on({
'show.bs.dropdown': function showBsDropdown() {
// On show, start in effect
var dropdown = dropdownEffectData(this);
dropdownEffectStart(dropdown, dropdown.effectIn);
},
'shown.bs.dropdown': function shownBsDropdown() {
// On shown, remove in effect once complete
var dropdown = dropdownEffectData(this);
if (dropdown.effectIn && dropdown.effectOut) {
dropdownEffectEnd(dropdown);
}
},
'hide.bs.dropdown': function hideBsDropdown(e) {
// On hide, start out effect
var dropdown = dropdownEffectData(this);
if (dropdown.effectOut) {
e.preventDefault();
dropdownEffectStart(dropdown, dropdown.effectOut);
dropdownEffectEnd(dropdown, function () {
dropdown.dropdown.removeClass('show');
dropdown.dropdownMenu.removeClass('show');
});
}
}
});