hovertip.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. /**
  2. * Hovertip - easy and elegant tooltips
  3. *
  4. * By Dave Cohen <http://dave-cohen.com>
  5. * With ideas and and javascript code borrowed from many folks.
  6. * (See URLS in the comments)
  7. *
  8. * Licensed under GPL.
  9. * Requires jQuery.js. <http://jquery.com>,
  10. * which may be distributed under a different licence.
  11. *
  12. * $Date: 2006-09-15 12:49:19 -0700 (Fri, 15 Sep 2006) $
  13. * $Rev: $
  14. * $Id:$
  15. *
  16. * This plugin helps you create tooltips. It supports:
  17. *
  18. * hovertips - these appear under the mouse when mouse is over the target
  19. * element.
  20. *
  21. * clicktips - these appear in the document when the target element is
  22. * clicked.
  23. *
  24. * You may define behaviors for additional types of tooltips.
  25. *
  26. * There are a variety of ways to add tooltips. Each of the following is
  27. * supported:
  28. *
  29. * <p>blah blah blah
  30. * <span>important term</span>
  31. * <span class="tooltip">text that appears.</span>
  32. * blah blah blah</p>
  33. *
  34. * or,
  35. *
  36. * <p>blah blah blah
  37. * <span hovertip="termdefinition">important term</span>
  38. * blah blah blah</p>
  39. * <div id="termdefinition" class="hovertip"><h1>term definition</h1><p>the term means...</p></div>
  40. *
  41. * or,
  42. *
  43. * <p>blah blah blah
  44. * <span id="term">important term</span>
  45. * blah blah blah</p>
  46. * <div target="term" class="hovertip"><h1>term definition</h1><p>the term means...</p></div>
  47. *
  48. *
  49. * Hooks are available to customize both the behavior of activated tooltips,
  50. * and the syntax used to mark them up.
  51. *
  52. */
  53. //// mouse events ////
  54. /**
  55. * To make hovertips appear correctly we need the exact mouse position.
  56. * These functions make that possible.
  57. */
  58. // use globals to track mouse position
  59. var hovertipMouseX;
  60. var hovertipMouseY;
  61. function hovertipMouseUpdate(e) {
  62. var mouse = hovertipMouseXY(e);
  63. hovertipMouseX = mouse[0];
  64. hovertipMouseY = mouse[1];
  65. }
  66. // http://www.howtocreate.co.uk/tutorials/javascript/eventinfo
  67. function hovertipMouseXY(e) {
  68. if( !e ) {
  69. if( window.event ) {
  70. //Internet Explorer
  71. e = window.event;
  72. } else {
  73. //total failure, we have no way of referencing the event
  74. return;
  75. }
  76. }
  77. if( typeof( e.pageX ) == 'number' ) {
  78. //most browsers
  79. var xcoord = e.pageX;
  80. var ycoord = e.pageY;
  81. } else if( typeof( e.clientX ) == 'number' ) {
  82. //Internet Explorer and older browsers
  83. //other browsers provide this, but follow the pageX/Y branch
  84. var xcoord = e.clientX;
  85. var ycoord = e.clientY;
  86. var badOldBrowser = ( window.navigator.userAgent.indexOf( 'Opera' ) + 1 ) ||
  87. ( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 ) ||
  88. ( navigator.vendor == 'KDE' );
  89. if( !badOldBrowser ) {
  90. if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
  91. //IE 4, 5 & 6 (in non-standards compliant mode)
  92. xcoord += document.body.scrollLeft;
  93. ycoord += document.body.scrollTop;
  94. } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
  95. //IE 6 (in standards compliant mode)
  96. xcoord += document.documentElement.scrollLeft;
  97. ycoord += document.documentElement.scrollTop;
  98. }
  99. }
  100. } else {
  101. //total failure, we have no way of obtaining the mouse coordinates
  102. return;
  103. }
  104. return [xcoord, ycoord];
  105. }
  106. //// target selectors ////
  107. /**
  108. * These selectors find the targets for a given tooltip element.
  109. * Several methods are supported.
  110. *
  111. * You may write your own selector functions to customize.
  112. */
  113. /**
  114. * For this model:
  115. * <span hovertip="ht1">target term</span>...
  116. * <div class="hovertip" id="ht1">tooltip text</div>
  117. */
  118. targetSelectById = function(el, config) {
  119. var id;
  120. var selector;
  121. if (id = el.getAttribute('id')) {
  122. selector = '*[@'+config.attribute+'=\''+id+'\']';
  123. return $(selector);
  124. }
  125. };
  126. /**
  127. * For this model:
  128. * <span id="ht1">target term</span>...
  129. * <div class="hovertip" target="ht1">tooltip text</div>
  130. */
  131. targetSelectByTargetAttribute = function(el, config) {
  132. target_list = el.getAttribute('target');
  133. if (target_list) {
  134. // use for attribute to specify targets
  135. target_ids = target_list.split(' ');
  136. var selector = '#' + target_ids.join(',#');
  137. return $(selector);
  138. }
  139. };
  140. /**
  141. * For this model:
  142. * <span>target term</span><span class="hovertip">tooltip text</span>
  143. */
  144. targetSelectByPrevious = function(el, config) {
  145. return $(el.previousSibling);
  146. }
  147. /**
  148. * Make all siblings targets. Experimental.
  149. */
  150. targetSelectBySiblings = function(el, config) {
  151. return $(el).siblings();
  152. }
  153. //// prepare tip elements ////
  154. /**
  155. * The tooltip element needs special preparation. You may define your own
  156. * prepare functions to cusomize the behavior.
  157. */
  158. // adds a close link to clicktips
  159. clicktipPrepareWithCloseLink = function(o, config) {
  160. return o.append("<a class='clicktip_close'><span>close</span></a>")
  161. .find('a.clicktip_close').click(function(e) {
  162. o.hide();
  163. return false;
  164. }).end();
  165. };
  166. // ensure that hovertips do not disappear when the mouse is over them.
  167. // also position the hovertip as an absolutely positioned child of body.
  168. hovertipPrepare = function(o, config) {
  169. return o.hover(function() {
  170. hovertipHideCancel(this);
  171. }, function() {
  172. hovertipHideLater(this);
  173. }).css('position', 'absolute').each(hovertipPosition);
  174. };
  175. // do not modify tooltips when preparing
  176. hovertipPrepareNoOp = function(o, config) {
  177. return o;
  178. }
  179. //// manipulate tip elements /////
  180. /**
  181. * A variety of functions to modify tooltip elements
  182. */
  183. // move tooltips to body, so they are not descended from other absolutely
  184. // positioned elements.
  185. hovertipPosition = function(i) {
  186. document.body.appendChild(this);
  187. }
  188. hovertipIsVisible = function(el) {
  189. return (jQuery.css(el, 'display') != 'none');
  190. }
  191. // show the tooltip under the mouse.
  192. // Introduce a delay, so tip appears only if cursor rests on target for more than an instant.
  193. hovertipShowUnderMouse = function(el) {
  194. hovertipHideCancel(el);
  195. if (!hovertipIsVisible(el)) {
  196. el.ht.showing = // keep reference to timer
  197. window.setTimeout(function() {
  198. el.ht.tip.css({
  199. 'position':'absolute',
  200. 'top': hovertipMouseY + 'px',
  201. 'left': hovertipMouseX + 'px'})
  202. .show();
  203. }, el.ht.config.showDelay);
  204. }
  205. };
  206. // do not hide
  207. hovertipHideCancel = function(el) {
  208. if (el.ht.hiding) {
  209. window.clearTimeout(el.ht.hiding);
  210. el.ht.hiding = null;
  211. }
  212. };
  213. // Hide a tooltip, but only after a delay.
  214. // The delay allow the tip to remain when user moves mouse from target to tooltip
  215. hovertipHideLater = function(el) {
  216. if (el.ht.showing) {
  217. window.clearTimeout(el.ht.showing);
  218. el.ht.showing = null;
  219. }
  220. if (el.ht.hiding) {
  221. window.clearTimeout(el.ht.hiding);
  222. el.ht.hiding = null;
  223. }
  224. el.ht.hiding =
  225. window.setTimeout(function() {
  226. if (el.ht.hiding) {
  227. // fadeOut, slideUp do not work on Konqueror
  228. el.ht.tip.hide();
  229. }
  230. }, el.ht.config.hideDelay);
  231. };
  232. //// prepare target elements ////
  233. /**
  234. * As we prepared the tooltip elements, the targets also need preparation.
  235. *
  236. * You may define your own custom behavior.
  237. */
  238. // when clicked on target, toggle visibilty of tooltip
  239. clicktipTargetPrepare = function(o, el, config) {
  240. return o.addClass(config.attribute + '_target')
  241. .click(function() {
  242. el.ht.tip.toggle();
  243. return false;
  244. });
  245. };
  246. // when hover over target, make tooltip appear
  247. hovertipTargetPrepare = function(o, el, config) {
  248. return o.addClass(config.attribute + '_target')
  249. .hover(function() {
  250. // show tip when mouse over target
  251. hovertipShowUnderMouse(el);
  252. },
  253. function() {
  254. // hide the tip
  255. // add a delay so user can move mouse from the target to the tip
  256. hovertipHideLater(el);
  257. });
  258. };
  259. /**
  260. * hovertipActivate() is our jQuery plugin function. It turns on hovertip or
  261. * clicktip behavior for a set of elements.
  262. *
  263. * @param config
  264. * controls aspects of tooltip behavior. Be sure to define
  265. * 'attribute', 'showDelay' and 'hideDelay'.
  266. *
  267. * @param targetSelect
  268. * function finds the targets of a given tooltip element.
  269. *
  270. * @param tipPrepare
  271. * function alters the tooltip to display and behave properly
  272. *
  273. * @param targetPrepare
  274. * function alters the target to display and behave properly.
  275. */
  276. jQuery.fn.hovertipActivate = function(config, targetSelect, tipPrepare, targetPrepare) {
  277. //alert('activating ' + this.size());
  278. // unhide so jquery show/hide will work.
  279. return this.css('display', 'block')
  280. .hide() // don't show it until click
  281. .each(function() {
  282. if (!this.ht)
  283. this.ht = new Object();
  284. this.ht.config = config;
  285. // find our targets
  286. var targets = targetSelect(this, config);
  287. if (targets && targets.size()) {
  288. if (!this.ht.targets)
  289. this.ht.targets = targetPrepare(targets, this, config);
  290. else
  291. this.ht.targets.add(targetPrepare(targets, this, config));
  292. // listen to mouse move events so we know exatly where to place hovetips
  293. targets.mousemove(hovertipMouseUpdate);
  294. // prepare the tooltip element
  295. // is it bad form to call $(this) here?
  296. if (!this.ht.tip)
  297. this.ht.tip = tipPrepare($(this), config);
  298. }
  299. })
  300. ;
  301. };
  302. /**
  303. * Here's an example ready function which shows how to enable tooltips.
  304. *
  305. * You can make this considerably shorter by choosing only the markup style(s)
  306. * you will use.
  307. *
  308. * You may also remove the code that wraps hovertips to produce drop-shadow FX
  309. *
  310. * Invoke this function or one like it from your $(document).ready().
  311. *
  312. * Here, we break the action up into several timout callbacks, to avoid
  313. * locking up browsers.
  314. */
  315. function hovertipInit() {
  316. // specify the attribute name we use for our clicktips
  317. var clicktipConfig = {'attribute':'clicktip'};
  318. /**
  319. * To enable this style of markup (id on tooltip):
  320. * <span clicktip="foo">target</span>...
  321. * <div id="foo" class="clicktip">blah blah</div>
  322. */
  323. window.setTimeout(function() {
  324. $('.clicktip').hovertipActivate(clicktipConfig,
  325. targetSelectById,
  326. clicktipPrepareWithCloseLink,
  327. clicktipTargetPrepare);
  328. }, 0);
  329. /**
  330. * To enable this style of markup (id on target):
  331. * <span id="foo">target</span>...
  332. * <div target="foo" class="clicktip">blah blah</div>
  333. */
  334. window.setTimeout(function() {
  335. $('.clicktip').hovertipActivate(clicktipConfig,
  336. targetSelectByTargetAttribute,
  337. clicktipPrepareWithCloseLink,
  338. clicktipTargetPrepare);
  339. }, 0);
  340. // specify our configuration for hovertips, including delay times (millisec)
  341. var hovertipConfig = {'attribute':'hovertip',
  342. 'showDelay': 300,
  343. 'hideDelay': 700};
  344. // use <div class='hovertip'>blah blah</div>
  345. var hovertipSelect = 'div.hovertip';
  346. // OPTIONAL: here we wrap each hovertip to apply special effect. (i.e. drop shadow):
  347. $(hovertipSelect).css('display', 'block').addClass('hovertip_wrap3').
  348. wrap("<div class='hovertip_wrap0'><div class='hovertip_wrap1'><div class='hovertip_wrap2'>" +
  349. "</div></div></div>").each(function() {
  350. // fix class and attributes for newly wrapped elements
  351. var tooltip = this.parentNode.parentNode.parentNode;
  352. if (this.getAttribute('target'))
  353. tooltip.setAttribute('target', this.getAttribute('target'));
  354. if (this.getAttribute('id')) {
  355. var id = this.getAttribute('id');
  356. this.removeAttribute('id');
  357. tooltip.setAttribute('id', id);
  358. }
  359. });
  360. hovertipSelect = 'div.hovertip_wrap0';
  361. // end optional FX section
  362. /**
  363. * To enable this style of markup (id on tooltip):
  364. * <span hovertip="foo">target</span>...
  365. * <div id="foo" class="hovertip">blah blah</div>
  366. */
  367. window.setTimeout(function() {
  368. $(hovertipSelect).hovertipActivate(hovertipConfig,
  369. targetSelectById,
  370. hovertipPrepare,
  371. hovertipTargetPrepare);
  372. }, 0);
  373. /**
  374. * To enable this style of markup (id on target):
  375. * <span id="foo">target</span>...
  376. * <div target="foo" class="hovertip">blah blah</div>
  377. */
  378. window.setTimeout(function() {
  379. $(hovertipSelect).hovertipActivate(hovertipConfig,
  380. targetSelectByTargetAttribute,
  381. hovertipPrepare,
  382. hovertipTargetPrepare);
  383. }, 0);
  384. /**
  385. * This next section enables this style of markup:
  386. * <foo><span>target</span><span class="hovertip">blah blah</span></foo>
  387. *
  388. * With drop shadow effect.
  389. *
  390. */
  391. var hovertipSpanSelect = 'span.hovertip';
  392. // activate hovertips with wrappers for FX (drop shadow):
  393. $(hovertipSpanSelect).css('display', 'block').addClass('hovertip_wrap3').
  394. wrap("<span class='hovertip_wrap0'><span class='hovertip_wrap1'><span class='hovertip_wrap2'>" +
  395. "</span></span></span>").each(function() {
  396. // fix class and attributes for newly wrapped elements
  397. var tooltip = this.parentNode.parentNode.parentNode;
  398. if (this.getAttribute('target'))
  399. tooltip.setAttribute('target', this.getAttribute('target'));
  400. if (this.getAttribute('id')) {
  401. var id = this.getAttribute('id');
  402. this.removeAttribute('id');
  403. tooltip.setAttribute('id', id);
  404. }
  405. });
  406. hovertipSpanSelect = 'span.hovertip_wrap0';
  407. window.setTimeout(function() {
  408. $(hovertipSpanSelect)
  409. .hovertipActivate(hovertipConfig,
  410. targetSelectByPrevious,
  411. hovertipPrepare,
  412. hovertipTargetPrepare);
  413. }, 0);
  414. }