foundation.tooltip.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. ;(function ($, window, document, undefined) {
  2. 'use strict';
  3. Foundation.libs.tooltip = {
  4. name : 'tooltip',
  5. version : '5.3.3',
  6. settings : {
  7. additional_inheritable_classes : [],
  8. tooltip_class : '.tooltip',
  9. append_to: 'body',
  10. touch_close_text: 'Tap To Close',
  11. disable_for_touch: false,
  12. hover_delay: 200,
  13. show_on : 'all',
  14. tip_template : function (selector, content) {
  15. return '<span data-selector="' + selector + '" class="'
  16. + Foundation.libs.tooltip.settings.tooltip_class.substring(1)
  17. + '">' + content + '<span class="nub"></span></span>';
  18. }
  19. },
  20. cache : {},
  21. init : function (scope, method, options) {
  22. Foundation.inherit(this, 'random_str');
  23. this.bindings(method, options);
  24. },
  25. should_show: function (target, tip) {
  26. var settings = $.extend({}, this.settings, this.data_options(target));
  27. if (settings.show_on === 'all') {
  28. return true;
  29. } else if (this.small() && settings.show_on === 'small') {
  30. return true;
  31. } else if (this.medium() && settings.show_on === 'medium') {
  32. return true;
  33. } else if (this.large() && settings.show_on === 'large') {
  34. return true;
  35. }
  36. return false;
  37. },
  38. medium : function () {
  39. return matchMedia(Foundation.media_queries['medium']).matches;
  40. },
  41. large : function () {
  42. return matchMedia(Foundation.media_queries['large']).matches;
  43. },
  44. events : function (instance) {
  45. var self = this,
  46. S = self.S;
  47. self.create(this.S(instance));
  48. $(this.scope)
  49. .off('.tooltip')
  50. .on('mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip',
  51. '[' + this.attr_name() + ']', function (e) {
  52. var $this = S(this),
  53. settings = $.extend({}, self.settings, self.data_options($this)),
  54. is_touch = false;
  55. if (Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type) && S(e.target).is('a')) {
  56. return false;
  57. }
  58. if (/mouse/i.test(e.type) && self.ie_touch(e)) return false;
  59. if ($this.hasClass('open')) {
  60. if (Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) e.preventDefault();
  61. self.hide($this);
  62. } else {
  63. if (settings.disable_for_touch && Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
  64. return;
  65. } else if(!settings.disable_for_touch && Modernizr.touch && /touchstart|MSPointerDown/i.test(e.type)) {
  66. e.preventDefault();
  67. S(settings.tooltip_class + '.open').hide();
  68. is_touch = true;
  69. }
  70. if (/enter|over/i.test(e.type)) {
  71. this.timer = setTimeout(function () {
  72. var tip = self.showTip($this);
  73. }.bind(this), self.settings.hover_delay);
  74. } else if (e.type === 'mouseout' || e.type === 'mouseleave') {
  75. clearTimeout(this.timer);
  76. self.hide($this);
  77. } else {
  78. self.showTip($this);
  79. }
  80. }
  81. })
  82. .on('mouseleave.fndtn.tooltip touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip', '[' + this.attr_name() + '].open', function (e) {
  83. if (/mouse/i.test(e.type) && self.ie_touch(e)) return false;
  84. if($(this).data('tooltip-open-event-type') == 'touch' && e.type == 'mouseleave') {
  85. return;
  86. }
  87. else if($(this).data('tooltip-open-event-type') == 'mouse' && /MSPointerDown|touchstart/i.test(e.type)) {
  88. self.convert_to_touch($(this));
  89. } else {
  90. self.hide($(this));
  91. }
  92. })
  93. .on('DOMNodeRemoved DOMAttrModified', '[' + this.attr_name() + ']:not(a)', function (e) {
  94. self.hide(S(this));
  95. });
  96. },
  97. ie_touch : function (e) {
  98. // How do I distinguish between IE11 and Windows Phone 8?????
  99. return false;
  100. },
  101. showTip : function ($target) {
  102. var $tip = this.getTip($target);
  103. if (this.should_show($target, $tip)){
  104. return this.show($target);
  105. }
  106. return;
  107. },
  108. getTip : function ($target) {
  109. var selector = this.selector($target),
  110. settings = $.extend({}, this.settings, this.data_options($target)),
  111. tip = null;
  112. if (selector) {
  113. tip = this.S('span[data-selector="' + selector + '"]' + settings.tooltip_class);
  114. }
  115. return (typeof tip === 'object') ? tip : false;
  116. },
  117. selector : function ($target) {
  118. var id = $target.attr('id'),
  119. dataSelector = $target.attr(this.attr_name()) || $target.attr('data-selector');
  120. if ((id && id.length < 1 || !id) && typeof dataSelector != 'string') {
  121. dataSelector = this.random_str(6);
  122. $target.attr('data-selector', dataSelector);
  123. }
  124. return (id && id.length > 0) ? id : dataSelector;
  125. },
  126. create : function ($target) {
  127. var self = this,
  128. settings = $.extend({}, this.settings, this.data_options($target)),
  129. tip_template = this.settings.tip_template;
  130. if (typeof settings.tip_template === 'string' && window.hasOwnProperty(settings.tip_template)) {
  131. tip_template = window[settings.tip_template];
  132. }
  133. var $tip = $(tip_template(this.selector($target), $('<div></div>').html($target.attr('title')).html())),
  134. classes = this.inheritable_classes($target);
  135. $tip.addClass(classes).appendTo(settings.append_to);
  136. if (Modernizr.touch) {
  137. $tip.append('<span class="tap-to-close">'+settings.touch_close_text+'</span>');
  138. $tip.on('touchstart.fndtn.tooltip MSPointerDown.fndtn.tooltip', function(e) {
  139. self.hide($target);
  140. });
  141. }
  142. $target.removeAttr('title').attr('title','');
  143. },
  144. reposition : function (target, tip, classes) {
  145. var width, nub, nubHeight, nubWidth, column, objPos;
  146. tip.css('visibility', 'hidden').show();
  147. width = target.data('width');
  148. nub = tip.children('.nub');
  149. nubHeight = nub.outerHeight();
  150. nubWidth = nub.outerHeight();
  151. if (this.small()) {
  152. tip.css({'width' : '100%' });
  153. } else {
  154. tip.css({'width' : (width) ? width : 'auto'});
  155. }
  156. objPos = function (obj, top, right, bottom, left, width) {
  157. return obj.css({
  158. 'top' : (top) ? top : 'auto',
  159. 'bottom' : (bottom) ? bottom : 'auto',
  160. 'left' : (left) ? left : 'auto',
  161. 'right' : (right) ? right : 'auto'
  162. }).end();
  163. };
  164. objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', target.offset().left);
  165. if (this.small()) {
  166. objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', 12.5, $(this.scope).width());
  167. tip.addClass('tip-override');
  168. objPos(nub, -nubHeight, 'auto', 'auto', target.offset().left);
  169. } else {
  170. var left = target.offset().left;
  171. if (Foundation.rtl) {
  172. nub.addClass('rtl');
  173. left = target.offset().left + target.outerWidth() - tip.outerWidth();
  174. }
  175. objPos(tip, (target.offset().top + target.outerHeight() + 10), 'auto', 'auto', left);
  176. tip.removeClass('tip-override');
  177. if (classes && classes.indexOf('tip-top') > -1) {
  178. if (Foundation.rtl) nub.addClass('rtl');
  179. objPos(tip, (target.offset().top - tip.outerHeight()), 'auto', 'auto', left)
  180. .removeClass('tip-override');
  181. } else if (classes && classes.indexOf('tip-left') > -1) {
  182. objPos(tip, (target.offset().top + (target.outerHeight() / 2) - (tip.outerHeight() / 2)), 'auto', 'auto', (target.offset().left - tip.outerWidth() - nubHeight))
  183. .removeClass('tip-override');
  184. nub.removeClass('rtl');
  185. } else if (classes && classes.indexOf('tip-right') > -1) {
  186. objPos(tip, (target.offset().top + (target.outerHeight() / 2) - (tip.outerHeight() / 2)), 'auto', 'auto', (target.offset().left + target.outerWidth() + nubHeight))
  187. .removeClass('tip-override');
  188. nub.removeClass('rtl');
  189. }
  190. }
  191. tip.css('visibility', 'visible').hide();
  192. },
  193. small : function () {
  194. return matchMedia(Foundation.media_queries.small).matches &&
  195. !matchMedia(Foundation.media_queries.medium).matches;
  196. },
  197. inheritable_classes : function ($target) {
  198. var settings = $.extend({}, this.settings, this.data_options($target)),
  199. inheritables = ['tip-top', 'tip-left', 'tip-bottom', 'tip-right', 'radius', 'round'].concat(settings.additional_inheritable_classes),
  200. classes = $target.attr('class'),
  201. filtered = classes ? $.map(classes.split(' '), function (el, i) {
  202. if ($.inArray(el, inheritables) !== -1) {
  203. return el;
  204. }
  205. }).join(' ') : '';
  206. return $.trim(filtered);
  207. },
  208. convert_to_touch : function($target) {
  209. var self = this,
  210. $tip = self.getTip($target),
  211. settings = $.extend({}, self.settings, self.data_options($target));
  212. if ($tip.find('.tap-to-close').length === 0) {
  213. $tip.append('<span class="tap-to-close">'+settings.touch_close_text+'</span>');
  214. $tip.on('click.fndtn.tooltip.tapclose touchstart.fndtn.tooltip.tapclose MSPointerDown.fndtn.tooltip.tapclose', function(e) {
  215. self.hide($target);
  216. });
  217. }
  218. $target.data('tooltip-open-event-type', 'touch');
  219. },
  220. show : function ($target) {
  221. var $tip = this.getTip($target);
  222. if ($target.data('tooltip-open-event-type') == 'touch') {
  223. this.convert_to_touch($target);
  224. }
  225. this.reposition($target, $tip, $target.attr('class'));
  226. $target.addClass('open');
  227. $tip.fadeIn(150);
  228. },
  229. hide : function ($target) {
  230. var $tip = this.getTip($target);
  231. $tip.fadeOut(150, function() {
  232. $tip.find('.tap-to-close').remove();
  233. $tip.off('click.fndtn.tooltip.tapclose MSPointerDown.fndtn.tapclose');
  234. $target.removeClass('open');
  235. });
  236. },
  237. off : function () {
  238. var self = this;
  239. this.S(this.scope).off('.fndtn.tooltip');
  240. this.S(this.settings.tooltip_class).each(function (i) {
  241. $('[' + self.attr_name() + ']').eq(i).attr('title', $(this).text());
  242. }).remove();
  243. },
  244. reflow : function () {}
  245. };
  246. }(jQuery, window, window.document));