foundation.tooltip.js 9.5 KB

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