Opened 13 years ago

Closed 12 years ago

#1526 closed defect (fixed)

Dynamically creating and destroying a Tooltip does not work

Reported by: tdondich Owned by: bill
Priority: high Milestone: 0.9beta
Component: Dijit Version: 0.3
Keywords: Cc:
Blocked By: Blocking:

Description

Loading a page with over 200 tooltips can be very long during initial load when using markup to define your tooltips. Therefore, the need for dynamically creating and destroying the widgets when needed is a necessity. The proper procedure needs to be defined for this functionality with 0.3.1 release and future releases. An example of a tooltip library that performs this method is overlib; however, Dojo Tooltips have more flexibility, and who cares about other libraries anyways.

The following code snippet attempts to create a tooltip when hovering over an element, attempts to show it, then when the mouse moves away from the element, tries to destroy it.

<span id="test" onmouseover="
		if(this.tooltip == null) {
			this.tooltip = dojo.widget.createWidget('Tooltip', {caption: 'testing', connectId: 'test'});
			document.body.appendChild(this.tooltip.domNode);
		}
		this.tooltip.onMouseOver(event);" onmouseOut="this.tooltip.onMouseMove(event); this.tooltip.hide(); this.tooltip.destroy(); this.tooltip = null;">TEST</span>

The problem with the above snippet is that some event connection is still retained sometimes, meaning when you move the mouse cursor away and anywhere on the document, a slew of dojo errors such as:

node has no properties, dojo.js line 4209

Change History (5)

comment:1 Changed 13 years ago by bill

Milestone: 0.5

It also seems like the newly created tooltip won't work because it won't get the onMouseOver event (since it's created after that event occurs), and also it seems like the tooltip is getting destroyed a little early (usually the tooltip stays visible for half a second after you mouse off the control).

But point taken. Scheduling for 0.5.

comment:2 Changed 13 years ago by tdondich

I did some more work on this using your comments as guidance. If I add a property to the Tooltip class called destroyOnHide: false, as so:

...
showDelay: 500,
hideDelay: 100,
connectId: "",
	
templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlTooltipTemplate.html"),
templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTooltipTemplate.css"),

connectNode: null,
	
destroyOnHide: false,
...

And modified the hide() method to contain the following:

hide: function() {
	if(this.state=="displaying"){
		// in the process of fading in.  wait until that is finished and then fade out
		this.eraseScheduled=true;
		return;
	}
	if ( this.state=="displayed" ) {
		this.state="erasing";
		if ( this.showTimer ) {
			clearTimeout(this.showTimer);
			delete this.showTimer;
		}
		if ( this.hideTimer ) {
			clearTimeout(this.hideTimer);
			delete this.hideTimer;
		}
		dojo.event.disconnect(document.documentElement, "onmousemove", this, "onMouseMove");
		dojo.widget.html.Tooltip.superclass.hide.call(this);
				
		// New code
		if(this.destroyOnHide) {
			dojo.event.disconnect(this.connectNode, "onmouseover", this, "onMouseOver");
			this.destroy();
		}
	}
},

Then the following code will work as expected:

<span id="test" onmouseover="
		tooltip = dojo.widget.createWidget('Tooltip', {caption: 'testing', connectId: 'test', destroyOnHide: true});
		document.body.appendChild(tooltip.domNode);
		tooltip.onMouseOver(event);">TEST</span>

This fires the onMouseOver for the tooltip and has the tooltip maintain it's lifecycle as a widget. When it hides, it will destroy itself and remove any event connections. Seems like this clears memory and should solve world hunger.

It may be a neat idea to at least give tooltips the option to do this, as dynamically creating tooltips on the fly is a big use-case.

comment:3 Changed 13 years ago by bill

Sounds good. But if you quickly move the mouse on/off the same object, I think that new tooltips (with identical content) will be created, before the old tooltips disappear. I guess we need a check to avoid that. Or set the disappear delay to 0. Note that there is a delay before the tooltip disappears so that you can move the mouse from the connected domnode _into_ the tooltip window, and press a button or scroll down or whatever.

So anyway, maybe it's best to write a utility function, maybe something like:

onMouseOver='dojo.widget.Tooltip.display({caption: ..., connectId: this.id, event: evt});'

A few other minor things:

  • I think Alex just added in the dojo.event.disconnect() call to the general tooltip code so you shouldn't need it in your patch anymore.
  • Also, the tooltip automatically attaches itself to document.body so that code isn't needed.
  • I don't think that your way of getting the event object object is portable across browsers.
  • Typically the argument to onMouseOver(evt) is the event object, but it has been normalized by dojo; it's not the raw event object. Normalization makes sure that pageX/pageY are set for all browsers, etc. (I think there's some function called fixEvent() or something in event/browser.js)

comment:4 Changed 12 years ago by Adam Peller

Component: WidgetsDijit

Bill, is this covered by Dijit?

comment:5 Changed 12 years ago by bill

Milestone: 0.90.9beta
Resolution: fixed
Status: newclosed

Yeah, you can look at the Validation widgets to see how to throw up tooltips on demand, and also the Tooltip class itself is much lighter than it used to be because it doesn't instantiate a template for every tooltip (it doesn't have a template at all).

Note: See TracTickets for help on using tickets.