├── Demo.html ├── Graphics └── logo.png ├── README.md ├── Source ├── FloatingTips.Dialog.js └── FloatingTips.js └── package.yml /Demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FloatingTips demo page 6 | 7 | 8 | 9 | 10 | 11 | 72 | 73 | 113 | 114 | 115 | 116 | 117 |

FloatingTips demo page

118 | 119 |
120 | 121 |

Some simple tips. Different position, always centered:

122 |

123 | Default (top) | 124 | Bottom | 125 | Left | 126 | Right 127 |

128 | 129 |
130 | 131 |
132 | 133 |

134 | You can place it also inside the target element: 135 | Try me 136 |

137 | 138 |
139 | 140 |
141 | 142 |

A customized tip. Not centered, bigger arrow, customized content:

143 |

What can I say about me?

144 | 145 |
146 | 147 |
148 | 149 |

An advanced (and very cool!) tip. HTML content:

150 |

Let me see!

151 | 152 | 153 | 163 | 164 |
165 | 166 |
167 |

You can show tooltip on focus instead of cursor events:

168 |

169 |
170 | 171 |
172 |

When this tooltip has been shown, this tooltip will be shown

173 |
174 | 175 |
176 |

177 | Read Javascript comments on this demo or see 178 | docs 179 | for more! 180 |

