5
<title>Example: Creating a Simple Tooltip Widget With Extensions</title>
6
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Maven+Pro:400,700">
7
<link rel="stylesheet" href="../../build/cssgrids/grids-min.css">
8
<link rel="stylesheet" href="../assets/css/main.css">
9
<link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
10
<script src="../../build/yui/yui-min.js"></script>
15
<h1>Example: Creating a Simple Tooltip Widget With Extensions</h1>
20
<div class="yui3-u-3-4">
22
<div class="content"><style type="text/css" scoped>
27
.yui3-tooltip-content {
30
border-color: #D4C237 #A6982B #A6982B #A6982B;
33
background-color: #FFEE69;
36
.yui3-tooltip-hidden {
41
border:1px solid #243356;
42
background-color:#406ED9;
50
div.yui3-hastooltip span {
56
.yui3-tooltip-content strong {
62
<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>
67
<div class="yui3-hastooltip" title="Tooltip 1" id="tt1">Tooltip One <span>(content from title)</span></div>
68
<div class="yui3-hastooltip" title="Tooltip 2" id="tt2">Tooltip Two <span>(content set in event listener)</span></div>
69
<div class="yui3-hastooltip" title="Tooltip 3" id="tt3">Tooltip Three <span>(content from lookup)</span></div>
70
<div class="yui3-hastooltip" title="Tooltip 4" id="tt4">Tooltip Four <span>(content from title)</span></div>
71
<label><input type="checkbox" id="prevent" /> Prevent Tooltip Four</label>
74
<script type="text/javascript">
75
YUI().use("event-mouseenter", "widget", "widget-position", "widget-stack", function(Y) {
81
var Tooltip = Y.Base.create("tooltip", Y.Widget, [Y.WidgetPosition, Y.WidgetStack], {
83
// PROTOTYPE METHODS/PROPERTIES
86
* Initialization Code: Sets up privately used state
87
* properties, and publishes the events Tooltip introduces
89
initializer : function(config) {
91
this._triggerClassName = this.getClassName("trigger");
93
// Currently bound trigger node information
97
mouseX: Tooltip.OFFSCREEN_X,
98
mouseY: Tooltip.OFFSCREEN_Y
101
// Event handles - mouse over is set on the delegate
102
// element, mousemove and mouseleave are set on the trigger node
103
this._eventHandles = {
117
// Publish events introduced by Tooltip. Note the triggerEnter event is preventable,
118
// with the default behavior defined in the _defTriggerEnterFn method
119
this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true});
120
this.publish("triggerLeave", {preventable:false});
124
* Destruction Code: Clears event handles, timers,
125
* and current trigger information
127
destructor : function() {
128
this._clearCurrentTrigger();
130
this._clearHandles();
134
* bindUI is used to bind attribute change and dom event
137
bindUI : function() {
138
this.after("delegateChange", this._afterSetDelegate);
139
this.after("nodesChange", this._afterSetNodes);
141
this._bindDelegate();
145
* syncUI is used to update the rendered DOM, based on the current
148
syncUI : function() {
149
this._uiSetNodes(this.get("triggerNodes"));
153
* Public method, which can be used by triggerEvent event listeners
154
* to set the content of the tooltip for the current trigger node
156
setTriggerContent : function(content) {
157
var contentBox = this.get("contentBox");
158
contentBox.set("innerHTML", "");
161
if (content instanceof Node) {
162
for (var i = 0, l = content.size(); i < l; ++i) {
163
contentBox.appendChild(content.item(i));
165
} else if (Lang.isString(content)) {
166
contentBox.set("innerHTML", content);
172
* Default attribute change listener for
173
* the triggerNodes attribute
175
_afterSetNodes : function(e) {
176
this._uiSetNodes(e.newVal);
180
* Default attribute change listener for
181
* the delegate attribute
183
_afterSetDelegate : function(e) {
184
this._bindDelegate(e.newVal);
188
* Updates the rendered DOM to reflect the
189
* set of trigger nodes passed in
191
_uiSetNodes : function(nodes) {
192
if (this._triggerNodes) {
193
this._triggerNodes.removeClass(this._triggerClassName);
197
this._triggerNodes = nodes;
198
this._triggerNodes.addClass(this._triggerClassName);
203
* Attaches the default mouseover DOM listener to the
204
* current delegate node
206
_bindDelegate : function() {
207
var eventHandles = this._eventHandles;
209
if (eventHandles.delegate) {
210
eventHandles.delegate.detach();
211
eventHandles.delegate = null;
213
eventHandles.delegate = Y.delegate("mouseenter", Y.bind(this._onNodeMouseEnter, this), this.get("delegate"), "." + this._triggerClassName);
217
* Default mouse enter DOM event listener.
219
* Delegates to the _enterTrigger method,
220
* if the mouseover enters a trigger node.
222
_onNodeMouseEnter : function(e) {
223
var node = e.currentTarget;
224
if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) {
225
this._enterTrigger(node, e.pageX, e.pageY);
230
* Default mouse leave DOM event listener
232
* Delegates to _leaveTrigger if the mouse
233
* leaves the current trigger node
235
_onNodeMouseLeave : function(e) {
236
this._leaveTrigger(e.currentTarget);
240
* Default mouse move DOM event listener
242
_onNodeMouseMove : function(e) {
243
this._overTrigger(e.pageX, e.pageY);
247
* Default handler invoked when the mouse enters
248
* a trigger node. Fires the triggerEnter
249
* event which can be prevented by listeners to
250
* show the tooltip from being displayed.
252
_enterTrigger : function(node, x, y) {
253
this._setCurrentTrigger(node, x, y);
254
this.fire("triggerEnter", {node:node, pageX:x, pageY:y});
258
* Default handler for the triggerEvent event,
259
* which will setup the timer to display the tooltip,
260
* if the default handler has not been prevented.
262
_defTriggerEnterFn : function(e) {
264
if (!this.get("disabled")) {
266
var delay = (this.get("visible")) ? 0 : this.get("showDelay");
267
this._timers.show = Y.later(delay, this, this._showTooltip, [node]);
272
* Default handler invoked when the mouse leaves
273
* the current trigger node. Fires the triggerLeave
274
* event and sets up the hide timer
276
_leaveTrigger : function(node) {
277
this.fire("triggerLeave");
279
this._clearCurrentTrigger();
282
this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip);
286
* Default handler invoked for mousemove events
287
* on the trigger node. Stores the current mouse
290
_overTrigger : function(x, y) {
291
this._currTrigger.mouseX = x;
292
this._currTrigger.mouseY = y;
296
* Shows the tooltip, after moving it to the current mouse
299
_showTooltip : function(node) {
300
var x = this._currTrigger.mouseX;
301
var y = this._currTrigger.mouseY;
303
this.move(x + Tooltip.OFFSET_X, y + Tooltip.OFFSET_Y);
308
this._timers.hide = Y.later(this.get("autoHideDelay"), this, this._hideTooltip);
312
* Hides the tooltip, after clearing existing timers.
314
_hideTooltip : function() {
320
* Set the rendered content of the tooltip for the current
321
* trigger, based on (in order of precedence):
323
* a). The string/node content attribute value
324
* b). From the content lookup map if it is set, or
325
* c). From the title attribute if set.
327
_setTriggerContent : function(node) {
328
var content = this.get("content");
329
if (content && !(content instanceof Node || Lang.isString(content))) {
330
content = content[node.get("id")] || node.getAttribute("title");
332
this.setTriggerContent(content);
336
* Set the currently bound trigger node information, clearing
337
* out the title attribute if set and setting up mousemove/out
340
_setCurrentTrigger : function(node, x, y) {
342
var currTrigger = this._currTrigger,
343
triggerHandles = this._eventHandles.trigger;
345
this._setTriggerContent(node);
347
triggerHandles.mouseMove = Y.on("mousemove", Y.bind(this._onNodeMouseMove, this), node);
348
triggerHandles.mouseOut = Y.on("mouseleave", Y.bind(this._onNodeMouseLeave, this), node);
350
var title = node.getAttribute("title");
351
node.setAttribute("title", "");
353
currTrigger.mouseX = x;
354
currTrigger.mouseY = y;
355
currTrigger.node = node;
356
currTrigger.title = title;
360
* Clear out the current trigger state, restoring
361
* the title attribute on the trigger node,
362
* if it was originally set.
364
_clearCurrentTrigger : function() {
366
var currTrigger = this._currTrigger,
367
triggerHandles = this._eventHandles.trigger;
369
if (currTrigger.node) {
370
var node = currTrigger.node;
371
var title = currTrigger.title || "";
373
currTrigger.node = null;
374
currTrigger.title = "";
376
triggerHandles.mouseMove.detach();
377
triggerHandles.mouseOut.detach();
378
triggerHandles.mouseMove = null;
379
triggerHandles.mouseOut = null;
381
node.setAttribute("title", title);
386
* Cancel any existing show/hide timers
388
_clearTimers : function() {
389
var timers = this._timers;
391
timers.hide.cancel();
395
timers.show.cancel();
401
* Detach any stored event handles
403
_clearHandles : function() {
404
var eventHandles = this._eventHandles;
406
if (eventHandles.delegate) {
407
this._eventHandles.delegate.detach();
409
if (eventHandles.trigger.mouseOut) {
410
eventHandles.trigger.mouseOut.detach();
412
if (eventHandles.trigger.mouseMove) {
413
eventHandles.trigger.mouseMove.detach();
418
// STATIC METHODS/PROPERTIES
428
* The tooltip content. This can either be a fixed content value,
429
* or a map of id-to-values, designed to be used when a single
430
* tooltip is mapped to multiple trigger elements.
437
* The set of nodes to bind to the tooltip instance. Can be a string,
438
* or a node instance.
442
setter: function(val) {
443
if (val && Lang.isString(val)) {
451
* The delegate node to which event listeners should be attached.
452
* This node should be an ancestor of all trigger nodes bound
453
* to the instance. By default the document is used.
457
setter: function(val) {
458
return Y.one(val) || Y.one("document");
463
* The time to wait, after the mouse enters the trigger node,
464
* to display the tooltip
471
* The time to wait, after the mouse leaves the trigger node,
472
* to hide the tooltip
479
* The time to wait, after the tooltip is first displayed for
480
* a trigger node, to hide it, if the mouse has not left the
488
* Override the default visibility set by the widget base class
495
* Override the default XY value set by the widget base class,
496
* to position the tooltip offscreen
504
var tt = new Tooltip({
505
triggerNodes:".yui3-hastooltip",
506
delegate: "#delegate",
508
tt3: "Tooltip 3 (from lookup)"
515
tt.on("triggerEnter", function(e) {
517
if (node && node.get("id") == "tt2") {
518
this.setTriggerContent("Tooltip 2 (from triggerEvent)");
522
var prevent = Y.one("#prevent");
523
tt.on("triggerEnter", function(e) {
525
if (prevent.get("checked")) {
526
if (node && node.get("id") == "tt4") {
536
<h2>Creating A Tooltip Widget Class</h2>
538
<h3>Basic Class Structure</h3>
540
<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>
545
<li>Set up the constructor to invoke the superclass constructor</li>
546
<li>Define a <code>NAME</code> property, to identify the class</li>
547
<li>Define the default attribute configuration, using the <code>ATTRS</code> property</li>
548
<li>Implement prototype methods</li>
551
<p>This basic structure is shown below:</p>
553
<pre class="code prettyprint">/*
554
* Required NAME static field, used to identify the Widget class and
555
* used as an event prefix, to generate class names etc. (set to the
556
* class name in camel case).
558
Tooltip.NAME = "tooltip";
560
/* Default Tooltip Attributes */
564
* The tooltip content. This can either be a fixed content value,
565
* or a map of id-to-values, designed to be used when a single
566
* tooltip is mapped to multiple trigger elements.
573
* The set of nodes to bind to the tooltip instance. Can be a string,
574
* or a node instance.
578
setter: function(val) {
579
if (val && Lang.isString(val)) {
587
* The delegate node to which event listeners should be attached.
588
* This node should be an ancestor of all trigger nodes bound
589
* to the instance. By default the document is used.
593
setter: function(val) {
594
return Y.one(val) || Y.one("document");
599
* The time to wait, after the mouse enters the trigger node,
600
* to display the tooltip
607
* The time to wait, after the mouse leaves the trigger node,
608
* to hide the tooltip
615
* The time to wait, after the tooltip is first displayed for
616
* a trigger node, to hide it, if the mouse has not left the
624
* Override the default visibility set by the widget base class
631
* Override the default XY value set by the widget base class,
632
* to position the tooltip offscreen
635
value:[Tooltip.OFFSCREEN_X, Tooltip.OFFSCREEN_Y]
639
Y.extend(Tooltip, Y.Widget, {
640
// Prototype methods/properties
644
<h3>Adding WidgetPosition and WidgetStack Extension Support</h3>
646
<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
647
<code>Base.create</code> to create a new <code>Tooltip</code> class with this support:</p>
649
<pre class="code prettyprint">var Tooltip = Y.Base.create("tooltip", Y.Widget, [Y.WidgetPosition, Y.WidgetStack],
650
{ ... prototype properties ... },
651
{ ... static properties ... },</pre>
654
<h3>Lifecycle Methods: initializer, destructor</h3>
656
<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
657
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>
659
<pre class="code prettyprint">initializer : function(config) {
661
this._triggerClassName = this.getClassName("trigger");
663
// Currently bound trigger node information
664
this._currTrigger = {
667
mouseX: Tooltip.OFFSCREEN_X,
668
mouseY: Tooltip.OFFSCREEN_Y
671
// Event handles - mouse over is set on the delegate
672
// element, mousemove and mouseleave are set on the trigger node
673
this._eventHandles = {
681
// Show/hide timers
687
// Publish events introduced by Tooltip. Note the triggerEnter event is preventable,
688
// with the default behavior defined in the _defTriggerEnterFn method
689
this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true});
690
this.publish("triggerLeave", {preventable:false});
694
<p>The <code>destructor</code> is used to clear out stored state, detach any event handles and clear out the show/hide timers:</p>
696
<pre class="code prettyprint">destructor : function() {
697
this._clearCurrentTrigger();
699
this._clearHandles();
703
<h3>Lifecycle Methods: bindUI, syncUI</h3>
705
<p>The <code>bindUI</code> and <code>syncUI</code> are invoked by the base Widget class' <code>renderer</code> method.</p>
707
<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
708
the DOM listeners required to enable the UI for interaction.</p>
710
<p><code>syncUI</code> is used to sync the UI state from the current widget state, when initially rendered.</p>
712
<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
713
tooltip does not have any additional elements in needs to add to the DOM, outside of the default Widget boundingBox and contentBox.</p>
715
<pre class="code prettyprint">bindUI : function() {
716
this.after("delegateChange", this._afterSetDelegate);
717
this.after("nodesChange", this._afterSetNodes);
719
this._bindDelegate();
722
syncUI : function() {
723
this._uiSetNodes(this.get("triggerNodes"));
727
<h3>Attribute Supporting Methods</h3>
729
<p>Tooltip's <code>triggerNodes</code>, which defines the set of nodes which should trigger this tooltip instance,
730
has a couple of supporting methods associated with it.</p>
732
<p>The <code>_afterSetNodes</code> method is the default attribute change event handler for the <code>triggerNodes</code>
733
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>
735
<pre class="code prettyprint">_afterSetNodes : function(e) {
736
this._uiSetNodes(e.newVal);
739
_uiSetNodes : function(nodes) {
740
if (this._triggerNodes) {
741
this._triggerNodes.removeClass(this._triggerClassName);
745
this._triggerNodes = nodes;
746
this._triggerNodes.addClass(this._triggerClassName);
751
<p>Similarly the <code>_afterSetDelegate</code> method is the default attribute change listener for the <code>delegate</code> attribute,
752
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,
753
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>
755
<pre class="code prettyprint">_afterSetDelegate : function(e) {
756
this._bindDelegate(e.newVal);
759
_bindDelegate : function() {
760
var eventHandles = this._eventHandles;
762
if (eventHandles.delegate) {
763
eventHandles.delegate.detach();
764
eventHandles.delegate = null;
766
eventHandles.delegate = Y.delegate("mouseenter", Y.bind(this._onNodeMouseEnter, this), this.get("delegate"), "." + this._triggerClassName);
770
<h3>DOM Event Handlers</h3>
772
<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>
774
<pre class="code prettyprint">_onNodeMouseEnter : function(e) {
775
var node = e.currentTarget;
776
if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) {
777
this._enterTrigger(node, e.pageX, e.pageY);
782
<p>Since the <code>mouseenter</code> implementation doesn't invoke it's listeners for <code>mouseover</code> events generated from elements nested
783
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
784
the <code>_enterTrigger</code> method to setup the current trigger state and attach mousemove and mouseleave listeners on the current trigger node.</p>
786
<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>
788
<pre class="code prettyprint">_onNodeMouseLeave : function(e) {
789
this._leaveTrigger(e.currentTarget);
793
<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>
795
<pre class="code prettyprint">_onNodeMouseMove : function(e) {
796
this._overTrigger(e.pageX, e.pageY);
800
<h3>Trigger Event Delegates: _enterTrigger, _leaveTrigger, _overTrigger</h3>
802
<p>As seen above, the DOM event handlers delegate to the <code>_enterTrigger, _leaveTrigger and _overTrigger</code> methods to update the
803
Tooltip state based on the currently active trigger node.</p>
805
<p>The <code>_enterTrigger</code> method sets the current trigger state (which node is the current tooltip trigger,
806
what the current mouse XY position is, etc.). The method also fires the <code>triggerEnter</code> event, whose default function actually handles
807
showing the tooltip after the configured <code>showDelay</code> period. The <code>triggerEnter</code> event can be prevented by listeners, allowing
808
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>
810
<pre class="code prettyprint">_enterTrigger : function(node, x, y) {
811
this._setCurrentTrigger(node, x, y);
812
this.fire("triggerEnter", null, node, x, y);
815
_defTriggerEnterFn : function(e) {
817
if (!this.get("disabled")) {
819
var delay = (this.get("visible")) ? 0 : this.get("showDelay");
820
this._timers.show = Y.later(delay, this, this._showTooltip, [node]);
825
<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
826
the <code>hideDelay</code> timer. It fires a <code>triggerLeave</code> event, but cannot be prevented, and has no default behavior to prevent:</p>
828
<pre class="code prettyprint">_leaveTrigger : function(node) {
829
this.fire("triggerLeave");
831
this._clearCurrentTrigger();
834
this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip);
838
<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>
840
<pre class="code prettyprint">_overTrigger : function(x, y) {
841
this._currTrigger.mouseX = x;
842
this._currTrigger.mouseY = y;
846
<h3>Setting Tooltip Content</h3>
848
<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>
851
<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
852
for all triggerNodes</li>
853
<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
854
will be set using the map, when the tooltip is triggered for the node.</li>
855
<li>Setting the title attribute on the trigger node. The value of the title attribute is used to set the tooltip content,
856
when triggered for the node.</li>
857
<li>By calling the <code>setTriggerContent</code> method to set content for a specific trigger node, in a <code>triggerEnter</code> event listener.</li>
860
<p>The precedence of these methods is handled in the <code>_setTriggerContent</code> method, invoked when the mouse enters a trigger:</p>
862
<pre class="code prettyprint">_setTriggerContent : function(node) {
863
var content = this.get("content");
864
if (content && !(content instanceof Node || Lang.isString(content))) {
865
content = content[node.get("id")] || node.getAttribute("title");
867
this.setTriggerContent(content);
870
setTriggerContent : function(content) {
871
var contentBox = this.get("contentBox");
872
contentBox.set("innerHTML", "");
875
if (content instanceof Node) {
876
for (var i = 0, l = content.size(); i < l; ++i) {
877
contentBox.appendChild(content.item(i));
879
} else if (Lang.isString(content)) {
880
contentBox.set("innerHTML", content);
886
<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>
888
<h3>Using Tooltip</h3>
890
<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>
892
<pre class="code prettyprint">var tt = new Tooltip({
893
triggerNodes:".yui3-hastooltip",
894
delegate: "#delegate",
896
tt3: "Tooltip 3 (from lookup)"
904
<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>
906
<pre class="code prettyprint">tt.on("triggerEnter", function(e) {
908
if (node && node.get("id") == "tt2") {
909
this.setTriggerContent("Tooltip 2 (from triggerEvent)");
914
<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>
916
<pre class="code prettyprint">var prevent = Y.one("#prevent");
917
tt.on("triggerEnter", function(e) {
919
if (prevent.get("checked")) {
920
if (node && node.get("id") == "tt4") {
927
<h2>Complete Example Source</h2>
928
<pre class="code prettyprint"><div id="delegate">
929
<div class="yui3-hastooltip" title="Tooltip 1" id="tt1">Tooltip One <span>(content from title)</span></div>
930
<div class="yui3-hastooltip" title="Tooltip 2" id="tt2">Tooltip Two <span>(content set in event listener)</span></div>
931
<div class="yui3-hastooltip" title="Tooltip 3" id="tt3">Tooltip Three <span>(content from lookup)</span></div>
932
<div class="yui3-hastooltip" title="Tooltip 4" id="tt4">Tooltip Four <span>(content from title)</span></div>
933
<label><input type="checkbox" id="prevent" /> Prevent Tooltip Four</label>
936
<script type="text/javascript">
937
YUI().use("event-mouseenter", "widget", "widget-position", "widget-stack", function(Y) {
943
var Tooltip = Y.Base.create("tooltip", Y.Widget, [Y.WidgetPosition, Y.WidgetStack], {
945
// PROTOTYPE METHODS/PROPERTIES
948
* Initialization Code: Sets up privately used state
949
* properties, and publishes the events Tooltip introduces
951
initializer : function(config) {
953
this._triggerClassName = this.getClassName("trigger");
955
// Currently bound trigger node information
956
this._currTrigger = {
959
mouseX: Tooltip.OFFSCREEN_X,
960
mouseY: Tooltip.OFFSCREEN_Y
963
// Event handles - mouse over is set on the delegate
964
// element, mousemove and mouseleave are set on the trigger node
965
this._eventHandles = {
973
// Show/hide timers
979
// Publish events introduced by Tooltip. Note the triggerEnter event is preventable,
980
// with the default behavior defined in the _defTriggerEnterFn method
981
this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true});
982
this.publish("triggerLeave", {preventable:false});
986
* Destruction Code: Clears event handles, timers,
987
* and current trigger information
989
destructor : function() {
990
this._clearCurrentTrigger();
992
this._clearHandles();
996
* bindUI is used to bind attribute change and dom event
999
bindUI : function() {
1000
this.after("delegateChange", this._afterSetDelegate);
1001
this.after("nodesChange", this._afterSetNodes);
1003
this._bindDelegate();
1007
* syncUI is used to update the rendered DOM, based on the current
1010
syncUI : function() {
1011
this._uiSetNodes(this.get("triggerNodes"));
1015
* Public method, which can be used by triggerEvent event listeners
1016
* to set the content of the tooltip for the current trigger node
1018
setTriggerContent : function(content) {
1019
var contentBox = this.get("contentBox");
1020
contentBox.set("innerHTML", "");
1023
if (content instanceof Node) {
1024
for (var i = 0, l = content.size(); i < l; ++i) {
1025
contentBox.appendChild(content.item(i));
1027
} else if (Lang.isString(content)) {
1028
contentBox.set("innerHTML", content);
1034
* Default attribute change listener for
1035
* the triggerNodes attribute
1037
_afterSetNodes : function(e) {
1038
this._uiSetNodes(e.newVal);
1042
* Default attribute change listener for
1043
* the delegate attribute
1045
_afterSetDelegate : function(e) {
1046
this._bindDelegate(e.newVal);
1050
* Updates the rendered DOM to reflect the
1051
* set of trigger nodes passed in
1053
_uiSetNodes : function(nodes) {
1054
if (this._triggerNodes) {
1055
this._triggerNodes.removeClass(this._triggerClassName);
1059
this._triggerNodes = nodes;
1060
this._triggerNodes.addClass(this._triggerClassName);
1065
* Attaches the default mouseover DOM listener to the
1066
* current delegate node
1068
_bindDelegate : function() {
1069
var eventHandles = this._eventHandles;
1071
if (eventHandles.delegate) {
1072
eventHandles.delegate.detach();
1073
eventHandles.delegate = null;
1075
eventHandles.delegate = Y.delegate("mouseenter", Y.bind(this._onNodeMouseEnter, this), this.get("delegate"), "." + this._triggerClassName);
1079
* Default mouse enter DOM event listener.
1081
* Delegates to the _enterTrigger method,
1082
* if the mouseover enters a trigger node.
1084
_onNodeMouseEnter : function(e) {
1085
var node = e.currentTarget;
1086
if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) {
1087
this._enterTrigger(node, e.pageX, e.pageY);
1092
* Default mouse leave DOM event listener
1094
* Delegates to _leaveTrigger if the mouse
1095
* leaves the current trigger node
1097
_onNodeMouseLeave : function(e) {
1098
this._leaveTrigger(e.currentTarget);
1102
* Default mouse move DOM event listener
1104
_onNodeMouseMove : function(e) {
1105
this._overTrigger(e.pageX, e.pageY);
1109
* Default handler invoked when the mouse enters
1110
* a trigger node. Fires the triggerEnter
1111
* event which can be prevented by listeners to
1112
* show the tooltip from being displayed.
1114
_enterTrigger : function(node, x, y) {
1115
this._setCurrentTrigger(node, x, y);
1116
this.fire("triggerEnter", {node:node, pageX:x, pageY:y});
1120
* Default handler for the triggerEvent event,
1121
* which will setup the timer to display the tooltip,
1122
* if the default handler has not been prevented.
1124
_defTriggerEnterFn : function(e) {
1126
if (!this.get("disabled")) {
1127
this._clearTimers();
1128
var delay = (this.get("visible")) ? 0 : this.get("showDelay");
1129
this._timers.show = Y.later(delay, this, this._showTooltip, [node]);
1134
* Default handler invoked when the mouse leaves
1135
* the current trigger node. Fires the triggerLeave
1136
* event and sets up the hide timer
1138
_leaveTrigger : function(node) {
1139
this.fire("triggerLeave");
1141
this._clearCurrentTrigger();
1142
this._clearTimers();
1144
this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip);
1148
* Default handler invoked for mousemove events
1149
* on the trigger node. Stores the current mouse
1152
_overTrigger : function(x, y) {
1153
this._currTrigger.mouseX = x;
1154
this._currTrigger.mouseY = y;
1158
* Shows the tooltip, after moving it to the current mouse
1161
_showTooltip : function(node) {
1162
var x = this._currTrigger.mouseX;
1163
var y = this._currTrigger.mouseY;
1165
this.move(x + Tooltip.OFFSET_X, y + Tooltip.OFFSET_Y);
1168
this._clearTimers();
1170
this._timers.hide = Y.later(this.get("autoHideDelay"), this, this._hideTooltip);
1174
* Hides the tooltip, after clearing existing timers.
1176
_hideTooltip : function() {
1177
this._clearTimers();
1182
* Set the rendered content of the tooltip for the current
1183
* trigger, based on (in order of precedence):
1185
* a). The string/node content attribute value
1186
* b). From the content lookup map if it is set, or
1187
* c). From the title attribute if set.
1189
_setTriggerContent : function(node) {
1190
var content = this.get("content");
1191
if (content && !(content instanceof Node || Lang.isString(content))) {
1192
content = content[node.get("id")] || node.getAttribute("title");
1194
this.setTriggerContent(content);
1198
* Set the currently bound trigger node information, clearing
1199
* out the title attribute if set and setting up mousemove/out
1202
_setCurrentTrigger : function(node, x, y) {
1204
var currTrigger = this._currTrigger,
1205
triggerHandles = this._eventHandles.trigger;
1207
this._setTriggerContent(node);
1209
triggerHandles.mouseMove = Y.on("mousemove", Y.bind(this._onNodeMouseMove, this), node);
1210
triggerHandles.mouseOut = Y.on("mouseleave", Y.bind(this._onNodeMouseLeave, this), node);
1212
var title = node.getAttribute("title");
1213
node.setAttribute("title", "");
1215
currTrigger.mouseX = x;
1216
currTrigger.mouseY = y;
1217
currTrigger.node = node;
1218
currTrigger.title = title;
1222
* Clear out the current trigger state, restoring
1223
* the title attribute on the trigger node,
1224
* if it was originally set.
1226
_clearCurrentTrigger : function() {
1228
var currTrigger = this._currTrigger,
1229
triggerHandles = this._eventHandles.trigger;
1231
if (currTrigger.node) {
1232
var node = currTrigger.node;
1233
var title = currTrigger.title || "";
1235
currTrigger.node = null;
1236
currTrigger.title = "";
1238
triggerHandles.mouseMove.detach();
1239
triggerHandles.mouseOut.detach();
1240
triggerHandles.mouseMove = null;
1241
triggerHandles.mouseOut = null;
1243
node.setAttribute("title", title);
1248
* Cancel any existing show/hide timers
1250
_clearTimers : function() {
1251
var timers = this._timers;
1253
timers.hide.cancel();
1257
timers.show.cancel();
1263
* Detach any stored event handles
1265
_clearHandles : function() {
1266
var eventHandles = this._eventHandles;
1268
if (eventHandles.delegate) {
1269
this._eventHandles.delegate.detach();
1271
if (eventHandles.trigger.mouseOut) {
1272
eventHandles.trigger.mouseOut.detach();
1274
if (eventHandles.trigger.mouseMove) {
1275
eventHandles.trigger.mouseMove.detach();
1280
// STATIC METHODS/PROPERTIES
1290
* The tooltip content. This can either be a fixed content value,
1291
* or a map of id-to-values, designed to be used when a single
1292
* tooltip is mapped to multiple trigger elements.
1299
* The set of nodes to bind to the tooltip instance. Can be a string,
1300
* or a node instance.
1304
setter: function(val) {
1305
if (val && Lang.isString(val)) {
1306
val = Node.all(val);
1313
* The delegate node to which event listeners should be attached.
1314
* This node should be an ancestor of all trigger nodes bound
1315
* to the instance. By default the document is used.
1319
setter: function(val) {
1320
return Y.one(val) || Y.one("document");
1325
* The time to wait, after the mouse enters the trigger node,
1326
* to display the tooltip
1333
* The time to wait, after the mouse leaves the trigger node,
1334
* to hide the tooltip
1341
* The time to wait, after the tooltip is first displayed for
1342
* a trigger node, to hide it, if the mouse has not left the
1350
* Override the default visibility set by the widget base class
1357
* Override the default XY value set by the widget base class,
1358
* to position the tooltip offscreen
1366
var tt = new Tooltip({
1367
triggerNodes:".yui3-hastooltip",
1368
delegate: "#delegate",
1370
tt3: "Tooltip 3 (from lookup)"
1377
tt.on("triggerEnter", function(e) {
1379
if (node && node.get("id") == "tt2") {
1380
this.setTriggerContent("Tooltip 2 (from triggerEvent)");
1384
var prevent = Y.one("#prevent");
1385
tt.on("triggerEnter", function(e) {
1387
if (prevent.get("checked")) {
1388
if (node && node.get("id") == "tt4") {
1394
</script></pre>
1400
<div class="yui3-u-1-4">
1401
<div class="sidebar">
1405
<div class="sidebox">
1407
<h2 class="no-toc">Examples</h2>
1411
<ul class="examples">
1414
<li data-description="Shows how to extend the base widget class, to create your own Widgets.">
1415
<a href="widget-extend.html">Extending the Base Widget Class</a>
1420
<li data-description="Shows how to use Base.create and mix/match extensions to create custom Widget classes.">
1421
<a href="widget-build.html">Creating Custom Widget Classes With Extensions</a>
1426
<li data-description="Shows how to create an IO plugin for Widget.">
1427
<a href="widget-plugin.html">Creating a Widget Plugin</a>
1432
<li data-description="Shows how to extend the Widget class, and add WidgetPosition and WidgetStack to create a Tooltip widget class.">
1433
<a href="widget-tooltip.html">Creating a Simple Tooltip Widget With Extensions</a>
1438
<li data-description="Shows how to extend the Widget class, and add WidgetParent and WidgetChild to create a simple ListBox widget.">
1439
<a href="widget-parentchild-listbox.html">Creating a Hierarchical ListBox Widget</a>
1454
<script src="../assets/vendor/prettify/prettify-min.js"></script>
1455
<script>prettyPrint();</script>