foundation.dropdown.js 9.5 KB

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