181 |
182 | 183 | 184 | -------------------------------------------------------------------------------- /Graphics/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lorenzos/FloatingTips/784806c260650249c06abb151d0574ae784b9553/Graphics/logo.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FloatingTips 2 | ============ 3 | 4 | A Mootools plugin for creating floating balloon tips that nicely appears when hovering an element. 5 | High customizable using options (tooltip position, centering, arrow size, distance, animation, etc). 6 | 7 | ![Screenshot](https://github.com/lorenzos/FloatingTips/raw/master/Graphics/logo.png) 8 | 9 | 10 | How to use 11 | ---------- 12 | 13 | JS sample: 14 | 15 | #JS 16 | 17 | // Create a simple tips for all elements 18 | new FloatingTips('a'); // Title attribute will be used as tip. 19 | 20 | // A customized tip for all elements 21 | new FloatingTips('span.custom', { 22 | 23 | // Content can also be a function of the target element! 24 | content: function(e) { return 'I am ' + e.getSize().x + ' px wide! :)'; }, 25 | 26 | position: 'bottom', // Bottom positioned 27 | center: false, // Place the tip aligned with target 28 | arrowSize: 12, // A bigger arrow! 29 | 30 | }); 31 | 32 | HTML code: 33 | 34 | #HTML 35 | 36 | Simple tip 37 | Custom tip 38 | 39 | CSS tip styling: 40 | 41 | #CSS 42 | 43 | .floating-tip { 44 | background-color: black; 45 | padding: 5px 15px; 46 | color: #dddddd; 47 | font-weight: bold; 48 | font-size: 11px; 49 | -moz-border-radius: 3px; 50 | -webkit-border-radius: 3px; 51 | border-radius: 3px; 52 | } 53 | 54 | 55 | Docs 56 | ---- 57 | 58 | **Implements:** Options, Events 59 | 60 | **Syntax:** 61 | 62 | #JS 63 | 64 | var myTips = new FloatingTips(elements, options); 65 | 66 | - **elements**: Elements that will trigger floating tips; can an be a string selector or an element collection. 67 | - **options**: (*object*) Options for the class. They are all listed below. 68 | 69 | **Options:** 70 | 71 | - **position**: Tip position, can be "top", "right", "bottom", "left" or "inside" (default `"top"`). 72 | - **fixed**: If the tip should be placed in fixed position (default `FALSE`). This allows you to have tips on fixed elements, that do no scroll on page scrolling. 73 | - **center**: If the tip will be placed centered on the target element (default `TRUE`). 74 | - **content**: (*string or function*) If this is a string, the content of the tip will be the value of the target element attribute with that name (example `"title"`, default); if this is a function, the content will be the value returned by the function, that can accept an argument that is the target element (see **How to use** or **Demo**). 75 | - **html**: If the tooltip content must be interpreted as HTML code (default `FALSE`); if this is TRUE and `content` option is a function that returns an HTML element, inner HTML of that returned element is used as tip content. 76 | - **balloon**: `TRUE` if the tip is a balloon with a small triangle pointing the target element (default `TRUE`). 77 | - **arrowSize**: Size in pixel of the small triangle in the balloon (default `6`). 78 | - **arrowOffset**: Distance in pixel of the small triangle from the edge of the balloon when `center` option is `FALSE` (default `6`). 79 | - **distance**: Distance in pixel of the tip from the target element (default `3`). 80 | - **motion**: Distance in pixel that the tip will cover during in/out animation (default `6`). 81 | - **motionOnShow**: If the tip will animate when showing (default `TRUE`). 82 | - **motionOnHide**: If the tip will animate when hiding (default `TRUE`). 83 | - **showOn**: When to show the tip, can be any event of the target element (default `"mouseenter"`), or `null` when to never show the tip. 84 | - **hideOn**: When to hide the tip, can be any event of the target element (default `"mouseleave"`), or `null` when to never hide the tip. 85 | - **hideOnTipOutsideClick**: Will hide the tip if the mouse is clicked somewhere outside the tip itself or the triggering element (default `FALSE`). Especially useful for dialog-like tips which stay on the page until manual action. 86 | - **discrete**: Whether to show only one tip of the same group at the same time (default `FALSE`). The "same group" is considered the elements which have been passed to the `FloatingTips` instance in the constructor or being `attach()`ed later on. 87 | - **showDelay**: The delay the show event is fired (default `0`). 88 | - **hideDelay**: The delay the hide event is fired (default `0`). 89 | - **className**: The class name the tip container will get; necessary for styling (default `"floating-tip"`). 90 | - **identifier**: An identifier for the tip instance, will be added as class name to the outermost tooltip element. 91 | - **offset**: An object like `{x: 0, y: 0}` (default), that specify the distance of the tip from its normal position. 92 | - **fx**: An object for additional `Fx` options (default `{'duration': 'short'}`). 93 | 94 | **Events:** 95 | 96 | - **show(tip, element)**: Fires when the tip appears. `tip` is the tip element, `element` is the target element. 97 | - **hide(tip, element)**: Fires when the tip disappears. `tip` is the tip element, `element` is the target element. 98 | 99 | **Methods:** 100 | 101 | - **attach(elements)**: Adds other elements that will trigger floating tips; can an be a string selector or an element collection. 102 | - **detach(elements)**: Remove floating tips triggering from elements; can an be a string selector or an element collection. 103 | - **show(element)**: Manually show the tip on target `element`. 104 | - **hide(element)**: Manually hide the tip for `element`. 105 | - **toggle(element)**: Manually toggle the tip for `element`. 106 | 107 | **Element and Elements methods:** 108 | 109 | You can use some shortcut methods on **Element** and **Elements** for creating and showing tips. 110 | 111 | #JS 112 | 113 | $$('a').floatingTips(options); // Create tips 114 | $('myLink').floatingTipsShow(); // Show one 115 | var myTips = $('myLink').get('floatingTips'); // Get instance 116 | 117 | - **Elements.floatingTips(options)**: Creates a new instance of FloatingTips on elements. 118 | - **Element.floatingTips(options)**: Creates a new instance of FloatingTips on the element. 119 | - **Element.floatingTipsShow()**: Show the tip on the element. 120 | - **Element.floatingTipsHide()**: Hide the tip on the element. 121 | - **Element.floatingTipsToggle()**: Toggles the tip on the element. 122 | - **Element.get('floatingTips')**: Retrieves the instance of FloatingTips of the element. 123 | 124 | 125 | FloatingTips.Dialog 126 | ------------------- 127 | 128 | **FloatingTips.Dialog** is an extra class that extends **FloatingTips** to let you create dialog boxes that appears near target element (for example, on an important link for asking for confirmation). 129 | 130 | To learn more on how to use it, see **[HowTo](https://github.com/lorenzos/FloatingTips/wiki/Howto)** and **[Docs](https://github.com/lorenzos/FloatingTips/wiki/Docs)** wiki pages on Github. -------------------------------------------------------------------------------- /Source/FloatingTips.Dialog.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | description: Creates balloon dialogs with action buttons when clicking an element 4 | 5 | license: MIT-style 6 | 7 | authors: 8 | - Lorenzo Stanco 9 | 10 | requires: 11 | - core/1.3: '*' 12 | 13 | provides: [FloatingTips.Dialog] 14 | 15 | ... 16 | */ 17 | 18 | FloatingTips.Dialog = new Class({ 19 | 20 | Extends: FloatingTips, 21 | 22 | options: { 23 | showOn: 'click', 24 | hideOn: 'never', 25 | buttons: { }, 26 | buttonsClassName: '' 27 | }, 28 | 29 | initialize: function(element, text, options) { 30 | 31 | // Setup options 32 | this.setOptions(options); 33 | 34 | // Store element reference 35 | this.element = $(element); 36 | 37 | // Create buttons 38 | var s = this; 39 | var buttonsIndex = 0; 40 | var buttonsWrapper = new Element('p'); 41 | Object.each(this.options.buttons, function(buttonCallback, buttonCaption) { 42 | var button = new Element('button', { type: 'button' }); 43 | if (s.options.buttonsClassName) button.addClass(s.options.buttonsClassName); 44 | button.addClass(s.options.className + '-button' + ++buttonsIndex); 45 | button.set('text', buttonCaption).addEvent('click', buttonCallback.pass([ s.element, button, s ], s)); 46 | buttonsWrapper.adopt(button); 47 | }); 48 | 49 | // Create tip content 50 | var contentText = new Element('p', { 'text': text }); 51 | var content = new Element('div').adopt(contentText, buttonsWrapper); 52 | this.options.content = function() { return content; }; 53 | this.options.html = true; 54 | this.options.html_adopt = true; 55 | 56 | // Call FloatingTips constructor 57 | this.parent([this.element]); 58 | 59 | return this; 60 | }, 61 | 62 | popup: function() { 63 | this.show(this.element); 64 | return this; 65 | }, 66 | 67 | dismiss: function() { 68 | this.hide(this.element); 69 | return this; 70 | } 71 | 72 | }); -------------------------------------------------------------------------------- /Source/FloatingTips.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | description: Class for creating floating balloon tips that nicely appears when hovering an element. 4 | 5 | license: MIT-style 6 | 7 | authors: 8 | - Lorenzo Stanco 9 | 10 | requires: 11 | - core/1.3: '*' 12 | 13 | provides: [FloatingTips] 14 | 15 | ... 16 | */ 17 | 18 | var FloatingTips = new Class({ 19 | 20 | Implements: [Options, Events], 21 | 22 | options: { 23 | position: 'top', 24 | fixed: false, 25 | center: true, 26 | content: 'title', 27 | html: false, 28 | balloon: true, 29 | arrowSize: 6, 30 | arrowOffset: 6, 31 | distance: 3, 32 | motion: 6, 33 | motionOnShow: true, 34 | motionOnHide: true, 35 | showOn: 'mouseenter', 36 | hideOn: 'mouseleave', 37 | hideOnTipOutsideClick: false, 38 | discrete: false, 39 | showDelay: 0, 40 | hideDelay: 0, 41 | className: 'floating-tip', 42 | identifier: '', 43 | offset: { x: 0, y: 0 }, 44 | fx: { 'duration': 'short' } 45 | }, 46 | 47 | /** 48 | * Array containing the elements which have a Tip applied by this instance 49 | */ 50 | networkMembers: [], 51 | 52 | initialize: function(elements, options) { 53 | this.setOptions(options); 54 | var s = this; 55 | this.boundShow = (function() { 56 | var element = this; 57 | s.show(element); 58 | if (s.options.discrete) s.networkMembers.filter(function(item) { 59 | return item !== element; 60 | }).invoke('floatingTipsHide'); 61 | }); 62 | this.boundHide = (function() { s.hide(this); }); 63 | if (!['top', 'right', 'bottom', 'left', 'inside'].contains(this.options.position)) this.options.position = 'top'; 64 | if (elements) this.attach(elements); 65 | return this; 66 | }, 67 | 68 | attach: function(elements) { 69 | var s = this; 70 | $$(elements).each(function(e) { 71 | s.networkMembers.include(e); 72 | if (e.retrieve('floatingtip_hasevents')) { return; } 73 | var evs = { }; 74 | s.options.showOn && (evs[s.options.showOn] = s.boundShow); 75 | s.options.hideOn && (evs[s.options.hideOn] = s.boundHide); 76 | e.addEvents(evs); 77 | e.store('floatingtip_hasevents', true); 78 | e.store('floatingtip_object', s); 79 | }); 80 | return this; 81 | }, 82 | 83 | detach: function(elements) { 84 | var s = this; 85 | var evs = { }; 86 | evs[this.options.showOn] = this.boundShow; 87 | evs[this.options.hideOn] = this.boundHide; 88 | $$(elements).each(function(e) { 89 | s.networkMembers.erase(e); 90 | s.hide(e); 91 | e.removeEvents(evs); 92 | e.eliminate('floatingtip_hasevents'); 93 | e.eliminate('floatingtip_object'); 94 | }); 95 | return this; 96 | }, 97 | 98 | show: function(element) { 99 | var old = element.retrieve('floatingtip'); 100 | if (old) if (old.getStyle('opacity') != 0) { clearTimeout(old.retrieve('timeout')); return this; } 101 | var tip = this._create(element); 102 | if (tip == null) return this; 103 | element.store('floatingtip', tip); 104 | this._animate(tip, 'in'); 105 | element.store('floatingtip_visible', true); 106 | this.fireEvent('show', [tip, element]); 107 | return this; 108 | }, 109 | 110 | hide: function(element) { 111 | var tip = element.retrieve('floatingtip'); 112 | if (!tip) return this; 113 | this._animate(tip, 'out'); 114 | element.store('floatingtip_visible', false); 115 | this.fireEvent('hide', [tip, element]); 116 | return this; 117 | }, 118 | 119 | toggle: function(element) { 120 | if (element.retrieve('floatingtip_visible')) return this.hide(element); 121 | else return this.show(element); 122 | }, 123 | 124 | _create: function(elem) { 125 | var o = this.options; 126 | var oc = o.content; 127 | var opos = o.position; 128 | 129 | if (oc == 'title') { 130 | oc = 'floatingtitle'; 131 | if (!elem.get('floatingtitle')) elem.setProperty('floatingtitle', elem.get('title')); 132 | elem.set('title', ''); 133 | } 134 | 135 | var cnt = (typeof(oc) == 'string' ? elem.get(oc) : oc(elem)); 136 | var cwr = new Element('div').addClass(o.className).setStyle('margin', 0); 137 | var tip = new Element('div') 138 | .addClass(o.className + '-wrapper') 139 | .addClass('position-' + this.options.position) 140 | .setStyles({ 'margin': 0, 'padding': 0 }) 141 | .adopt(cwr); 142 | if (o.identifier.length > 0) { 143 | tip.addClass(o.identifier); 144 | } 145 | 146 | if (cnt) { 147 | if (o.html) { 148 | if (o.html_adopt) cwr.adopt(cnt); 149 | else cwr.set('html', typeof(cnt) == 'string' ? cnt : cnt.get('html')); 150 | } else { 151 | cwr.set('text', cnt); 152 | cwr.set('html', cwr.get('html').replace(/\n/g, '
')); 153 | } 154 | } else { 155 | return null; 156 | } 157 | 158 | var body = document.id(document.body); 159 | tip.setStyles({ 'position': (o.fixed ? 'fixed' : 'absolute'), 'opacity': 0, 'top': 0, 'left': 0 }).inject(body); 160 | 161 | // Z-index "copied" after tip injecting, because of webkit bug: https://bugs.webkit.org/show_bug.cgi?id=15562 162 | cwr.setStyle('position', 'relative'); // Position 163 | tip.setStyles({ 'z-index': cwr.getStyle('z-index') }); 164 | cwr.setStyle('position', null); // Reset position 165 | 166 | if (o.balloon && !Browser.ie6) { 167 | 168 | var trg = new Element('div').addClass(o.className + '-triangle').setStyles({ 'margin': 0, 'padding': 0 }); 169 | var trgSt = { 'border-color': cwr.getStyle('background-color'), 'border-width': o.arrowSize, 'border-style': 'solid','width': 0, 'height': 0 }; 170 | 171 | switch (opos) { 172 | case 'inside': 173 | case 'top' : trgSt['border-bottom-width'] = 0; break; 174 | case 'right' : trgSt['border-left-width' ] = 0; trgSt['float'] = 'left'; cwr.setStyle('margin-left', o.arrowSize); break; 175 | case 'bottom': trgSt['border-top-width' ] = 0; break; 176 | case 'left' : trgSt['border-right-width' ] = 0; 177 | if (Browser.ie7) { trgSt['position'] = 'absolute'; trgSt['right'] = 0; } else { trgSt['float'] = 'right'; } 178 | cwr.setStyle('margin-right', o.arrowSize); break; 179 | } 180 | 181 | switch (opos) { 182 | case 'inside': case 'top': case 'bottom': 183 | trgSt['border-left-color'] = trgSt['border-right-color'] = 'transparent'; 184 | trgSt['margin-left'] = o.center ? tip.getSize().x / 2 - o.arrowSize : o.arrowOffset; break; 185 | case 'left': case 'right': 186 | trgSt['border-top-color'] = trgSt['border-bottom-color'] = 'transparent'; 187 | trgSt['margin-top'] = o.center ? tip.getSize().y / 2 - o.arrowSize : o.arrowOffset; break; 188 | } 189 | 190 | // Firefox triangle pixelation fix (https://brettstrikesback.com/de-pixelating-the-css-triangle/) 191 | if (Browser.firefox || Browser.name == 'firefox') { 192 | trgSt['-moz-transform'] = 'scale(1.01)'; 193 | trgSt['transform'] = 'scale(1.01)'; 194 | } 195 | 196 | trg.setStyles(trgSt).inject(tip, (opos == 'top' || opos == 'inside') ? 'bottom' : 'top'); 197 | 198 | } 199 | 200 | var tipSz = tip.getSize(), trgC = elem.getCoordinates(); 201 | var offsetOption = ('function' === typeof(o.offset) ? Object.merge({ x: 0, y: 0 }, o.offset(elem)) : o.offset); 202 | var pos = { x: trgC.left + offsetOption.x, y: trgC.top + offsetOption.y }; 203 | 204 | if (opos == 'inside') { 205 | tip.setStyles({ 'width': tip.getStyle('width'), 'height': tip.getStyle('height') }); 206 | elem.setStyle('position', 'relative').adopt(tip); 207 | pos = { x: o.offset.x, y: o.offset.y }; 208 | } else { 209 | switch (opos) { 210 | case 'top' : pos.y -= tipSz.y + o.distance; break; 211 | case 'right' : pos.x += trgC.width + o.distance; break; 212 | case 'bottom': pos.y += trgC.height + o.distance; break; 213 | case 'left' : pos.x -= tipSz.x + o.distance; break; 214 | } 215 | } 216 | 217 | if (o.center) { 218 | switch (opos) { 219 | case 'top' : case 'bottom': pos.x += (trgC.width / 2 - tipSz.x / 2); break; 220 | case 'left': case 'right' : pos.y += (trgC.height / 2 - tipSz.y / 2); break; 221 | case 'inside': 222 | pos.x += (trgC.width / 2 - tipSz.x / 2); 223 | pos.y += (trgC.height / 2 - tipSz.y / 2); break; 224 | } 225 | } 226 | 227 | tip.set('morph', o.fx).store('position', pos); 228 | tip.setStyles({ 'top': pos.y, 'left': pos.x }); 229 | 230 | if (o.hideOnTipOutsideClick) { 231 | var documentClickHandler = null; 232 | documentClickHandler = function(event) { 233 | var eventTarget = document.id(event.target); 234 | if (elem && elem !== eventTarget && !elem.contains(eventTarget)) { 235 | this.hide(elem); 236 | } else if (!elem) { 237 | document.removeEvent('click', documentClickHandler); 238 | } 239 | }.bind(this); 240 | document.addEvent('click', documentClickHandler); 241 | tip.addEvent('click', function(event) { event.stopPropagation(); }); 242 | } 243 | 244 | return tip; 245 | }, 246 | 247 | _animate: function(tip, d) { 248 | 249 | clearTimeout(tip.retrieve('timeout')); 250 | tip.store('timeout', (function(t) { 251 | 252 | var o = this.options, din = (d == 'in'); 253 | var m = { 'opacity': din ? 1 : 0 }; 254 | 255 | if ((o.motionOnShow && din) || (o.motionOnHide && !din)) { 256 | var pos = t.retrieve('position'); 257 | if (!pos) return; 258 | switch (o.position) { 259 | case 'inside': 260 | case 'top' : m['top'] = din ? [pos.y - o.motion, pos.y] : pos.y - o.motion; break; 261 | case 'right' : m['left'] = din ? [pos.x + o.motion, pos.x] : pos.x + o.motion; break; 262 | case 'bottom': m['top'] = din ? [pos.y + o.motion, pos.y] : pos.y + o.motion; break; 263 | case 'left' : m['left'] = din ? [pos.x - o.motion, pos.x] : pos.x - o.motion; break; 264 | } 265 | } 266 | 267 | t.morph(m); 268 | if (!din) t.get('morph').chain(function() { this.dispose(); }.bind(t)); 269 | 270 | }).delay((d == 'in') ? this.options.showDelay : this.options.hideDelay, this, tip)); 271 | 272 | return this; 273 | 274 | } 275 | 276 | }); 277 | 278 | Elements.implement({ 279 | 280 | floatingTips: function(options) { 281 | new FloatingTips(this, options); 282 | return this; 283 | } 284 | 285 | }); 286 | 287 | Element.implement({ 288 | 289 | floatingTips: function(options) { 290 | new FloatingTips($$(this), options); 291 | return this; 292 | }, 293 | 294 | floatingTipsShow: function() { 295 | var tip = this.retrieve('floatingtip_object'); 296 | if (tip) tip.show(this); 297 | return this; 298 | }, 299 | 300 | floatingTipsHide: function() { 301 | var tip = this.retrieve('floatingtip_object'); 302 | if (tip) tip.hide(this); 303 | return this; 304 | }, 305 | 306 | floatingTipsToggle: function() { 307 | var tip = this.retrieve('floatingtip_object'); 308 | if (tip) tip.toggle(this); 309 | return this; 310 | } 311 | 312 | }); 313 | 314 | Element.Properties.floatingTips = { 315 | 316 | get: function(){ 317 | return this.retrieve('floatingtip_object'); 318 | } 319 | 320 | }; 321 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | name: FloatingTips 2 | author: lorenzo.stanco 3 | category: Interface 4 | tags: [tip, tooltip, balloon, tips] 5 | demo: http://jsfiddle.net/v520ae3L/ 6 | docs: http://wiki.github.com/lorenzos/FloatingTips/docs 7 | current: 2.10 8 | sources: 9 | - "Source/FloatingTips.js" 10 | - "Source/FloatingTips.Dialog.js" 11 | --------------------------------------------------------------------------------