├── css ├── tooltip.css └── styles.css ├── LICENSE ├── README.md ├── js └── Tooltip.js └── example.html /css/tooltip.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Tooltip.js styles 3 | */ 4 | 5 | .tooltip-js { 6 | position:absolute; 7 | background:#DB2A64; 8 | color:#ffffff; 9 | padding:8px; 10 | z-index:999; 11 | } 12 | 13 | .tooltip-js.alt-tooltip { 14 | background:#490B22; 15 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 Matthias Schuetz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tooltip.js 2 | ========== 3 | 4 | ## A basic mouseover tooltip script 5 | 6 | This tooltip script is a basic example of how data attributes can be used to add a tooltip functionality to DOM elements. The tooltips are created for each element by reading out the title attribute. Additionally the tooltip offset can be configured by passing a JSON option string to the data attribute. This concept comes from AngularJS and provides a quick extensibility. 7 | 8 | Here are some key facts that should provide a short overview of what Tooltip.js is and how it works: 9 | 10 | * Configuration via HTML and data attributes 11 | * Customizable via CSS 12 | * Extensible (JavaScript code is about 100 lines) 13 | * Runs in all major browsers and Internet Explorer 9+ 14 | * No external dependencies 15 | 16 | 17 | So the script is very basic and can be extended with various features. It's meant to be a learning example of how a tooltip functionality can be realized by using data attributes and parsing JSON string options. 18 | 19 | ## Demo 20 | You can check the example.html that is included in this package. This is also the website of Tooltip.js. 21 | 22 | ## Usage 23 | 24 | You just need to include the JavaScript file in the HTML code of your site. That's it. Any further configuration is optional. From this point, when the HTML page has been loaded, all elements having the data attribute "data-tooltip" will be processed and provided with the tooltip functionality. The tooltip text comes from an additional title attribute. A sample element looks like this: 25 | 26 | ### HTML 27 | 28 | ```html 29 | 30 | 31 | 32 | 33 | 34 | 35 |
Demo
36 | 37 | 38 | ``` 39 | 40 | You can also specify additional options to define a custom offset or a CSS class for each tooltip by using a JSON option string: 41 | 42 | ```html 43 |
Demo
44 | ``` 45 | 46 | This would set the tooltip offset (relative to the mouse cursor) to 10 pixels for this element. It has to be said that this is a very basic implementation of parsing a JSON option string. So you'll have to use the single quotes for attributes in order to make JSON.parse work. 47 | 48 | ### JavaScript 49 | 50 | In addition you can call some additional methods which are globally available. You may set the default options which contain values for tooltipId and offsetDefault. This will affect all tooltips on the page. You can do this by using the following code: 51 | 52 | ```javascript 53 | tooltip.setOptions({ 54 | tooltipId: "example", 55 | offsetDefault: 20 56 | }); 57 | ``` 58 | 59 | Furthermore you can refresh all tooltips on a page with one call. This is required if you add tooltip elements dynamically (via Ajax) or update their options/text afterwards. Simply call this method to rebuild all tooltips. 60 | 61 | ```javascript 62 | tooltip.refresh(); 63 | ``` 64 | 65 | ### CSS 66 | 67 | There's always one div element added and removed to the body element when hovering over a tooltip source element. By default this div element has the id "tooltip". So the CSS is very basic to style the tooltip: 68 | 69 | ```css 70 | #tooltip { 71 | position:absolute; 72 | background:#DB2A64; 73 | color:#ffffff; 74 | padding:8px; 75 | } 76 | ``` 77 | 78 | That's all. As the Tooltip.js script is very basic, all tooltips on a page share the same look. You can go ahead and extend the script if you want to use different styles for each tooltip. 79 | 80 | ## License 81 | 82 | Tooltip.js is released under the MIT license. 83 | -------------------------------------------------------------------------------- /js/Tooltip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tooltip.js 3 | * A basic script that applies a mouseover tooltip functionality to all elements of a page that have a data-tooltip attribute 4 | * Matthias Schuetz, https://github.com/matthias-schuetz 5 | * 6 | * Copyright (C) Matthias Schuetz 7 | * Free to use under the MIT license 8 | */ 9 | 10 | (function (root, factory) { 11 | if (typeof define === "function" && define.amd) { 12 | // AMD. Register as an anonymous module. 13 | define(factory); 14 | } else if (!root.tooltip) { 15 | // Browser globals 16 | root.tooltip = factory(root); 17 | } 18 | }(this, function() { 19 | var _options = { 20 | tooltipId: "tooltip", 21 | offsetDefault: 15 22 | }; 23 | 24 | var _tooltips = []; 25 | var _tooltipsTemp = null; 26 | 27 | function _bindTooltips(resetTooltips) { 28 | if (resetTooltips) { 29 | _tooltipsTemp = _tooltips.concat(); 30 | _tooltips = []; 31 | } 32 | 33 | Array.prototype.forEach.call(document.querySelectorAll("[data-tooltip]"), function(elm, idx) { 34 | var tooltipText = (elm.getAttribute("title") || "").trim(); 35 | var options; 36 | 37 | if (resetTooltips && _tooltipsTemp.length && _tooltipsTemp[idx] && _tooltipsTemp[idx].text) { 38 | if (tooltipText.length === 0) { 39 | elm.setAttribute("title", _tooltipsTemp[idx].text); 40 | tooltipText = _tooltipsTemp[idx].text; 41 | } 42 | 43 | elm.removeEventListener("mousemove", _onElementMouseMove); 44 | elm.removeEventListener("mouseout", _onElementMouseOut); 45 | elm.removeEventListener("mouseover", _onElementMouseOver); 46 | } 47 | 48 | if (tooltipText) { 49 | elm.setAttribute("title", ""); 50 | elm.setAttribute("data-tooltip-id", idx); 51 | options = _parseOptions(elm.getAttribute("data-tooltip")); 52 | 53 | _tooltips[idx] = { 54 | text: tooltipText, 55 | options: options 56 | }; 57 | 58 | elm.addEventListener("mousemove", _onElementMouseMove); 59 | elm.addEventListener("mouseout", _onElementMouseOut); 60 | elm.addEventListener("mouseover", _onElementMouseOver); 61 | } 62 | }); 63 | 64 | if (resetTooltips) { 65 | _tooltipsTemp = null; 66 | } 67 | } 68 | 69 | function _createTooltip(text, tooltipId) { 70 | var tooltipElm = document.createElement("div"); 71 | var tooltipText = document.createTextNode(text); 72 | var options = tooltipId && _tooltips[tooltipId] && _tooltips[tooltipId].options; 73 | 74 | if (options && options["class"]) { 75 | tooltipElm.setAttribute("class", 'tooltip-js ' + options["class"]); 76 | } else { 77 | tooltipElm.setAttribute("class", 'tooltip-js'); 78 | } 79 | 80 | tooltipElm.setAttribute("id", _options.tooltipId); 81 | tooltipElm.appendChild(tooltipText); 82 | 83 | document.querySelector("body").appendChild(tooltipElm); 84 | } 85 | 86 | function _getTooltipElm() { 87 | return document.querySelector("#" + _options.tooltipId); 88 | } 89 | 90 | function _onElementMouseMove(evt) { 91 | var tooltipId = this.getAttribute("data-tooltip-id"); 92 | var tooltipElm = _getTooltipElm(); 93 | var options = tooltipId && _tooltips[tooltipId] && _tooltips[tooltipId].options; 94 | var offset = options && options.offset || _options.offsetDefault; 95 | var scrollY = window.scrollY || window.pageYOffset; 96 | var scrollX = window.scrollX || window.pageXOffset; 97 | var tooltipTop = evt.pageY + offset; 98 | var tooltipLeft = evt.pageX + offset; 99 | 100 | if (tooltipElm) { 101 | tooltipTop = (tooltipTop - scrollY + tooltipElm.offsetHeight + 20 >= window.innerHeight ? (tooltipTop - tooltipElm.offsetHeight - 20) : tooltipTop); 102 | tooltipLeft = (tooltipLeft - scrollX + tooltipElm.offsetWidth + 20 >= window.innerWidth ? (tooltipLeft - tooltipElm.offsetWidth - 20) : tooltipLeft); 103 | 104 | tooltipElm.style.top = tooltipTop + "px"; 105 | tooltipElm.style.left = tooltipLeft + "px"; 106 | } 107 | } 108 | 109 | function _onElementMouseOut(evt) { 110 | var tooltipElm = _getTooltipElm(); 111 | 112 | if (tooltipElm) { 113 | document.querySelector("body").removeChild(tooltipElm); 114 | } 115 | } 116 | 117 | function _onElementMouseOver(evt) { 118 | var tooltipId = this.getAttribute("data-tooltip-id"); 119 | var tooltipText = tooltipId && _tooltips[tooltipId] && _tooltips[tooltipId].text; 120 | 121 | if (tooltipText) { 122 | _createTooltip(tooltipText, tooltipId); 123 | } 124 | } 125 | 126 | function _parseOptions(options) { 127 | var optionsObj; 128 | 129 | if (options.length) { 130 | try { 131 | optionsObj = JSON.parse(options.replace(/'/ig, "\"")); 132 | } catch(err) { 133 | console.log(err); 134 | } 135 | } 136 | 137 | return optionsObj; 138 | } 139 | 140 | function _init() { 141 | window.addEventListener("load", _bindTooltips); 142 | } 143 | 144 | _init(); 145 | 146 | return { 147 | setOptions: function(options) { 148 | for (var option in options) { 149 | if (_options.hasOwnProperty(option)) { 150 | _options[option] = options[option]; 151 | } 152 | } 153 | }, 154 | refresh: function() { 155 | _bindTooltips(true); 156 | } 157 | }; 158 | })); 159 | -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tooltip.js - A basic mouseover tooltip script 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |

Tooltips through data-attributes

34 |

This tooltip script is a basic example of how data attributes can be used to add a tooltip functionality to DOM elements. The tooltips are created for each element by reading out the title attribute. Additionally the tooltip offset can be configured by passing a JSON option string to the data attribute. This concept comes from AngularJS and provides a quick extensibility.

35 |
36 |
37 |
38 |
39 |
40 |
41 |

Demo

42 |

43 | Tooltip demo 1 (hover here) 44 |

45 |

46 | Tooltip demo 2 (hover here) 47 |

48 |
49 |
50 |
51 |
52 |
53 |
54 |

Facts

55 |

Here are some key facts that should provide a short overview of what Tooltip.js is and how it works:

56 |
    57 |
  • Configuration via HTML and data attributes
  • 58 |
  • Customizable via CSS
  • 59 |
  • Extensible (JavaScript code is about 150 lines)
  • 60 |
  • Runs in all major browsers and Internet Explorer 9+
  • 61 |
  • No external dependencies
  • 62 |
63 |

So the script is very basic and can be extended with various features. It's meant to be a learning example of how a tooltip functionality can be realized by using data attributes and parsing JSON string options.

64 |
65 |
66 |
67 |
68 |
69 |
70 |

Usage

71 |

After downloading the package from GitHub, you just need to include the JavaScript file in the HTML code of your site. That's it. Any further configuration is optional. From this point, when the HTML page has been loaded, all elements having the data attribute "data-tooltip" will be processed and provided with the tooltip functionality. The tooltip text comes from an additional title attribute. A sample element looks like this:

72 |
 73 | <div data-tooltip title="I'm a tooltip text">Demo</div>
74 |

You can also specify additional options to define a custom offset or a CSS class for each tooltip by using a JSON option string:

75 |
 76 | <div data-tooltip="{ 'offset': 10, 'class': 'alt-tooltip' }" title="I'm a tooltip text">Demo</div>
77 |

This would set the tooltip offset (relative to the mouse cursor) to 10 pixels for this element. It has to be said that this is a very basic implementation of parsing a JSON option string. So you'll have to use the single quotes for attributes in order to make JSON.parse work. If you want to omit the single quotes you could use a library like the relaxed JSON parser.

78 |

In addition you may set the default options which contain values for tooltipId and offsetDefault. This will affect all tooltips on the page. You can do this by using the following code:

79 |
 80 | tooltip.setOptions({
 81 | 	tooltipId: "example",
 82 | 	offsetDefault: 20
 83 | });
84 |

Finally we will have a look at the CSS configuration of the tooltip. There's always one div element added and removed to the body element when hovering over a tooltip source element. By default this div element has the id "tooltip". So the CSS is very basic to style the tooltip:

85 |
 86 | #tooltip {
 87 | 	position:absolute;
 88 | 	background:#DB2A64;
 89 | 	color:#ffffff;
 90 | 	padding:8px;
 91 | }
