PDA

View Full Version : [CLOSED] Ext.ToolTip-> onMouseMove



tobiu
8 Jul 2010, 5:43 AM
hi team,

i took a closer look at this method:


onMouseMove : function(e){
var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
if (t) {
this.targetXY = e.getXY();
if (t === this.triggerElement) {
if(!this.hidden && this.trackMouse){
this.setPagePosition(this.getTargetXY());
}
} else {
this.hide();
this.lastActive = new Date(0);
this.onTargetOver(e);
}
} else if (!this.closable && this.isVisible()) {
this.hide();
}
}


if not this.delegate, this.triggerElement will be set to true, so we will always end up in the if(t){...} - part.

the fix should be quite simple:


Ext.override(Ext.ToolTip, {

onMouseMove : function(e){
var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement;
if (t) {
this.targetXY = e.getXY();
if (t === this.triggerElement) {
if(!this.hidden && this.trackMouse){
this.setPagePosition(this.getTargetXY());
}
} else {
this.hide();
this.lastActive = new Date(0);
this.onTargetOver(e);
}
} else if (!this.closable && this.isVisible()) {
this.hide();
}
}
});


i have a second point i am wondering about: this method seems to get called all the time on document-level for each tooltip.

if you put this simpe console.log() in the qtip-exampe under examples/simple-widgets/qtips.js:



/*!
* Ext JS Library 3.2.1
* Copyright(c) 2006-2010 Ext JS, Inc.
* licensing@extjs.com
* http://www.extjs.com/license
*/
Ext.onReady(function(){

Ext.ToolTip.prototype.hideDelay = 5000;

Ext.override(Ext.ToolTip, {
onMouseMove : function(e){
console.log("onMouseMove");
var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement;
if (t) {
this.targetXY = e.getXY();
if (t === this.triggerElement) {
console.log("onMouseMove");
if(!this.hidden && this.trackMouse){
this.setPagePosition(this.getTargetXY());
}
} else {
this.hide();
this.lastActive = new Date(0);
this.onTargetOver(e);
}
} else if (!this.closable && this.isVisible()) {
this.hide();
}
}
});

new Ext.ToolTip({
target: 'tip1',
html: 'A very simple tooltip'
});

new Ext.ToolTip({
target: 'ajax-tip',
width: 200,
autoLoad: {url: 'ajax-tip.html'},
dismissDelay: 15000 // auto hide after 15 seconds
});

new Ext.ToolTip({
target: 'tip2',
html: 'Click the X to close me',
title: 'My Tip Title',
autoHide: false,
closable: true,
draggable:true
});

new Ext.ToolTip({
target: 'track-tip',
title: 'Mouse Track',
width:200,
html: 'This tip will follow the mouse while it is over the element',
trackMouse:true
});

new Ext.ToolTip({
title: '<a href="#">Rich Content Tooltip</a>',
id: 'content-anchor-tip',
target: 'leftCallout',
anchor: 'left',
html: null,
width: 415,
autoHide: false,
closable: true,
contentEl: 'content-tip', // load content from the page
listeners: {
'render': function(){
this.header.on('click', function(e){
e.stopEvent();
Ext.Msg.alert('Link', 'Link to something interesting.');
Ext.getCmp('content-anchor-tip').hide();
}, this, {delegate:'a'});
}
}
});

new Ext.ToolTip({
target: 'bottomCallout',
anchor: 'top',
anchorOffset: 85, // center the anchor on the tooltip
html: 'This tip\'s anchor is centered'
});

new Ext.ToolTip({
target: 'trackCallout',
anchor: 'right',
trackMouse: true,
html: 'Tracking while you move the mouse'
});


Ext.QuickTips.init();

});


you will notice that the event fires all the time, anywhere on the screen and for all tips. in big apps with lots of tooltips, this might get an performance issue.


kind regards
tobiu

Animal
8 Jul 2010, 6:23 AM
"Fix" for what buggy behaviour?

In the first case that code is there for a reason.

If there is no delegate, then the if test has to find true. But within the if test (t === this.triggerElement) must be false.

So setting t explicitly to true is correct

In the second case, the QuickTips singleton targets the document body, so it is tracking its mouse moves within its target.

tobiu
12 Jul 2010, 2:25 AM
hi nige,

thanks for your reply. i think you did not get my point right on the first issue: if it is really wanted to set this.triggerElement to true if there is no delegate, then we will always enter the part of if(t). because we have t = e.getTarget(this.delegate) || true.



var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
if (t) {
...
} else if (!this.closable && this.isVisible()) {
this.hide();
}
}


the red-marked part will never be executed, so should be removed.

the else-part is also more than worth a look:



