|
1 <!DOCTYPE html> |
|
2 <html lang="en"> |
|
3 <head> |
|
4 <meta charset="utf-8"> |
|
5 <title>Example: Creating a Simple Tooltip Widget With Extensions</title> |
|
6 <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic"> |
|
7 <link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css"> |
|
8 <link rel="stylesheet" href="../assets/css/main.css"> |
|
9 <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css"> |
|
10 <link rel="shortcut icon" type="image/png" href="../assets/favicon.png"> |
|
11 <script src="../../build/yui/yui-min.js"></script> |
|
12 |
|
13 </head> |
|
14 <body> |
|
15 <!-- |
|
16 <a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a> |
|
17 --> |
|
18 <div id="doc"> |
|
19 <div id="hd"> |
|
20 <h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1> |
|
21 </div> |
|
22 |
|
23 |
|
24 <h1>Example: Creating a Simple Tooltip Widget With Extensions</h1> |
|
25 <div class="yui3-g"> |
|
26 <div class="yui3-u-3-4"> |
|
27 <div id="main"> |
|
28 <div class="content"><style type="text/css" scoped> |
|
29 .yui3-tooltip { |
|
30 position:absolute; |
|
31 } |
|
32 |
|
33 .yui3-tooltip-content { |
|
34 color: #000; |
|
35 padding: 2px 5px; |
|
36 border-color: #D4C237 #A6982B #A6982B #A6982B; |
|
37 border-width: 1px; |
|
38 border-style: solid; |
|
39 background-color: #FFEE69; |
|
40 } |
|
41 |
|
42 .yui3-tooltip-hidden { |
|
43 visibility:hidden; |
|
44 } |
|
45 |
|
46 div.yui3-hastooltip { |
|
47 border:1px solid #243356; |
|
48 background-color:#406ED9; |
|
49 color:#ffffff; |
|
50 width:25em; |
|
51 margin:20px 0px; |
|
52 padding:5px; |
|
53 cursor:default; |
|
54 } |
|
55 |
|
56 div.yui3-hastooltip span { |
|
57 font-style:italic; |
|
58 font-weight:bold; |
|
59 color:#ABCEFF; |
|
60 } |
|
61 |
|
62 .yui3-tooltip-content strong { |
|
63 font-weight:bold; |
|
64 } |
|
65 </style> |
|
66 |
|
67 <div class="intro"> |
|
68 <p>This is an advanced example, in which we create a Tooltip widget, by extending the base <code>Widget</code> class, and adding <code>WidgetStack</code> and <code>WidgetPosition</code> extensions, through <code>Base.build</code>.</p> |
|
69 </div> |
|
70 |
|
71 <div class="example"> |
|
72 <div id="delegate"> |
|
73 <div class="yui3-hastooltip" title="Tooltip 1" id="tt1">Tooltip One <span>(content from title)</span></div> |
|
74 <div class="yui3-hastooltip" title="Tooltip 2" id="tt2">Tooltip Two <span>(content set in event listener)</span></div> |
|
75 <div class="yui3-hastooltip" title="Tooltip 3" id="tt3">Tooltip Three <span>(content from lookup)</span></div> |
|
76 <div class="yui3-hastooltip" title="Tooltip 4" id="tt4">Tooltip Four <span>(content from title)</span></div> |
|
77 <label><input type="checkbox" id="prevent" /> Prevent Tooltip Four</label> |
|
78 </div> |
|
79 |
|
80 <script type="text/javascript"> |
|
81 YUI().use("event-mouseenter", "widget", "widget-position", "widget-stack", function(Y) { |
|
82 var Lang = Y.Lang, |
|
83 Node = Y.Node, |
|
84 OX = -10000, |
|
85 OY = -10000; |
|
86 |
|
87 var Tooltip = Y.Base.create("tooltip", Y.Widget, [Y.WidgetPosition, Y.WidgetStack], { |
|
88 |
|
89 // PROTOTYPE METHODS/PROPERTIES |
|
90 |
|
91 /* |
|
92 * Initialization Code: Sets up privately used state |
|
93 * properties, and publishes the events Tooltip introduces |
|
94 */ |
|
95 initializer : function(config) { |
|
96 |
|
97 this._triggerClassName = this.getClassName("trigger"); |
|
98 |
|
99 // Currently bound trigger node information |
|
100 this._currTrigger = { |
|
101 node: null, |
|
102 title: null, |
|
103 mouseX: Tooltip.OFFSCREEN_X, |
|
104 mouseY: Tooltip.OFFSCREEN_Y |
|
105 }; |
|
106 |
|
107 // Event handles - mouse over is set on the delegate |
|
108 // element, mousemove and mouseleave are set on the trigger node |
|
109 this._eventHandles = { |
|
110 delegate: null, |
|
111 trigger: { |
|
112 mouseMove : null, |
|
113 mouseOut: null |
|
114 } |
|
115 }; |
|
116 |
|
117 // Show/hide timers |
|
118 this._timers = { |
|
119 show: null, |
|
120 hide: null |
|
121 }; |
|
122 |
|
123 // Publish events introduced by Tooltip. Note the triggerEnter event is preventable, |
|
124 // with the default behavior defined in the _defTriggerEnterFn method |
|
125 this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true}); |
|
126 this.publish("triggerLeave", {preventable:false}); |
|
127 }, |
|
128 |
|
129 /* |
|
130 * Destruction Code: Clears event handles, timers, |
|
131 * and current trigger information |
|
132 */ |
|
133 destructor : function() { |
|
134 this._clearCurrentTrigger(); |
|
135 this._clearTimers(); |
|
136 this._clearHandles(); |
|
137 }, |
|
138 |
|
139 /* |
|
140 * bindUI is used to bind attribute change and dom event |
|
141 * listeners |
|
142 */ |
|
143 bindUI : function() { |
|
144 this.after("delegateChange", this._afterSetDelegate); |
|
145 this.after("nodesChange", this._afterSetNodes); |
|
146 |
|
147 this._bindDelegate(); |
|
148 }, |
|
149 |
|
150 /* |
|
151 * syncUI is used to update the rendered DOM, based on the current |
|
152 * Tooltip state |
|
153 */ |
|
154 syncUI : function() { |
|
155 this._uiSetNodes(this.get("triggerNodes")); |
|
156 }, |
|
157 |
|
158 /* |
|
159 * Public method, which can be used by triggerEvent event listeners |
|
160 * to set the content of the tooltip for the current trigger node |
|
161 */ |
|
162 setTriggerContent : function(content) { |
|
163 var contentBox = this.get("contentBox"); |
|
164 contentBox.set("innerHTML", ""); |
|
165 |
|
166 if (content) { |
|
167 if (content instanceof Node) { |
|
168 for (var i = 0, l = content.size(); i < l; ++i) { |
|
169 contentBox.appendChild(content.item(i)); |
|
170 } |
|
171 } else if (Lang.isString(content)) { |
|
172 contentBox.set("innerHTML", content); |
|
173 } |
|
174 } |
|
175 }, |
|
176 |
|
177 /* |
|
178 * Default attribute change listener for |
|
179 * the triggerNodes attribute |
|
180 */ |
|
181 _afterSetNodes : function(e) { |
|
182 this._uiSetNodes(e.newVal); |
|
183 }, |
|
184 |
|
185 /* |
|
186 * Default attribute change listener for |
|
187 * the delegate attribute |
|
188 */ |
|
189 _afterSetDelegate : function(e) { |
|
190 this._bindDelegate(e.newVal); |
|
191 }, |
|
192 |
|
193 /* |
|
194 * Updates the rendered DOM to reflect the |
|
195 * set of trigger nodes passed in |
|
196 */ |
|
197 _uiSetNodes : function(nodes) { |
|
198 if (this._triggerNodes) { |
|
199 this._triggerNodes.removeClass(this._triggerClassName); |
|
200 } |
|
201 |
|
202 if (nodes) { |
|
203 this._triggerNodes = nodes; |
|
204 this._triggerNodes.addClass(this._triggerClassName); |
|
205 } |
|
206 }, |
|
207 |
|
208 /* |
|
209 * Attaches the default mouseover DOM listener to the |
|
210 * current delegate node |
|
211 */ |
|
212 _bindDelegate : function() { |
|
213 var eventHandles = this._eventHandles; |
|
214 |
|
215 if (eventHandles.delegate) { |
|
216 eventHandles.delegate.detach(); |
|
217 eventHandles.delegate = null; |
|
218 } |
|
219 eventHandles.delegate = Y.delegate("mouseenter", Y.bind(this._onNodeMouseEnter, this), this.get("delegate"), "." + this._triggerClassName); |
|
220 }, |
|
221 |
|
222 /* |
|
223 * Default mouse enter DOM event listener. |
|
224 * |
|
225 * Delegates to the _enterTrigger method, |
|
226 * if the mouseover enters a trigger node. |
|
227 */ |
|
228 _onNodeMouseEnter : function(e) { |
|
229 var node = e.currentTarget; |
|
230 if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) { |
|
231 this._enterTrigger(node, e.pageX, e.pageY); |
|
232 } |
|
233 }, |
|
234 |
|
235 /* |
|
236 * Default mouse leave DOM event listener |
|
237 * |
|
238 * Delegates to _leaveTrigger if the mouse |
|
239 * leaves the current trigger node |
|
240 */ |
|
241 _onNodeMouseLeave : function(e) { |
|
242 this._leaveTrigger(e.currentTarget); |
|
243 }, |
|
244 |
|
245 /* |
|
246 * Default mouse move DOM event listener |
|
247 */ |
|
248 _onNodeMouseMove : function(e) { |
|
249 this._overTrigger(e.pageX, e.pageY); |
|
250 }, |
|
251 |
|
252 /* |
|
253 * Default handler invoked when the mouse enters |
|
254 * a trigger node. Fires the triggerEnter |
|
255 * event which can be prevented by listeners to |
|
256 * show the tooltip from being displayed. |
|
257 */ |
|
258 _enterTrigger : function(node, x, y) { |
|
259 this._setCurrentTrigger(node, x, y); |
|
260 this.fire("triggerEnter", {node:node, pageX:x, pageY:y}); |
|
261 }, |
|
262 |
|
263 /* |
|
264 * Default handler for the triggerEvent event, |
|
265 * which will setup the timer to display the tooltip, |
|
266 * if the default handler has not been prevented. |
|
267 */ |
|
268 _defTriggerEnterFn : function(e) { |
|
269 var node = e.node; |
|
270 if (!this.get("disabled")) { |
|
271 this._clearTimers(); |
|
272 var delay = (this.get("visible")) ? 0 : this.get("showDelay"); |
|
273 this._timers.show = Y.later(delay, this, this._showTooltip, [node]); |
|
274 } |
|
275 }, |
|
276 |
|
277 /* |
|
278 * Default handler invoked when the mouse leaves |
|
279 * the current trigger node. Fires the triggerLeave |
|
280 * event and sets up the hide timer |
|
281 */ |
|
282 _leaveTrigger : function(node) { |
|
283 this.fire("triggerLeave"); |
|
284 |
|
285 this._clearCurrentTrigger(); |
|
286 this._clearTimers(); |
|
287 |
|
288 this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip); |
|
289 }, |
|
290 |
|
291 /* |
|
292 * Default handler invoked for mousemove events |
|
293 * on the trigger node. Stores the current mouse |
|
294 * x, y positions |
|
295 */ |
|
296 _overTrigger : function(x, y) { |
|
297 this._currTrigger.mouseX = x; |
|
298 this._currTrigger.mouseY = y; |
|
299 }, |
|
300 |
|
301 /* |
|
302 * Shows the tooltip, after moving it to the current mouse |
|
303 * position. |
|
304 */ |
|
305 _showTooltip : function(node) { |
|
306 var x = this._currTrigger.mouseX; |
|
307 var y = this._currTrigger.mouseY; |
|
308 |
|
309 this.move(x + Tooltip.OFFSET_X, y + Tooltip.OFFSET_Y); |
|
310 |
|
311 this.show(); |
|
312 this._clearTimers(); |
|
313 |
|
314 this._timers.hide = Y.later(this.get("autoHideDelay"), this, this._hideTooltip); |
|
315 }, |
|
316 |
|
317 /* |
|
318 * Hides the tooltip, after clearing existing timers. |
|
319 */ |
|
320 _hideTooltip : function() { |
|
321 this._clearTimers(); |
|
322 this.hide(); |
|
323 }, |
|
324 |
|
325 /* |
|
326 * Set the rendered content of the tooltip for the current |
|
327 * trigger, based on (in order of precedence): |
|
328 * |
|
329 * a). The string/node content attribute value |
|
330 * b). From the content lookup map if it is set, or |
|
331 * c). From the title attribute if set. |
|
332 */ |
|
333 _setTriggerContent : function(node) { |
|
334 var content = this.get("content"); |
|
335 if (content && !(content instanceof Node || Lang.isString(content))) { |
|
336 content = content[node.get("id")] || node.getAttribute("title"); |
|
337 } |
|
338 this.setTriggerContent(content); |
|
339 }, |
|
340 |
|
341 /* |
|
342 * Set the currently bound trigger node information, clearing |
|
343 * out the title attribute if set and setting up mousemove/out |
|
344 * listeners. |
|
345 */ |
|
346 _setCurrentTrigger : function(node, x, y) { |
|
347 |
|
348 var currTrigger = this._currTrigger, |
|
349 triggerHandles = this._eventHandles.trigger; |
|
350 |
|
351 this._setTriggerContent(node); |
|
352 |
|
353 triggerHandles.mouseMove = Y.on("mousemove", Y.bind(this._onNodeMouseMove, this), node); |
|
354 triggerHandles.mouseOut = Y.on("mouseleave", Y.bind(this._onNodeMouseLeave, this), node); |
|
355 |
|
356 var title = node.getAttribute("title"); |
|
357 node.setAttribute("title", ""); |
|
358 |
|
359 currTrigger.mouseX = x; |
|
360 currTrigger.mouseY = y; |
|
361 currTrigger.node = node; |
|
362 currTrigger.title = title; |
|
363 }, |
|
364 |
|
365 /* |
|
366 * Clear out the current trigger state, restoring |
|
367 * the title attribute on the trigger node, |
|
368 * if it was originally set. |
|
369 */ |
|
370 _clearCurrentTrigger : function() { |
|
371 |
|
372 var currTrigger = this._currTrigger, |
|
373 triggerHandles = this._eventHandles.trigger; |
|
374 |
|
375 if (currTrigger.node) { |
|
376 var node = currTrigger.node; |
|
377 var title = currTrigger.title || ""; |
|
378 |
|
379 currTrigger.node = null; |
|
380 currTrigger.title = ""; |
|
381 |
|
382 triggerHandles.mouseMove.detach(); |
|
383 triggerHandles.mouseOut.detach(); |
|
384 triggerHandles.mouseMove = null; |
|
385 triggerHandles.mouseOut = null; |
|
386 |
|
387 node.setAttribute("title", title); |
|
388 } |
|
389 }, |
|
390 |
|
391 /* |
|
392 * Cancel any existing show/hide timers |
|
393 */ |
|
394 _clearTimers : function() { |
|
395 var timers = this._timers; |
|
396 if (timers.hide) { |
|
397 timers.hide.cancel(); |
|
398 timers.hide = null; |
|
399 } |
|
400 if (timers.show) { |
|
401 timers.show.cancel(); |
|
402 timers.show = null; |
|
403 } |
|
404 }, |
|
405 |
|
406 /* |
|
407 * Detach any stored event handles |
|
408 */ |
|
409 _clearHandles : function() { |
|
410 var eventHandles = this._eventHandles; |
|
411 |
|
412 if (eventHandles.delegate) { |
|
413 this._eventHandles.delegate.detach(); |
|
414 } |
|
415 if (eventHandles.trigger.mouseOut) { |
|
416 eventHandles.trigger.mouseOut.detach(); |
|
417 } |
|
418 if (eventHandles.trigger.mouseMove) { |
|
419 eventHandles.trigger.mouseMove.detach(); |
|
420 } |
|
421 } |
|
422 }, { |
|
423 |
|
424 // STATIC METHODS/PROPERTIES |
|
425 |
|
426 OFFSET_X : 15, |
|
427 OFFSET_Y : 15, |
|
428 OFFSCREEN_X : OX, |
|
429 OFFSCREEN_Y : OY, |
|
430 |
|
431 ATTRS : { |
|
432 |
|
433 /* |
|
434 * The tooltip content. This can either be a fixed content value, |
|
435 * or a map of id-to-values, designed to be used when a single |
|
436 * tooltip is mapped to multiple trigger elements. |
|
437 */ |
|
438 content : { |
|
439 value: null |
|
440 }, |
|
441 |
|
442 /* |
|
443 * The set of nodes to bind to the tooltip instance. Can be a string, |
|
444 * or a node instance. |
|
445 */ |
|
446 triggerNodes : { |
|
447 value: null, |
|
448 setter: function(val) { |
|
449 if (val && Lang.isString(val)) { |
|
450 val = Node.all(val); |
|
451 } |
|
452 return val; |
|
453 } |
|
454 }, |
|
455 |
|
456 /* |
|
457 * The delegate node to which event listeners should be attached. |
|
458 * This node should be an ancestor of all trigger nodes bound |
|
459 * to the instance. By default the document is used. |
|
460 */ |
|
461 delegate : { |
|
462 value: null, |
|
463 setter: function(val) { |
|
464 return Y.one(val) || Y.one("document"); |
|
465 } |
|
466 }, |
|
467 |
|
468 /* |
|
469 * The time to wait, after the mouse enters the trigger node, |
|
470 * to display the tooltip |
|
471 */ |
|
472 showDelay : { |
|
473 value:250 |
|
474 }, |
|
475 |
|
476 /* |
|
477 * The time to wait, after the mouse leaves the trigger node, |
|
478 * to hide the tooltip |
|
479 */ |
|
480 hideDelay : { |
|
481 value:10 |
|
482 }, |
|
483 |
|
484 /* |
|
485 * The time to wait, after the tooltip is first displayed for |
|
486 * a trigger node, to hide it, if the mouse has not left the |
|
487 * trigger node |
|
488 */ |
|
489 autoHideDelay : { |
|
490 value:2000 |
|
491 }, |
|
492 |
|
493 /* |
|
494 * Override the default visibility set by the widget base class |
|
495 */ |
|
496 visible : { |
|
497 value:false |
|
498 }, |
|
499 |
|
500 /* |
|
501 * Override the default XY value set by the widget base class, |
|
502 * to position the tooltip offscreen |
|
503 */ |
|
504 xy: { |
|
505 value:[OX, OY] |
|
506 } |
|
507 } |
|
508 }); |
|
509 |
|
510 var tt = new Tooltip({ |
|
511 triggerNodes:".yui3-hastooltip", |
|
512 delegate: "#delegate", |
|
513 content: { |
|
514 tt3: "Tooltip 3 (from lookup)" |
|
515 }, |
|
516 shim:false, |
|
517 zIndex:2 |
|
518 }); |
|
519 tt.render(); |
|
520 |
|
521 tt.on("triggerEnter", function(e) { |
|
522 var node = e.node; |
|
523 if (node && node.get("id") == "tt2") { |
|
524 this.setTriggerContent("Tooltip 2 (from triggerEvent)"); |
|
525 } |
|
526 }); |
|
527 |
|
528 var prevent = Y.one("#prevent"); |
|
529 tt.on("triggerEnter", function(e) { |
|
530 var node = e.node; |
|
531 if (prevent.get("checked")) { |
|
532 if (node && node.get("id") == "tt4") { |
|
533 e.preventDefault(); |
|
534 } |
|
535 } |
|
536 }); |
|
537 }); |
|
538 </script> |
|
539 |
|
540 </div> |
|
541 |
|
542 <h2>Creating A Tooltip Widget Class</h2> |
|
543 |
|
544 <h3>Basic Class Structure</h3> |
|
545 |
|
546 <p>As with the basic <a href="widget-extend.html">"Extending Widget"</a> example, the <code>Tooltip</code> class will extend the <code>Widget</code> base class and follows the same pattern we use for other classes which extend Base.</p> |
|
547 |
|
548 <p>Namely:</p> |
|
549 |
|
550 <ul> |
|
551 <li>Set up the constructor to invoke the superclass constructor</li> |
|
552 <li>Define a <code>NAME</code> property, to identify the class</li> |
|
553 <li>Define the default attribute configuration, using the <code>ATTRS</code> property</li> |
|
554 <li>Implement prototype methods</li> |
|
555 </ul> |
|
556 |
|
557 <p>This basic structure is shown below:</p> |
|
558 |
|
559 <pre class="code prettyprint">/* |
|
560 * Required NAME static field, used to identify the Widget class and |
|
561 * used as an event prefix, to generate class names etc. (set to the |
|
562 * class name in camel case). |
|
563 */ |
|
564 Tooltip.NAME = "tooltip"; |
|
565 |
|
566 /* Default Tooltip Attributes */ |
|
567 Tooltip.ATTRS = { |
|
568 |
|
569 /* |
|
570 * The tooltip content. This can either be a fixed content value, |
|
571 * or a map of id-to-values, designed to be used when a single |
|
572 * tooltip is mapped to multiple trigger elements. |
|
573 */ |
|
574 content : { |
|
575 value: null |
|
576 }, |
|
577 |
|
578 /* |
|
579 * The set of nodes to bind to the tooltip instance. Can be a string, |
|
580 * or a node instance. |
|
581 */ |
|
582 triggerNodes : { |
|
583 value: null, |
|
584 setter: function(val) { |
|
585 if (val && Lang.isString(val)) { |
|
586 val = Node.all(val); |
|
587 } |
|
588 return val; |
|
589 } |
|
590 }, |
|
591 |
|
592 /* |
|
593 * The delegate node to which event listeners should be attached. |
|
594 * This node should be an ancestor of all trigger nodes bound |
|
595 * to the instance. By default the document is used. |
|
596 */ |
|
597 delegate : { |
|
598 value: null, |
|
599 setter: function(val) { |
|
600 return Y.one(val) || Y.one("document"); |
|
601 } |
|
602 }, |
|
603 |
|
604 /* |
|
605 * The time to wait, after the mouse enters the trigger node, |
|
606 * to display the tooltip |
|
607 */ |
|
608 showDelay : { |
|
609 value:250 |
|
610 }, |
|
611 |
|
612 /* |
|
613 * The time to wait, after the mouse leaves the trigger node, |
|
614 * to hide the tooltip |
|
615 */ |
|
616 hideDelay : { |
|
617 value:10 |
|
618 }, |
|
619 |
|
620 /* |
|
621 * The time to wait, after the tooltip is first displayed for |
|
622 * a trigger node, to hide it, if the mouse has not left the |
|
623 * trigger node |
|
624 */ |
|
625 autoHideDelay : { |
|
626 value:2000 |
|
627 }, |
|
628 |
|
629 /* |
|
630 * Override the default visibility set by the widget base class |
|
631 */ |
|
632 visible : { |
|
633 value:false |
|
634 }, |
|
635 |
|
636 /* |
|
637 * Override the default XY value set by the widget base class, |
|
638 * to position the tooltip offscreen |
|
639 */ |
|
640 xy: { |
|
641 value:[Tooltip.OFFSCREEN_X, Tooltip.OFFSCREEN_Y] |
|
642 } |
|
643 }; |
|
644 |
|
645 Y.extend(Tooltip, Y.Widget, { |
|
646 // Prototype methods/properties |
|
647 });</pre> |
|
648 |
|
649 |
|
650 <h3>Adding WidgetPosition and WidgetStack Extension Support</h3> |
|
651 |
|
652 <p>The Tooltip class also needs basic positioning and stacking (z-index, shimming) support. As with the <a href="widget-build.html">Custom Widget Classes</a> example, we use |
|
653 <code>Base.create</code> to create a new <code>Tooltip</code> class with this support:</p> |
|
654 |
|
655 <pre class="code prettyprint">var Tooltip = Y.Base.create("tooltip", Y.Widget, [Y.WidgetPosition, Y.WidgetStack], |
|
656 { ... prototype properties ... }, |
|
657 { ... static properties ... },</pre> |
|
658 |
|
659 |
|
660 <h3>Lifecycle Methods: initializer, destructor</h3> |
|
661 |
|
662 <p>The <code>initializer</code> method is invoked during the <code>init</code> lifecycle phase, after the attributes are configured for each class. <code>Tooltip</code> uses it |
|
663 to setup the private state variables it will use to store the trigger node currently being serviced by the tooltip instance, event handles and show/hide timers.</p> |
|
664 |
|
665 <pre class="code prettyprint">initializer : function(config) { |
|
666 |
|
667 this._triggerClassName = this.getClassName("trigger"); |
|
668 |
|
669 // Currently bound trigger node information |
|
670 this._currTrigger = { |
|
671 node: null, |
|
672 title: null, |
|
673 mouseX: Tooltip.OFFSCREEN_X, |
|
674 mouseY: Tooltip.OFFSCREEN_Y |
|
675 }; |
|
676 |
|
677 // Event handles - mouse over is set on the delegate |
|
678 // element, mousemove and mouseleave are set on the trigger node |
|
679 this._eventHandles = { |
|
680 delegate: null, |
|
681 trigger: { |
|
682 mouseMove : null, |
|
683 mouseOut: null |
|
684 } |
|
685 }; |
|
686 |
|
687 // Show/hide timers |
|
688 this._timers = { |
|
689 show: null, |
|
690 hide: null |
|
691 }; |
|
692 |
|
693 // Publish events introduced by Tooltip. Note the triggerEnter event is preventable, |
|
694 // with the default behavior defined in the _defTriggerEnterFn method |
|
695 this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true}); |
|
696 this.publish("triggerLeave", {preventable:false}); |
|
697 }</pre> |
|
698 |
|
699 |
|
700 <p>The <code>destructor</code> is used to clear out stored state, detach any event handles and clear out the show/hide timers:</p> |
|
701 |
|
702 <pre class="code prettyprint">destructor : function() { |
|
703 this._clearCurrentTrigger(); |
|
704 this._clearTimers(); |
|
705 this._clearHandles(); |
|
706 }</pre> |
|
707 |
|
708 |
|
709 <h3>Lifecycle Methods: bindUI, syncUI</h3> |
|
710 |
|
711 <p>The <code>bindUI</code> and <code>syncUI</code> are invoked by the base Widget class' <code>renderer</code> method.</p> |
|
712 |
|
713 <p><code>bindUI</code> is used to bind the attribute change listeners used to update the rendered UI from the current state of the widget and also to bind |
|
714 the DOM listeners required to enable the UI for interaction.</p> |
|
715 |
|
716 <p><code>syncUI</code> is used to sync the UI state from the current widget state, when initially rendered.</p> |
|
717 |
|
718 <p><em>NOTE:</em> Widget's <code>renderer</code> method also invokes the <code>renderUI</code> method, which is responsible for laying down any additional content elements a widget requires. However |
|
719 tooltip does not have any additional elements in needs to add to the DOM, outside of the default Widget boundingBox and contentBox.</p> |
|
720 |
|
721 <pre class="code prettyprint">bindUI : function() { |
|
722 this.after("delegateChange", this._afterSetDelegate); |
|
723 this.after("nodesChange", this._afterSetNodes); |
|
724 |
|
725 this._bindDelegate(); |
|
726 }, |
|
727 |
|
728 syncUI : function() { |
|
729 this._uiSetNodes(this.get("triggerNodes")); |
|
730 }</pre> |
|
731 |
|
732 |
|
733 <h3>Attribute Supporting Methods</h3> |
|
734 |
|
735 <p>Tooltip's <code>triggerNodes</code>, which defines the set of nodes which should trigger this tooltip instance, |
|
736 has a couple of supporting methods associated with it.</p> |
|
737 |
|
738 <p>The <code>_afterSetNodes</code> method is the default attribute change event handler for the <code>triggerNodes</code> |
|
739 attribute. It invokes the <code>_uiSetNodes</code> method, which marks all trigger nodes with a trigger class name (<code>yui-tooltip-trigger</code>) when set.</p> |
|
740 |
|
741 <pre class="code prettyprint">_afterSetNodes : function(e) { |
|
742 this._uiSetNodes(e.newVal); |
|
743 }, |
|
744 |
|
745 _uiSetNodes : function(nodes) { |
|
746 if (this._triggerNodes) { |
|
747 this._triggerNodes.removeClass(this._triggerClassName); |
|
748 } |
|
749 |
|
750 if (nodes) { |
|
751 this._triggerNodes = nodes; |
|
752 this._triggerNodes.addClass(this._triggerClassName); |
|
753 } |
|
754 },</pre> |
|
755 |
|
756 |
|
757 <p>Similarly the <code>_afterSetDelegate</code> method is the default attribute change listener for the <code>delegate</code> attribute, |
|
758 and invokes <code>_bindDelegate</code> to set up the listeners when a new delegate node is set. We use <code>Y.delegate</code> support, along with Event's <code>mouseenter</code> support, |
|
759 which means the only thing we need to do is tell delegate which node we want to act as the delegate, and which elements we want to target using the <code>"." + this._triggerClassName</code> selector.</p> |
|
760 |
|
761 <pre class="code prettyprint">_afterSetDelegate : function(e) { |
|
762 this._bindDelegate(e.newVal); |
|
763 }, |
|
764 |
|
765 _bindDelegate : function() { |
|
766 var eventHandles = this._eventHandles; |
|
767 |
|
768 if (eventHandles.delegate) { |
|
769 eventHandles.delegate.detach(); |
|
770 eventHandles.delegate = null; |
|
771 } |
|
772 eventHandles.delegate = Y.delegate("mouseenter", Y.bind(this._onNodeMouseEnter, this), this.get("delegate"), "." + this._triggerClassName); |
|
773 },</pre> |
|
774 |
|
775 |
|
776 <h3>DOM Event Handlers</h3> |
|
777 |
|
778 <p>Tooltips interaction revolves around the <code>mouseenter</code>, <code>mousemove</code> and <code>mouseleave</code> DOM events. The mousenter listener is the only listener set up initially, on the <code>delegate</code> node:</p> |
|
779 |
|
780 <pre class="code prettyprint">_onNodeMouseEnter : function(e) { |
|
781 var node = e.currentTarget; |
|
782 if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) { |
|
783 this._enterTrigger(node, e.pageX, e.pageY); |
|
784 } |
|
785 }</pre> |
|
786 |
|
787 |
|
788 <p>Since the <code>mouseenter</code> implementation doesn't invoke it's listeners for <code>mouseover</code> events generated from elements nested |
|
789 inside the targeted node (for example when mousing out of a child element of a trigger node), there are no additional checks we need to perform other than to see if the node is the current trigger, before handing off to |
|
790 the <code>_enterTrigger</code> method to setup the current trigger state and attach mousemove and mouseleave listeners on the current trigger node.</p> |
|
791 |
|
792 <p>The mouseleave listener delegates to the <code>_leaveTrigger</code> method, and again, since the <code>mouseleave</code> implementation deals with nested elements, we don't need to perform any additional target checks:</p> |
|
793 |
|
794 <pre class="code prettyprint">_onNodeMouseLeave : function(e) { |
|
795 this._leaveTrigger(e.currentTarget); |
|
796 }</pre> |
|
797 |
|
798 |
|
799 <p>The mouse move listener delegates to the <code>_overTrigger</code> method to store the current mouse XY co-ordinates (used to position the Tooltip when it is displayed after the <code>showDelay</code>):</p> |
|
800 |
|
801 <pre class="code prettyprint">_onNodeMouseMove : function(e) { |
|
802 this._overTrigger(e.pageX, e.pageY); |
|
803 }</pre> |
|
804 |
|
805 |
|
806 <h3>Trigger Event Delegates: _enterTrigger, _leaveTrigger, _overTrigger</h3> |
|
807 |
|
808 <p>As seen above, the DOM event handlers delegate to the <code>_enterTrigger, _leaveTrigger and _overTrigger</code> methods to update the |
|
809 Tooltip state based on the currently active trigger node.</p> |
|
810 |
|
811 <p>The <code>_enterTrigger</code> method sets the current trigger state (which node is the current tooltip trigger, |
|
812 what the current mouse XY position is, etc.). The method also fires the <code>triggerEnter</code> event, whose default function actually handles |
|
813 showing the tooltip after the configured <code>showDelay</code> period. The <code>triggerEnter</code> event can be prevented by listeners, allowing |
|
814 users to prevent the tooltip from being shown if required. (<code>triggerEnter</code> listeners are passed the current trigger node and pageX, pageY mouse co-ordinates as event facade properties):</p> |
|
815 |
|
816 <pre class="code prettyprint">_enterTrigger : function(node, x, y) { |
|
817 this._setCurrentTrigger(node, x, y); |
|
818 this.fire("triggerEnter", null, node, x, y); |
|
819 }, |
|
820 |
|
821 _defTriggerEnterFn : function(e) { |
|
822 var node = e.node; |
|
823 if (!this.get("disabled")) { |
|
824 this._clearTimers(); |
|
825 var delay = (this.get("visible")) ? 0 : this.get("showDelay"); |
|
826 this._timers.show = Y.later(delay, this, this._showTooltip, [node]); |
|
827 } |
|
828 },</pre> |
|
829 |
|
830 |
|
831 <p>Similarly the <code>_leaveTrigger</code> method is invoked when the mouse leaves a trigger node, and clears any stored state, timers and listeners before setting up |
|
832 the <code>hideDelay</code> timer. It fires a <code>triggerLeave</code> event, but cannot be prevented, and has no default behavior to prevent:</p> |
|
833 |
|
834 <pre class="code prettyprint">_leaveTrigger : function(node) { |
|
835 this.fire("triggerLeave"); |
|
836 |
|
837 this._clearCurrentTrigger(); |
|
838 this._clearTimers(); |
|
839 |
|
840 this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip); |
|
841 },</pre> |
|
842 |
|
843 |
|
844 <p>As mentioned previously, the <code>_overTrigger</code> method simply stores the current mouse XY co-ordinates for use when the tooltip is shown:</p> |
|
845 |
|
846 <pre class="code prettyprint">_overTrigger : function(x, y) { |
|
847 this._currTrigger.mouseX = x; |
|
848 this._currTrigger.mouseY = y; |
|
849 }</pre> |
|
850 |
|
851 |
|
852 <h3>Setting Tooltip Content</h3> |
|
853 |
|
854 <p>Since the content for a tooltip is usually a function of the trigger node and not constant, <code>Tooltip</code> provides a number of ways to set the content.</p> |
|
855 |
|
856 <ol> |
|
857 <li>Setting the <code>content</code> attribute to a string or node. In this case, the value of the <code>content</code> attribute is used |
|
858 for all triggerNodes</li> |
|
859 <li>Setting the <code>content</code> attribute to an object literal, containing a map of triggerNode id to content. The content for a trigger node |
|
860 will be set using the map, when the tooltip is triggered for the node.</li> |
|
861 <li>Setting the title attribute on the trigger node. The value of the title attribute is used to set the tooltip content, |
|
862 when triggered for the node.</li> |
|
863 <li>By calling the <code>setTriggerContent</code> method to set content for a specific trigger node, in a <code>triggerEnter</code> event listener.</li> |
|
864 </ol> |
|
865 |
|
866 <p>The precedence of these methods is handled in the <code>_setTriggerContent</code> method, invoked when the mouse enters a trigger:</p> |
|
867 |
|
868 <pre class="code prettyprint">_setTriggerContent : function(node) { |
|
869 var content = this.get("content"); |
|
870 if (content && !(content instanceof Node || Lang.isString(content))) { |
|
871 content = content[node.get("id")] || node.getAttribute("title"); |
|
872 } |
|
873 this.setTriggerContent(content); |
|
874 }, |
|
875 |
|
876 setTriggerContent : function(content) { |
|
877 var contentBox = this.get("contentBox"); |
|
878 contentBox.set("innerHTML", ""); |
|
879 |
|
880 if (content) { |
|
881 if (content instanceof Node) { |
|
882 for (var i = 0, l = content.size(); i < l; ++i) { |
|
883 contentBox.appendChild(content.item(i)); |
|
884 } |
|
885 } else if (Lang.isString(content)) { |
|
886 contentBox.set("innerHTML", content); |
|
887 } |
|
888 } |
|
889 }</pre> |
|
890 |
|
891 |
|
892 <p>Calling the public <code>setTriggerContent</code> in a <code>triggerEvent</code> listener will over-ride content set using the <code>content</code> attribute or the trigger node's title value.</p> |
|
893 |
|
894 <h3>Using Tooltip</h3> |
|
895 |
|
896 <p>For this example, we set up 4 DIV elements which will act as tooltip triggers. They are all marked using a <code>yui-hastooltip</code> class, so that they can be queried using a simple selector, passed as the value for the <code>triggerNodes</code> attribute in the tooltip's constructor Also all 4 trigger nodes are contained in a wrapper DIV with <code>id="delegate"</code> which will act as the <code>delegate</code> node.</p> |
|
897 |
|
898 <pre class="code prettyprint">var tt = new Tooltip({ |
|
899 triggerNodes:".yui3-hastooltip", |
|
900 delegate: "#delegate", |
|
901 content: { |
|
902 tt3: "Tooltip 3 (from lookup)" |
|
903 }, |
|
904 shim:false, |
|
905 zIndex:2 |
|
906 }); |
|
907 tt.render();</pre> |
|
908 |
|
909 |
|
910 <p>The tooltip content for each of the trigger nodes is setup differently. The first trigger node uses the title attribute to set it's content. The third trigger node's content is set using the content map set in the constructor above. The second trigger node's content is set using a <code>triggerEnter</code> event listener and the <code>setTriggerContent</code> method as shown below:</p> |
|
911 |
|
912 <pre class="code prettyprint">tt.on("triggerEnter", function(e) { |
|
913 var node = e.node; |
|
914 if (node && node.get("id") == "tt2") { |
|
915 this.setTriggerContent("Tooltip 2 (from triggerEvent)"); |
|
916 } |
|
917 });</pre> |
|
918 |
|
919 |
|
920 <p>The fourth trigger node's content is set using it's title attribute, however it also has a <code>triggerEvent</code> listener which prevents the tooltip from being displayed for it, if the checkbox is checked.</p> |
|
921 |
|
922 <pre class="code prettyprint">var prevent = Y.one("#prevent"); |
|
923 tt.on("triggerEnter", function(e) { |
|
924 var node = e.node; |
|
925 if (prevent.get("checked")) { |
|
926 if (node && node.get("id") == "tt4") { |
|
927 e.preventDefault(); |
|
928 } |
|
929 } |
|
930 });</pre> |
|
931 |
|
932 |
|
933 <h2>Complete Example Source</h2> |
|
934 <pre class="code prettyprint"><div id="delegate"> |
|
935 <div class="yui3-hastooltip" title="Tooltip 1" id="tt1">Tooltip One <span>(content from title)</span></div> |
|
936 <div class="yui3-hastooltip" title="Tooltip 2" id="tt2">Tooltip Two <span>(content set in event listener)</span></div> |
|
937 <div class="yui3-hastooltip" title="Tooltip 3" id="tt3">Tooltip Three <span>(content from lookup)</span></div> |
|
938 <div class="yui3-hastooltip" title="Tooltip 4" id="tt4">Tooltip Four <span>(content from title)</span></div> |
|
939 <label><input type="checkbox" id="prevent" /> Prevent Tooltip Four</label> |
|
940 </div> |
|
941 |
|
942 <script type="text/javascript"> |
|
943 YUI().use("event-mouseenter", "widget", "widget-position", "widget-stack", function(Y) { |
|
944 var Lang = Y.Lang, |
|
945 Node = Y.Node, |
|
946 OX = -10000, |
|
947 OY = -10000; |
|
948 |
|
949 var Tooltip = Y.Base.create("tooltip", Y.Widget, [Y.WidgetPosition, Y.WidgetStack], { |
|
950 |
|
951 // PROTOTYPE METHODS/PROPERTIES |
|
952 |
|
953 /* |
|
954 * Initialization Code: Sets up privately used state |
|
955 * properties, and publishes the events Tooltip introduces |
|
956 */ |
|
957 initializer : function(config) { |
|
958 |
|
959 this._triggerClassName = this.getClassName("trigger"); |
|
960 |
|
961 // Currently bound trigger node information |
|
962 this._currTrigger = { |
|
963 node: null, |
|
964 title: null, |
|
965 mouseX: Tooltip.OFFSCREEN_X, |
|
966 mouseY: Tooltip.OFFSCREEN_Y |
|
967 }; |
|
968 |
|
969 // Event handles - mouse over is set on the delegate |
|
970 // element, mousemove and mouseleave are set on the trigger node |
|
971 this._eventHandles = { |
|
972 delegate: null, |
|
973 trigger: { |
|
974 mouseMove : null, |
|
975 mouseOut: null |
|
976 } |
|
977 }; |
|
978 |
|
979 // Show/hide timers |
|
980 this._timers = { |
|
981 show: null, |
|
982 hide: null |
|
983 }; |
|
984 |
|
985 // Publish events introduced by Tooltip. Note the triggerEnter event is preventable, |
|
986 // with the default behavior defined in the _defTriggerEnterFn method |
|
987 this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true}); |
|
988 this.publish("triggerLeave", {preventable:false}); |
|
989 }, |
|
990 |
|
991 /* |
|
992 * Destruction Code: Clears event handles, timers, |
|
993 * and current trigger information |
|
994 */ |
|
995 destructor : function() { |
|
996 this._clearCurrentTrigger(); |
|
997 this._clearTimers(); |
|
998 this._clearHandles(); |
|
999 }, |
|
1000 |
|
1001 /* |
|
1002 * bindUI is used to bind attribute change and dom event |
|
1003 * listeners |
|
1004 */ |
|
1005 bindUI : function() { |
|
1006 this.after("delegateChange", this._afterSetDelegate); |
|
1007 this.after("nodesChange", this._afterSetNodes); |
|
1008 |
|
1009 this._bindDelegate(); |
|
1010 }, |
|
1011 |
|
1012 /* |
|
1013 * syncUI is used to update the rendered DOM, based on the current |
|
1014 * Tooltip state |
|
1015 */ |
|
1016 syncUI : function() { |
|
1017 this._uiSetNodes(this.get("triggerNodes")); |
|
1018 }, |
|
1019 |
|
1020 /* |
|
1021 * Public method, which can be used by triggerEvent event listeners |
|
1022 * to set the content of the tooltip for the current trigger node |
|
1023 */ |
|
1024 setTriggerContent : function(content) { |
|
1025 var contentBox = this.get("contentBox"); |
|
1026 contentBox.set("innerHTML", ""); |
|
1027 |
|
1028 if (content) { |
|
1029 if (content instanceof Node) { |
|
1030 for (var i = 0, l = content.size(); i < l; ++i) { |
|
1031 contentBox.appendChild(content.item(i)); |
|
1032 } |
|
1033 } else if (Lang.isString(content)) { |
|
1034 contentBox.set("innerHTML", content); |
|
1035 } |
|
1036 } |
|
1037 }, |
|
1038 |
|
1039 /* |
|
1040 * Default attribute change listener for |
|
1041 * the triggerNodes attribute |
|
1042 */ |
|
1043 _afterSetNodes : function(e) { |
|
1044 this._uiSetNodes(e.newVal); |
|
1045 }, |
|
1046 |
|
1047 /* |
|
1048 * Default attribute change listener for |
|
1049 * the delegate attribute |
|
1050 */ |
|
1051 _afterSetDelegate : function(e) { |
|
1052 this._bindDelegate(e.newVal); |
|
1053 }, |
|
1054 |
|
1055 /* |
|
1056 * Updates the rendered DOM to reflect the |
|
1057 * set of trigger nodes passed in |
|
1058 */ |
|
1059 _uiSetNodes : function(nodes) { |
|
1060 if (this._triggerNodes) { |
|
1061 this._triggerNodes.removeClass(this._triggerClassName); |
|
1062 } |
|
1063 |
|
1064 if (nodes) { |
|
1065 this._triggerNodes = nodes; |
|
1066 this._triggerNodes.addClass(this._triggerClassName); |
|
1067 } |
|
1068 }, |
|
1069 |
|
1070 /* |
|
1071 * Attaches the default mouseover DOM listener to the |
|
1072 * current delegate node |
|
1073 */ |
|
1074 _bindDelegate : function() { |
|
1075 var eventHandles = this._eventHandles; |
|
1076 |
|
1077 if (eventHandles.delegate) { |
|
1078 eventHandles.delegate.detach(); |
|
1079 eventHandles.delegate = null; |
|
1080 } |
|
1081 eventHandles.delegate = Y.delegate("mouseenter", Y.bind(this._onNodeMouseEnter, this), this.get("delegate"), "." + this._triggerClassName); |
|
1082 }, |
|
1083 |
|
1084 /* |
|
1085 * Default mouse enter DOM event listener. |
|
1086 * |
|
1087 * Delegates to the _enterTrigger method, |
|
1088 * if the mouseover enters a trigger node. |
|
1089 */ |
|
1090 _onNodeMouseEnter : function(e) { |
|
1091 var node = e.currentTarget; |
|
1092 if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) { |
|
1093 this._enterTrigger(node, e.pageX, e.pageY); |
|
1094 } |
|
1095 }, |
|
1096 |
|
1097 /* |
|
1098 * Default mouse leave DOM event listener |
|
1099 * |
|
1100 * Delegates to _leaveTrigger if the mouse |
|
1101 * leaves the current trigger node |
|
1102 */ |
|
1103 _onNodeMouseLeave : function(e) { |
|
1104 this._leaveTrigger(e.currentTarget); |
|
1105 }, |
|
1106 |
|
1107 /* |
|
1108 * Default mouse move DOM event listener |
|
1109 */ |
|
1110 _onNodeMouseMove : function(e) { |
|
1111 this._overTrigger(e.pageX, e.pageY); |
|
1112 }, |
|
1113 |
|
1114 /* |
|
1115 * Default handler invoked when the mouse enters |
|
1116 * a trigger node. Fires the triggerEnter |
|
1117 * event which can be prevented by listeners to |
|
1118 * show the tooltip from being displayed. |
|
1119 */ |
|
1120 _enterTrigger : function(node, x, y) { |
|
1121 this._setCurrentTrigger(node, x, y); |
|
1122 this.fire("triggerEnter", {node:node, pageX:x, pageY:y}); |
|
1123 }, |
|
1124 |
|
1125 /* |
|
1126 * Default handler for the triggerEvent event, |
|
1127 * which will setup the timer to display the tooltip, |
|
1128 * if the default handler has not been prevented. |
|
1129 */ |
|
1130 _defTriggerEnterFn : function(e) { |
|
1131 var node = e.node; |
|
1132 if (!this.get("disabled")) { |
|
1133 this._clearTimers(); |
|
1134 var delay = (this.get("visible")) ? 0 : this.get("showDelay"); |
|
1135 this._timers.show = Y.later(delay, this, this._showTooltip, [node]); |
|
1136 } |
|
1137 }, |
|
1138 |
|
1139 /* |
|
1140 * Default handler invoked when the mouse leaves |
|
1141 * the current trigger node. Fires the triggerLeave |
|
1142 * event and sets up the hide timer |
|
1143 */ |
|
1144 _leaveTrigger : function(node) { |
|
1145 this.fire("triggerLeave"); |
|
1146 |
|
1147 this._clearCurrentTrigger(); |
|
1148 this._clearTimers(); |
|
1149 |
|
1150 this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip); |
|
1151 }, |
|
1152 |
|
1153 /* |
|
1154 * Default handler invoked for mousemove events |
|
1155 * on the trigger node. Stores the current mouse |
|
1156 * x, y positions |
|
1157 */ |
|
1158 _overTrigger : function(x, y) { |
|
1159 this._currTrigger.mouseX = x; |
|
1160 this._currTrigger.mouseY = y; |
|
1161 }, |
|
1162 |
|
1163 /* |
|
1164 * Shows the tooltip, after moving it to the current mouse |
|
1165 * position. |
|
1166 */ |
|
1167 _showTooltip : function(node) { |
|
1168 var x = this._currTrigger.mouseX; |
|
1169 var y = this._currTrigger.mouseY; |
|
1170 |
|
1171 this.move(x + Tooltip.OFFSET_X, y + Tooltip.OFFSET_Y); |
|
1172 |
|
1173 this.show(); |
|
1174 this._clearTimers(); |
|
1175 |
|
1176 this._timers.hide = Y.later(this.get("autoHideDelay"), this, this._hideTooltip); |
|
1177 }, |
|
1178 |
|
1179 /* |
|
1180 * Hides the tooltip, after clearing existing timers. |
|
1181 */ |
|
1182 _hideTooltip : function() { |
|
1183 this._clearTimers(); |
|
1184 this.hide(); |
|
1185 }, |
|
1186 |
|
1187 /* |
|
1188 * Set the rendered content of the tooltip for the current |
|
1189 * trigger, based on (in order of precedence): |
|
1190 * |
|
1191 * a). The string/node content attribute value |
|
1192 * b). From the content lookup map if it is set, or |
|
1193 * c). From the title attribute if set. |
|
1194 */ |
|
1195 _setTriggerContent : function(node) { |
|
1196 var content = this.get("content"); |
|
1197 if (content && !(content instanceof Node || Lang.isString(content))) { |
|
1198 content = content[node.get("id")] || node.getAttribute("title"); |
|
1199 } |
|
1200 this.setTriggerContent(content); |
|
1201 }, |
|
1202 |
|
1203 /* |
|
1204 * Set the currently bound trigger node information, clearing |
|
1205 * out the title attribute if set and setting up mousemove/out |
|
1206 * listeners. |
|
1207 */ |
|
1208 _setCurrentTrigger : function(node, x, y) { |
|
1209 |
|
1210 var currTrigger = this._currTrigger, |
|
1211 triggerHandles = this._eventHandles.trigger; |
|
1212 |
|
1213 this._setTriggerContent(node); |
|
1214 |
|
1215 triggerHandles.mouseMove = Y.on("mousemove", Y.bind(this._onNodeMouseMove, this), node); |
|
1216 triggerHandles.mouseOut = Y.on("mouseleave", Y.bind(this._onNodeMouseLeave, this), node); |
|
1217 |
|
1218 var title = node.getAttribute("title"); |
|
1219 node.setAttribute("title", ""); |
|
1220 |
|
1221 currTrigger.mouseX = x; |
|
1222 currTrigger.mouseY = y; |
|
1223 currTrigger.node = node; |
|
1224 currTrigger.title = title; |
|
1225 }, |
|
1226 |
|
1227 /* |
|
1228 * Clear out the current trigger state, restoring |
|
1229 * the title attribute on the trigger node, |
|
1230 * if it was originally set. |
|
1231 */ |
|
1232 _clearCurrentTrigger : function() { |
|
1233 |
|
1234 var currTrigger = this._currTrigger, |
|
1235 triggerHandles = this._eventHandles.trigger; |
|
1236 |
|
1237 if (currTrigger.node) { |
|
1238 var node = currTrigger.node; |
|
1239 var title = currTrigger.title || ""; |
|
1240 |
|
1241 currTrigger.node = null; |
|
1242 currTrigger.title = ""; |
|
1243 |
|
1244 triggerHandles.mouseMove.detach(); |
|
1245 triggerHandles.mouseOut.detach(); |
|
1246 triggerHandles.mouseMove = null; |
|
1247 triggerHandles.mouseOut = null; |
|
1248 |
|
1249 node.setAttribute("title", title); |
|
1250 } |
|
1251 }, |
|
1252 |
|
1253 /* |
|
1254 * Cancel any existing show/hide timers |
|
1255 */ |
|
1256 _clearTimers : function() { |
|
1257 var timers = this._timers; |
|
1258 if (timers.hide) { |
|
1259 timers.hide.cancel(); |
|
1260 timers.hide = null; |
|
1261 } |
|
1262 if (timers.show) { |
|
1263 timers.show.cancel(); |
|
1264 timers.show = null; |
|
1265 } |
|
1266 }, |
|
1267 |
|
1268 /* |
|
1269 * Detach any stored event handles |
|
1270 */ |
|
1271 _clearHandles : function() { |
|
1272 var eventHandles = this._eventHandles; |
|
1273 |
|
1274 if (eventHandles.delegate) { |
|
1275 this._eventHandles.delegate.detach(); |
|
1276 } |
|
1277 if (eventHandles.trigger.mouseOut) { |
|
1278 eventHandles.trigger.mouseOut.detach(); |
|
1279 } |
|
1280 if (eventHandles.trigger.mouseMove) { |
|
1281 eventHandles.trigger.mouseMove.detach(); |
|
1282 } |
|
1283 } |
|
1284 }, { |
|
1285 |
|
1286 // STATIC METHODS/PROPERTIES |
|
1287 |
|
1288 OFFSET_X : 15, |
|
1289 OFFSET_Y : 15, |
|
1290 OFFSCREEN_X : OX, |
|
1291 OFFSCREEN_Y : OY, |
|
1292 |
|
1293 ATTRS : { |
|
1294 |
|
1295 /* |
|
1296 * The tooltip content. This can either be a fixed content value, |
|
1297 * or a map of id-to-values, designed to be used when a single |
|
1298 * tooltip is mapped to multiple trigger elements. |
|
1299 */ |
|
1300 content : { |
|
1301 value: null |
|
1302 }, |
|
1303 |
|
1304 /* |
|
1305 * The set of nodes to bind to the tooltip instance. Can be a string, |
|
1306 * or a node instance. |
|
1307 */ |
|
1308 triggerNodes : { |
|
1309 value: null, |
|
1310 setter: function(val) { |
|
1311 if (val && Lang.isString(val)) { |
|
1312 val = Node.all(val); |
|
1313 } |
|
1314 return val; |
|
1315 } |
|
1316 }, |
|
1317 |
|
1318 /* |
|
1319 * The delegate node to which event listeners should be attached. |
|
1320 * This node should be an ancestor of all trigger nodes bound |
|
1321 * to the instance. By default the document is used. |
|
1322 */ |
|
1323 delegate : { |
|
1324 value: null, |
|
1325 setter: function(val) { |
|
1326 return Y.one(val) || Y.one("document"); |
|
1327 } |
|
1328 }, |
|
1329 |
|
1330 /* |
|
1331 * The time to wait, after the mouse enters the trigger node, |
|
1332 * to display the tooltip |
|
1333 */ |
|
1334 showDelay : { |
|
1335 value:250 |
|
1336 }, |
|
1337 |
|
1338 /* |
|
1339 * The time to wait, after the mouse leaves the trigger node, |
|
1340 * to hide the tooltip |
|
1341 */ |
|
1342 hideDelay : { |
|
1343 value:10 |
|
1344 }, |
|
1345 |
|
1346 /* |
|
1347 * The time to wait, after the tooltip is first displayed for |
|
1348 * a trigger node, to hide it, if the mouse has not left the |
|
1349 * trigger node |
|
1350 */ |
|
1351 autoHideDelay : { |
|
1352 value:2000 |
|
1353 }, |
|
1354 |
|
1355 /* |
|
1356 * Override the default visibility set by the widget base class |
|
1357 */ |
|
1358 visible : { |
|
1359 value:false |
|
1360 }, |
|
1361 |
|
1362 /* |
|
1363 * Override the default XY value set by the widget base class, |
|
1364 * to position the tooltip offscreen |
|
1365 */ |
|
1366 xy: { |
|
1367 value:[OX, OY] |
|
1368 } |
|
1369 } |
|
1370 }); |
|
1371 |
|
1372 var tt = new Tooltip({ |
|
1373 triggerNodes:".yui3-hastooltip", |
|
1374 delegate: "#delegate", |
|
1375 content: { |
|
1376 tt3: "Tooltip 3 (from lookup)" |
|
1377 }, |
|
1378 shim:false, |
|
1379 zIndex:2 |
|
1380 }); |
|
1381 tt.render(); |
|
1382 |
|
1383 tt.on("triggerEnter", function(e) { |
|
1384 var node = e.node; |
|
1385 if (node && node.get("id") == "tt2") { |
|
1386 this.setTriggerContent("Tooltip 2 (from triggerEvent)"); |
|
1387 } |
|
1388 }); |
|
1389 |
|
1390 var prevent = Y.one("#prevent"); |
|
1391 tt.on("triggerEnter", function(e) { |
|
1392 var node = e.node; |
|
1393 if (prevent.get("checked")) { |
|
1394 if (node && node.get("id") == "tt4") { |
|
1395 e.preventDefault(); |
|
1396 } |
|
1397 } |
|
1398 }); |
|
1399 }); |
|
1400 </script></pre> |
|
1401 |
|
1402 </div> |
|
1403 </div> |
|
1404 </div> |
|
1405 |
|
1406 <div class="yui3-u-1-4"> |
|
1407 <div class="sidebar"> |
|
1408 |
|
1409 |
|
1410 |
|
1411 <div class="sidebox"> |
|
1412 <div class="hd"> |
|
1413 <h2 class="no-toc">Examples</h2> |
|
1414 </div> |
|
1415 |
|
1416 <div class="bd"> |
|
1417 <ul class="examples"> |
|
1418 |
|
1419 |
|
1420 <li data-description="Shows how to extend the base widget class, to create your own Widgets."> |
|
1421 <a href="widget-extend.html">Extending the Base Widget Class</a> |
|
1422 </li> |
|
1423 |
|
1424 |
|
1425 |
|
1426 <li data-description="Shows how to use Base.create and mix/match extensions to create custom Widget classes."> |
|
1427 <a href="widget-build.html">Creating Custom Widget Classes With Extensions</a> |
|
1428 </li> |
|
1429 |
|
1430 |
|
1431 |
|
1432 <li data-description="Shows how to create an IO plugin for Widget."> |
|
1433 <a href="widget-plugin.html">Creating a Widget Plugin</a> |
|
1434 </li> |
|
1435 |
|
1436 |
|
1437 |
|
1438 <li data-description="Shows how to extend the Widget class, and add WidgetPosition and WidgetStack to create a Tooltip widget class."> |
|
1439 <a href="widget-tooltip.html">Creating a Simple Tooltip Widget With Extensions</a> |
|
1440 </li> |
|
1441 |
|
1442 |
|
1443 |
|
1444 <li data-description="Shows how to extend the Widget class, and add WidgetParent and WidgetChild to create a simple ListBox widget."> |
|
1445 <a href="widget-parentchild-listbox.html">Creating a Hierarchical ListBox Widget</a> |
|
1446 </li> |
|
1447 |
|
1448 |
|
1449 </ul> |
|
1450 </div> |
|
1451 </div> |
|
1452 |
|
1453 |
|
1454 |
|
1455 </div> |
|
1456 </div> |
|
1457 </div> |
|
1458 </div> |
|
1459 |
|
1460 <script src="../assets/vendor/prettify/prettify-min.js"></script> |
|
1461 <script>prettyPrint();</script> |
|
1462 |
|
1463 <script> |
|
1464 YUI.Env.Tests = { |
|
1465 examples: [], |
|
1466 project: '../assets', |
|
1467 assets: '../assets/widget', |
|
1468 name: 'widget-tooltip', |
|
1469 title: 'Creating a Simple Tooltip Widget With Extensions', |
|
1470 newWindow: '', |
|
1471 auto: false |
|
1472 }; |
|
1473 YUI.Env.Tests.examples.push('widget-extend'); |
|
1474 YUI.Env.Tests.examples.push('widget-build'); |
|
1475 YUI.Env.Tests.examples.push('widget-plugin'); |
|
1476 YUI.Env.Tests.examples.push('widget-tooltip'); |
|
1477 YUI.Env.Tests.examples.push('widget-parentchild-listbox'); |
|
1478 |
|
1479 </script> |
|
1480 <script src="../assets/yui/test-runner.js"></script> |
|
1481 |
|
1482 |
|
1483 |
|
1484 </body> |
|
1485 </html> |