foundation.dropdown.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. ;(function ($, window, document, undefined) {
  2. 'use strict';
  3. Foundation.libs.dropdown = {
  4. name : 'dropdown',
  5. version : '5.3.3',
  6. settings : {
  7. active_class: 'open',
  8. align: 'bottom',
  9. is_hover: false,
  10. opened: function(){},
  11. closed: function(){}
  12. },
  13. init : function (scope, method, options) {
  14. Foundation.inherit(this, 'throttle');
  15. this.bindings(method, options);
  16. },
  17. events : function (scope) {
  18. var self = this,
  19. S = self.S;
  20. S(this.scope)
  21. .off('.dropdown')
  22. .on('click.fndtn.dropdown', '[' + this.attr_name() + ']', function (e) {
  23. var settings = S(this).data(self.attr_name(true) + '-init') || self.settings;
  24. if (!settings.is_hover || Modernizr.touch) {
  25. e.preventDefault();
  26. self.toggle($(this));
  27. }
  28. })
  29. .on('mouseenter.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
  30. var $this = S(this),
  31. dropdown,
  32. target;
  33. clearTimeout(self.timeout);
  34. if ($this.data(self.data_attr())) {
  35. dropdown = S('#' + $this.data(self.data_attr()));
  36. target = $this;
  37. } else {
  38. dropdown = $this;
  39. target = S("[" + self.attr_name() + "='" + dropdown.attr('id') + "']");
  40. }
  41. var settings = target.data(self.attr_name(true) + '-init') || self.settings;
  42. if(S(e.target).data(self.data_attr()) && settings.is_hover) {
  43. self.closeall.call(self);
  44. }
  45. if (settings.is_hover) self.open.apply(self, [dropdown, target]);
  46. })
  47. .on('mouseleave.fndtn.dropdown', '[' + this.attr_name() + '], [' + this.attr_name() + '-content]', function (e) {
  48. var $this = S(this);
  49. self.timeout = setTimeout(function () {
  50. if ($this.data(self.data_attr())) {
  51. var settings = $this.data(self.data_attr(true) + '-init') || self.settings;
  52. if (settings.is_hover) self.close.call(self, S('#' + $this.data(self.data_attr())));
  53. } else {
  54. var target = S('[' + self.attr_name() + '="' + S(this).attr('id') + '"]'),
  55. settings = target.data(self.attr_name(true) + '-init') || self.settings;
  56. if (settings.is_hover) self.close.call(self, $this);
  57. }
  58. }.bind(this), 150);
  59. })
  60. .on('click.fndtn.dropdown', function (e) {
  61. var parent = S(e.target).closest('[' + self.attr_name() + '-content]');
  62. if (S(e.target).closest('[' + self.attr_name() + ']').length > 0) {
  63. return;
  64. }
  65. if (!(S(e.target).data('revealId')) &&
  66. (parent.length > 0 && (S(e.target).is('[' + self.attr_name() + '-content]') ||
  67. $.contains(parent.first()[0], e.target)))) {
  68. e.stopPropagation();
  69. return;
  70. }
  71. self.close.call(self, S('[' + self.attr_name() + '-content]'));
  72. })
  73. .on('opened.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
  74. self.settings.opened.call(this);
  75. })
  76. .on('closed.fndtn.dropdown', '[' + self.attr_name() + '-content]', function () {
  77. self.settings.closed.call(this);
  78. });
  79. S(window)
  80. .off('.dropdown')
  81. .on('resize.fndtn.dropdown', self.throttle(function () {
  82. self.resize.call(self);
  83. }, 50));
  84. this.resize();
  85. },
  86. close: function (dropdown) {
  87. var self = this;
  88. dropdown.each(function () {
  89. if (self.S(this).hasClass(self.settings.active_class)) {
  90. self.S(this)
  91. .css(Foundation.rtl ? 'right':'left', '-99999px')
  92. .removeClass(self.settings.active_class)
  93. .prev('[' + self.attr_name() + ']')
  94. .removeClass(self.settings.active_class)
  95. .removeData('target');
  96. self.S(this).trigger('closed').trigger('closed.fndtn.dropdown', [dropdown]);
  97. }
  98. });
  99. },
  100. closeall: function() {
  101. var self = this;
  102. $.each(self.S('[' + this.attr_name() + '-content]'), function() {
  103. self.close.call(self, self.S(this))
  104. });
  105. },
  106. open: function (dropdown, target) {
  107. this
  108. .css(dropdown
  109. .addClass(this.settings.active_class), target);
  110. dropdown.prev('[' + this.attr_name() + ']').addClass(this.settings.active_class);
  111. dropdown.data('target', target.get(0)).trigger('opened').trigger('opened.fndtn.dropdown', [dropdown, target]);
  112. },
  113. data_attr: function () {
  114. if (this.namespace.length > 0) {
  115. return this.namespace + '-' + this.name;
  116. }
  117. return this.name;
  118. },
  119. toggle : function (target) {
  120. var dropdown = this.S('#' + target.data(this.data_attr()));
  121. if (dropdown.length === 0) {
  122. // No dropdown found, not continuing
  123. return;
  124. }
  125. this.close.call(this, this.S('[' + this.attr_name() + '-content]').not(dropdown));
  126. if (dropdown.hasClass(this.settings.active_class)) {
  127. this.close.call(this, dropdown);
  128. if (dropdown.data('target') !== target.get(0))
  129. this.open.call(this, dropdown, target);
  130. } else {
  131. this.open.call(this, dropdown, target);
  132. }
  133. },
  134. resize : function () {
  135. var dropdown = this.S('[' + this.attr_name() + '-content].open'),
  136. target = this.S("[" + this.attr_name() + "='" + dropdown.attr('id') + "']");
  137. if (dropdown.length && target.length) {
  138. this.css(dropdown, target);
  139. }
  140. },
  141. css : function (dropdown, target) {
  142. var left_offset = Math.max((target.width() - dropdown.width()) / 2, 8);
  143. this.clear_idx();
  144. if (this.small()) {
  145. var p = this.dirs.bottom.call(dropdown, target);
  146. dropdown.attr('style', '').removeClass('drop-left drop-right drop-top').css({
  147. position : 'absolute',
  148. width: '95%',
  149. 'max-width': 'none',
  150. top: p.top
  151. });
  152. dropdown.css(Foundation.rtl ? 'right':'left', left_offset);
  153. } else {
  154. var settings = target.data(this.attr_name(true) + '-init') || this.settings;
  155. this.style(dropdown, target, settings);
  156. }
  157. return dropdown;
  158. },
  159. style : function (dropdown, target, settings) {
  160. var css = $.extend({position: 'absolute'},
  161. this.dirs[settings.align].call(dropdown, target, settings));
  162. dropdown.attr('style', '').css(css);
  163. },
  164. // return CSS property object
  165. // `this` is the dropdown
  166. dirs : {
  167. // Calculate target offset
  168. _base : function (t) {
  169. var o_p = this.offsetParent(),
  170. o = o_p.offset(),
  171. p = t.offset();
  172. p.top -= o.top;
  173. p.left -= o.left;
  174. return p;
  175. },
  176. top: function (t, s) {
  177. var self = Foundation.libs.dropdown,
  178. p = self.dirs._base.call(this, t),
  179. pip_offset_base = 8;
  180. this.addClass('drop-top');
  181. if (t.outerWidth() < this.outerWidth() || self.small()) {
  182. self.adjust_pip(pip_offset_base, p);
  183. }
  184. if (Foundation.rtl) {
  185. return {left: p.left - this.outerWidth() + t.outerWidth(),
  186. top: p.top - this.outerHeight()};
  187. }
  188. return {left: p.left, top: p.top - this.outerHeight()};
  189. },
  190. bottom: function (t, s) {
  191. var self = Foundation.libs.dropdown,
  192. p = self.dirs._base.call(this, t),
  193. pip_offset_base = 8;
  194. if (t.outerWidth() < this.outerWidth() || self.small()) {
  195. self.adjust_pip(pip_offset_base, p);
  196. }
  197. if (self.rtl) {
  198. return {left: p.left - this.outerWidth() + t.outerWidth(), top: p.top + t.outerHeight()};
  199. }
  200. return {left: p.left, top: p.top + t.outerHeight()};
  201. },
  202. left: function (t, s) {
  203. var p = Foundation.libs.dropdown.dirs._base.call(this, t);
  204. this.addClass('drop-left');
  205. return {left: p.left - this.outerWidth(), top: p.top};
  206. },
  207. right: function (t, s) {
  208. var p = Foundation.libs.dropdown.dirs._base.call(this, t);
  209. this.addClass('drop-right');
  210. return {left: p.left + t.outerWidth(), top: p.top};
  211. }
  212. },
  213. // Insert rule to style psuedo elements
  214. adjust_pip : function (pip_offset_base, p) {
  215. var sheet = Foundation.stylesheet;
  216. if (this.small()) {
  217. pip_offset_base += p.left - 8;
  218. }
  219. this.rule_idx = sheet.cssRules.length;
  220. var sel_before = '.f-dropdown.open:before',
  221. sel_after = '.f-dropdown.open:after',
  222. css_before = 'left: ' + pip_offset_base + 'px;',
  223. css_after = 'left: ' + (pip_offset_base - 1) + 'px;';
  224. if (sheet.insertRule) {
  225. sheet.insertRule([sel_before, '{', css_before, '}'].join(' '), this.rule_idx);
  226. sheet.insertRule([sel_after, '{', css_after, '}'].join(' '), this.rule_idx + 1);
  227. } else {
  228. sheet.addRule(sel_before, css_before, this.rule_idx);
  229. sheet.addRule(sel_after, css_after, this.rule_idx + 1);
  230. }
  231. },
  232. // Remove old dropdown rule index
  233. clear_idx : function () {
  234. var sheet = Foundation.stylesheet;
  235. if (this.rule_idx) {
  236. sheet.deleteRule(this.rule_idx);
  237. sheet.deleteRule(this.rule_idx);
  238. delete this.rule_idx;
  239. }
  240. },
  241. small : function () {
  242. return matchMedia(Foundation.media_queries.small).matches &&
  243. !matchMedia(Foundation.media_queries.medium).matches;
  244. },
  245. off: function () {
  246. this.S(this.scope).off('.fndtn.dropdown');
  247. this.S('html, body').off('.fndtn.dropdown');
  248. this.S(window).off('.fndtn.dropdown');
  249. this.S('[data-dropdown-content]').off('.fndtn.dropdown');
  250. },
  251. reflow : function () {}
  252. };
  253. }(jQuery, window, window.document));