else {
this.hide();
this.lastActive = new Date(0);
this.onTargetOver(e);
}


the whole tooltip / quicktip - classes deal about hiding the tips with a delay,
so there should be at least a call to delayHide instead of a direct call to hide().

at least the config says so:



/**
* @cfg {Number} hideDelay Delay in milliseconds after the mouse exits the
* target element but before the tooltip actually hides (defaults to 200).
* Set to 0 for the tooltip to hide immediately.
*/


last point: why calling



this.onTargetOver(e);


after the tip is hidden without any delay? makes no sense to me.


kind regards,
tobiu

Animal
12 Jul 2010, 5:03 AM
That's not what is required.




onMouseMove : function(e){
var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
if (t) {
this.targetXY = e.getXY();
if (t === this.triggerElement) {
if(!this.hidden && this.trackMouse){
this.setPagePosition(this.getTargetXY());
}
} else {
this.hide();
this.lastActive = new Date(0);
this.onTargetOver(e);
}
} else if (!this.closable && this.isVisible()) {
this.hide();
}
}



If the Tooltip is using a delegate to subdivide its target, then attempt to see which subtarget the move event is over, and use that as t.

It MIGHT be null. For example a tooltip on a DataView who's delegate is the item selector. When moving OFF an item, t should become null, and the tip should disappear.

But if it not null the "if (t)" section: then if the t element is the same as the previous trigger target, just see if we need to track the mouse, but if the t element is NOT the same as the last trigger target, we've switched subtargets, so we need to hide, and then perform a "target over" operation to reshow on this new subtarget.

If NOT using a delegate, then we still want t to be true. BUT we never want it to equal the current trigger element.

The code is correct and does what it was designed to do. It does.

spacyspacy
12 Jul 2010, 6:58 AM
It seems not to be clear what is the exact meaning of the following line of code:

var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;

Is it:


if (this.delegate)
{
var t = e.getTarget(this.delegate);
}
else
{
this.triggerElement = true;
var t = true;
}

?

tobiu
12 Jul 2010, 7:13 AM
hi nige,

the tooltip-class has a listener onMouseout, thats why it gets hidden as expected -> different story.

i agree, that a check for this.delegate makes sense.
i just do not agree, that t can ever get the value of null.

if it was


var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement === true;


you were right.

if you open the firebug-console on any page not including the ext-bib and type into the console


var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;
console.log(t);

you will see a true. that leaves us at the point:



if(true){
...
} else if {
...
}


and the other point



else {
this.hide();
this.lastActive = new Date(0);
this.onTargetOver(e);
}


can never ever be right, since it is

a) hiding without any delay (see post above)
b) this.onOver after hiding it won't do.

i hope i made these points clearer now. it's hot as hell in the bureau here, quite difficult to concentrate ;)


kind regards,
tobiu

Animal
12 Jul 2010, 7:38 AM
It really is correct.

Consider the code



var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;


That says, if we are using a delegate then attempt to get a matching target from the mousemove event.

If our mousemove was not over a matching target, there we are, t will be null.

spacyspacy
12 Jul 2010, 7:55 AM
It really is correct.

Consider the code



var t = this.delegate ? e.getTarget(this.delegate) : this.triggerElement = true;


That says, if we are using a delegate then attempt to get a matching target from the mousemove event.

If our mousemove was not over a matching target, there we are, t will be null.

With the code above t will be e.getTarget(this.delegate) or true, but never null, because if we eliminate the ? part in the line we get:

var t = this.triggerElement = true;

Or am I completly wrong? ;-)

Condor
12 Jul 2010, 7:57 AM
Yes, t can be e.getTarget(this.delegate) or true.

But e.getTarget(this.delegate) can be null!

spacyspacy
12 Jul 2010, 9:01 AM
Yes, t can be e.getTarget(this.delegate) or true.

But e.getTarget(this.delegate) can be null!

Ah you are right. So there is still the "this.hide();" thing mentioned above...

tobiu
12 Jul 2010, 9:12 AM
hi condor, spacy and nige,

you are right. the heat affects my brain, about 35°C here :P

somehow i always had in mind:



var t = this.delegate ? this.delegate : this.triggerElement = true;


which is not what the code says. but setting the this.triggerElement to true still seems strange for me.

so, to clean up this topic, there is only 1 major point open now:



else {
this.hide();
this.lastActive = new Date(0);
this.onTargetOver(e);
}


details in the answers above.


kind regards,
tobiu

Animal
12 Jul 2010, 10:12 AM
OK, on that last point, that takes place if the current subtarget is different from the last one (We are using delegate in that code path, and we might have mousemoved WITHIN the target, but switched active elements)

So we must hide and then do a new show operation for the new active subtarget.