92 |

That's all. As the Tooltip.js script is very basic, all tooltips on a page share the same look. You can go ahead and extend the script if you want to use different styles for each tooltip.

93 |
94 |
95 |
96 | 103 |
104 |
105 | 106 | 107 | -------------------------------------------------------------------------------- /css/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Tooltip.js 3 | * Copyright (c) 2015 Matthias Schuetz 4 | */ 5 | 6 | html, body { 7 | width:100%; 8 | height:100%; 9 | margin:0; 10 | padding:0; 11 | font-family:"Roboto Slab", sans-serif; 12 | font-weight:300; 13 | font-size:18px; 14 | line-height:1; 15 | } 16 | 17 | body { 18 | background:#ffffff; 19 | color:#000000; 20 | } 21 | 22 | ::selection { 23 | background:#a11147; 24 | color:#ffffff; 25 | } 26 | 27 | ::-moz-selection { 28 | background:#a11147; 29 | color:#ffffff; 30 | } 31 | 32 | div, p, h1, h2, h3, h4, h5, h6, ul, ol, li { 33 | margin:0; 34 | padding:0; 35 | font-weight:300; 36 | } 37 | 38 | h1 { 39 | font-size:80px; 40 | font-weight:700; 41 | } 42 | 43 | h2 { 44 | font-size:26px; 45 | line-height:40px; 46 | } 47 | 48 | h3 { 49 | font-size:22px; 50 | } 51 | 52 | h4 { 53 | font-size:18px; 54 | } 55 | 56 | a { 57 | color:#a11147; 58 | line-height:1.2; 59 | text-decoration:none; 60 | outline:none; 61 | display:inline-block; 62 | transition:all 150ms; 63 | -webkit-transition:all 150ms; 64 | } 65 | 66 | a:hover { 67 | background:#a11147; 68 | color:white; 69 | } 70 | 71 | a.no-hover:hover { 72 | background:transparent; 73 | } 74 | 75 | p { 76 | line-height:28px; 77 | } 78 | 79 | p:not(:last-child) { 80 | margin-bottom:20px; 81 | } 82 | 83 | ul.list { 84 | padding:0 40px 20px 20px; 85 | list-style-type:square; 86 | } 87 | 88 | ul.list li { 89 | line-height:28px; 90 | } 91 | 92 | .fullwidth { 93 | width:100%; 94 | } 95 | 96 | .fullwidth.separator { 97 | position:relative; 98 | margin:105px 0 0 0; 99 | } 100 | 101 | .fullwidth.separator:before { 102 | position:absolute; 103 | top:-52px; 104 | left:0; 105 | width:100%; 106 | height:1px; 107 | background:#db2a64; 108 | content:""; 109 | display:block; 110 | } 111 | 112 | .fullwidth.header-wrapper { 113 | background:#db2a64; 114 | } 115 | 116 | .page-container { 117 | width:990px; 118 | margin:0 auto; 119 | } 120 | 121 | #header { 122 | position:relative; 123 | width:100%; 124 | height:400px; 125 | } 126 | 127 | #teaser { 128 | position:absolute; 129 | top:48px; 130 | left:72px; 131 | width:700px; 132 | } 133 | 134 | #teaser h1 { 135 | color:#db2a64; 136 | background:#490b22; 137 | padding-left:10px; 138 | padding-right:10px; 139 | margin:0; 140 | line-height:120px; 141 | display:inline-block; 142 | } 143 | 144 | #teaser h1 span { 145 | font-weight:400; 146 | } 147 | 148 | #teaser h2 { 149 | color:#ffffff; 150 | background:#a11147; 151 | font-size:22px; 152 | padding-left:10px; 153 | padding-right:10px; 154 | margin-bottom:60px; 155 | display:inline-block; 156 | } 157 | 158 | #content { 159 | background:#ffffff; 160 | } 161 | 162 | .button { 163 | position:relative; 164 | width:280px; 165 | height:82px; 166 | background-color:#ffffff; 167 | font-size:24px; 168 | font-weight:400; 169 | color:#db2a64; 170 | text-transform:uppercase; 171 | text-align:center; 172 | display:table-cell; 173 | vertical-align:middle; 174 | } 175 | 176 | .button-download:hover { 177 | color:#db2a64; 178 | background:#490b22; 179 | transform:scale(1.1, 1.1); 180 | -webkit-transform:scale(1.1, 1.1); 181 | } 182 | 183 | .block { 184 | width:700px; 185 | margin-left:72px; 186 | } 187 | 188 | .block-head { 189 | margin-bottom:20px; 190 | text-transform:uppercase; 191 | } 192 | 193 | .block-passage { 194 | margin-top:40px; 195 | } 196 | 197 | pre, 198 | span.pre { 199 | font-family:Consolas, "Courier New", Courier, monospace; 200 | white-space:pre-wrap; 201 | } 202 | 203 | pre span { 204 | color:#db2a64; 205 | } 206 | 207 | pre span.alt { 208 | color:#a11147; 209 | } 210 | 211 | .cursor { 212 | cursor:default; 213 | } 214 | 215 | .footer { 216 | margin-top:50px; 217 | padding:30px 0; 218 | color:#61595b; 219 | background:#ecdde3; 220 | } 221 | 222 | /* 223 | * ------------------------------------------------------------------------------------------ Media Queries 224 | */ 225 | @media (max-width:1020px) { 226 | #teaser { 227 | left:30px; 228 | } 229 | 230 | .page-container { 231 | width:760px; 232 | } 233 | 234 | .block { 235 | margin-left:30px; 236 | } 237 | } 238 | 239 | @media (max-width:767px) { 240 | ul.list { 241 | padding:0 40px 20px 40px; 242 | } 243 | 244 | .fullwidth.large { 245 | min-height:790px; 246 | } 247 | 248 | #teaser { 249 | width:520px; 250 | } 251 | 252 | .fullwidth.compact #header:before { 253 | display:none; 254 | } 255 | 256 | .fullwidth.compact #header { 257 | height:393px; 258 | } 259 | 260 | body.has-customscrollbar .button-download { 261 | margin-right:23px; 262 | } 263 | 264 | body.has-customscrollbar-firefox .button-download { 265 | margin-right:5px; 266 | } 267 | 268 | .fullwidth { 269 | float:left; 270 | } 271 | 272 | .page-container, 273 | .block { 274 | width:100%; 275 | } 276 | 277 | .block { 278 | padding-right:0; 279 | margin-left:0; 280 | } 281 | 282 | .block h2, 283 | .block h3, 284 | .block h4 { 285 | margin-left:20px; 286 | padding-right:40px; 287 | } 288 | 289 | .block p { 290 | margin-left:20px; 291 | } 292 | 293 | .block .block-head, 294 | .block p { 295 | padding-right:20px; 296 | } 297 | 298 | pre { 299 | font-size:16px; 300 | margin-left:20px; 301 | } 302 | 303 | body.has-customscrollbar .block .block-head, 304 | body.has-customscrollbar .block p { 305 | padding-right:40px; 306 | } 307 | 308 | body.has-customscrollbar-firefox .block .block-head, 309 | body.has-customscrollbar-firefox .block p { 310 | padding-right:23px; 311 | } 312 | 313 | .footer p { 314 | padding-right:40px; 315 | } 316 | } 317 | 318 | @media (max-width:570px) { 319 | #teaser { 320 | width:420px; 321 | } 322 | 323 | pre { 324 | margin-right:20px; 325 | font-size:15px; 326 | word-break: break-all; 327 | word-wrap: break-word; 328 | white-space: pre-wrap; 329 | } 330 | } 331 | 332 | @media (max-width:470px) { 333 | #teaser { 334 | width:250px; 335 | } 336 | 337 | #teaser h1 { 338 | font-size:56px; 339 | line-height:86px; 340 | clear:both; 341 | float:left; 342 | } 343 | 344 | #teaser h2 { 345 | width:200px; 346 | padding-top:5px; 347 | padding-bottom:5px; 348 | font-size:16px; 349 | line-height:22px; 350 | clear:both; 351 | float:left; 352 | } 353 | 354 | .button-download { 355 | width:180px; 356 | height:40px; 357 | padding-top:20px; 358 | padding-bottom:10px; 359 | float:left; 360 | clear:both; 361 | } 362 | 363 | pre { 364 | font-size:14px; 365 | } 366 | } 367 | 368 | @media (max-width:330px) { 369 | .footer p { 370 | font-size:14px; 371 | } 372 | } 373 | --------------------------------------------------------------------------------