UNPKG

7.09 kBJavaScriptView Raw
1(function ($) {
2
3 // Add posibility to scroll to selected option
4 // usefull for select for example
5 $.fn.scrollTo = function(elem) {
6 $(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem).offset().top);
7 return this;
8 };
9
10 $.fn.dropdown = function (option) {
11 var defaults = {
12 inDuration: 300,
13 outDuration: 225,
14 constrain_width: true, // Constrains width of dropdown to the activator
15 hover: false,
16 gutter: 0, // Spacing from edge
17 belowOrigin: false,
18 alignment: 'left'
19 };
20
21 this.each(function(){
22 var origin = $(this);
23 var options = $.extend({}, defaults, option);
24 var isFocused = false;
25
26 // Dropdown menu
27 var activates = $("#"+ origin.attr('data-activates'));
28
29 function updateOptions() {
30 if (origin.data('induration') !== undefined)
31 options.inDuration = origin.data('inDuration');
32 if (origin.data('outduration') !== undefined)
33 options.outDuration = origin.data('outDuration');
34 if (origin.data('constrainwidth') !== undefined)
35 options.constrain_width = origin.data('constrainwidth');
36 if (origin.data('hover') !== undefined)
37 options.hover = origin.data('hover');
38 if (origin.data('gutter') !== undefined)
39 options.gutter = origin.data('gutter');
40 if (origin.data('beloworigin') !== undefined)
41 options.belowOrigin = origin.data('beloworigin');
42 if (origin.data('alignment') !== undefined)
43 options.alignment = origin.data('alignment');
44 }
45
46 updateOptions();
47
48 // Attach dropdown to its activator
49 origin.after(activates);
50
51 /*
52 Helper function to position and resize dropdown.
53 Used in hover and click handler.
54 */
55 function placeDropdown(eventType) {
56 // Check for simultaneous focus and click events.
57 if (eventType === 'focus') {
58 isFocused = true;
59 }
60
61 // Check html data attributes
62 updateOptions();
63
64 // Set Dropdown state
65 activates.addClass('active');
66 origin.addClass('active');
67
68 // Constrain width
69 if (options.constrain_width === true) {
70 activates.css('width', origin.outerWidth());
71
72 } else {
73 activates.css('white-space', 'nowrap');
74 }
75
76 // Below Origin
77 var verticalOffset = 0;
78 if (options.belowOrigin === true) {
79 verticalOffset = origin.height();
80 }
81
82 // Offscreen detection
83 var offsetLeft = origin.offset().left;
84 var offsetTop = origin.offset().top - $(window).scrollTop();
85 var currAlignment = options.alignment;
86 var activatesLeft, gutterSpacing;
87 if (offsetLeft + activates.innerWidth() > $(window).width()) {
88 // Dropdown goes past screen on right, force right alignment
89 currAlignment = 'right';
90
91 } else if (offsetLeft - activates.innerWidth() + origin.innerWidth() < 0) {
92 // Dropdown goes past screen on left, force left alignment
93 currAlignment = 'left';
94 }
95 // Vertical bottom offscreen detection
96 if (offsetTop + activates.innerHeight() > window.innerHeight) {
97 if (!verticalOffset) {
98 verticalOffset += origin.innerHeight();
99 }
100 verticalOffset -= activates.innerHeight();
101 }
102
103 // Handle edge alignment
104 if (currAlignment === 'left') {
105 gutterSpacing = options.gutter;
106 leftPosition = origin.position().left + gutterSpacing;
107 }
108 else if (currAlignment === 'right') {
109 var offsetRight = origin.position().left + origin.outerWidth() - activates.outerWidth();
110 gutterSpacing = -options.gutter;
111 leftPosition = offsetRight + gutterSpacing;
112 }
113
114 // Position dropdown
115 activates.css({
116 position: 'absolute',
117 top: origin.position().top + verticalOffset,
118 left: leftPosition
119 });
120
121
122 // Show dropdown
123 activates.stop(true, true).css('opacity', 0)
124 .slideDown({
125 queue: false,
126 duration: options.inDuration,
127 easing: 'easeOutCubic',
128 complete: function() {
129 $(this).css('height', '');
130 }
131 })
132 .animate( {opacity: 1}, {queue: false, duration: options.inDuration, easing: 'easeOutSine'});
133 }
134
135 function hideDropdown() {
136 // Check for simultaneous focus and click events.
137 isFocused = false;
138 activates.fadeOut(options.outDuration);
139 activates.removeClass('active');
140 origin.removeClass('active');
141 }
142
143 // Hover
144 if (options.hover) {
145 var open = false;
146 origin.unbind('click.' + origin.attr('id'));
147 // Hover handler to show dropdown
148 origin.on('mouseenter', function(e){ // Mouse over
149 if (open === false) {
150 placeDropdown();
151 open = true;
152 }
153 });
154 origin.on('mouseleave', function(e){
155 // If hover on origin then to something other than dropdown content, then close
156 var toEl = e.toElement || e.relatedTarget; // added browser compatibility for target element
157 if(!$(toEl).closest('.dropdown-content').is(activates)) {
158 activates.stop(true, true);
159 hideDropdown();
160 open = false;
161 }
162 });
163
164 activates.on('mouseleave', function(e){ // Mouse out
165 var toEl = e.toElement || e.relatedTarget;
166 if(!$(toEl).closest('.dropdown-button').is(origin)) {
167 activates.stop(true, true);
168 hideDropdown();
169 open = false;
170 }
171 });
172
173 // Click
174 } else {
175 // Click handler to show dropdown
176 origin.unbind('click.' + origin.attr('id'));
177 origin.bind('click.'+origin.attr('id'), function(e){
178 if (!isFocused) {
179 if ( origin[0] == e.currentTarget &&
180 !origin.hasClass('active') &&
181 ($(e.target).closest('.dropdown-content').length === 0)) {
182 e.preventDefault(); // Prevents button click from moving window
183 placeDropdown('click');
184 }
185 // If origin is clicked and menu is open, close menu
186 else if (origin.hasClass('active')) {
187 hideDropdown();
188 $(document).unbind('click.'+ activates.attr('id') + ' touchstart.' + activates.attr('id'));
189 }
190 // If menu open, add click close handler to document
191 if (activates.hasClass('active')) {
192 $(document).bind('click.'+ activates.attr('id') + ' touchstart.' + activates.attr('id'), function (e) {
193 if (!activates.is(e.target) && !origin.is(e.target) && (!origin.find(e.target).length) ) {
194 hideDropdown();
195 $(document).unbind('click.'+ activates.attr('id') + ' touchstart.' + activates.attr('id'));
196 }
197 });
198 }
199 }
200 });
201
202 } // End else
203
204 // Listen to open and close event - useful for select component
205 origin.on('open', function(e, eventType) {
206 placeDropdown(eventType);
207 });
208 origin.on('close', hideDropdown);
209
210
211 });
212 }; // End dropdown plugin
213
214 $(document).ready(function(){
215 $('.dropdown-button').dropdown();
216 });
217}( jQuery ));
\No newline at end of file