Update: I've updated the JavaScript to use the hoverIntent plugin to avoid unnecessary ajax calls when moving the mouse quickly over a list of items.
We're taking a bit of a break from WPF right now, so I'm back working on our ASP.NET project.
Our application is very person centric, so we wanted an easy way to pop up a "tooltip" with more information about a person whenever you see a person's name. For example, here are some search results. When you hover over a name, more information appears.
This is very much based on the Coda Popup Bubble example for jQuery that's been going around with a few important differences:
- The information is requested via AJAX, so you don't have to include all of this extra information in a hidden div. This keeps your markup smaller for grids with tons of names in it.
- When you mouse over another name, the previous one will disappear. If you tried this with the original Coda example, you'd end up with a weird streaking animation since there's a delay before the div is hidden.
- Works in IE (just turned off the fade animation)
Note that this requires jQuery 1.3.1. I found out that the new .live() functionality is very useful for ajax applications. Previously, if you bind a handler at startup, it will apply to elements that currently exist. The new live() functionality makes it to where you can apply events to elements that are created in the future. This means I can create a new link via ajax and it will still work with the hover tooltip.
Markup
All you need to do is specify a certain css class that jquery expects, and we're using the "rel" tag to contain data to pass to our ajax helper page.
<a class="personPopupTrigger" href="<link to person>" rel="4218,a17bee64-8593-436e-a2f8-599a626370df">House, Devon</a> <a class="personPopupTrigger" href="<link to person>" rel="4218,f6434101-15bf-4c06-bbb2-fbe8c111b948">House, Gregory</a>
JavaScript
Run this on startup, and it will convert all of your links with the "personPopupTrigger" class to have the tooltip.
The container is the global container for the content. It's repositioned whenever a new link is hovered over.
$(function() { var hideDelay = 500; var currentID; var hideTimer = null; // One instance that's reused to show info for the current person var container = $('<div id="personPopupContainer">' + '<table width="" border="0" cellspacing="0" cellpadding="0" align="center" class="personPopupPopup">' + '<tr>' + ' <td class="corner topLeft"></td>' + ' <td class="top"></td>' + ' <td class="corner topRight"></td>' + '</tr>' + '<tr>' + ' <td class="left"> </td>' + ' <td><div id="personPopupContent"></div></td>' + ' <td class="right"> </td>' + '</tr>' + '<tr>' + ' <td class="corner bottomLeft"> </td>' + ' <td class="bottom"> </td>' + ' <td class="corner bottomRight"></td>' + '</tr>' + '</table>' + '</div>'); $('body').append(container); $('.personPopupTrigger').live('mouseover', function() { // format of 'rel' tag: pageid,personguid var settings = $(this).attr('rel').split(','); var pageID = settings[0]; currentID = settings[1]; // If no guid in url rel tag, don't popup blank if (currentID == '') return; if (hideTimer) clearTimeout(hideTimer); var pos = $(this).offset(); var width = $(this).width(); container.css({ left: (pos.left + width) + 'px', top: pos.top - 5 + 'px' }); $('#personPopupContent').html(' '); $.ajax({ type: 'GET', url: 'personajax.aspx', data: 'page=' + pageID + '&guid=' + currentID, success: function(data) { // Verify that we're pointed to a page that returned the expected results. if (data.indexOf('personPopupResult') < 0) { $('#personPopupContent').html('<span >Page ' + pageID + ' did not return a valid result for person ' + currentID + '.<br />Please have your administrator check the error log.</span>'); } // Verify requested person is this person since we could have multiple ajax // requests out if the server is taking a while. if (data.indexOf(currentID) > 0) { var text = $(data).find('.personPopupResult').html(); $('#personPopupContent').html(text); } } }); container.css('display', 'block'); }); $('.personPopupTrigger').live('mouseout', function() { if (hideTimer) clearTimeout(hideTimer); hideTimer = setTimeout(function() { container.css('display', 'none'); }, hideDelay); }); // Allow mouse over of details without hiding details $('#personPopupContainer').mouseover(function() { if (hideTimer) clearTimeout(hideTimer); }); // Hide after mouseout $('#personPopupContainer').mouseout(function() { if (hideTimer) clearTimeout(hideTimer); hideTimer = setTimeout(function() { container.css('display', 'none'); }, hideDelay); }); });
CSS
#personPopupContainer { position:absolute; left:0; top:0; display:none; z-index: 20000; } .personPopupPopup { } #personPopupContent { background-color: #FFF; min-width: 175px; min-height: 50px; } .personPopupPopup .personPopupImage { margin: 5px; margin-right: 15px; } .personPopupPopup .corner { width: 19px; height: 15px; } .personPopupPopup .topLeft { background: url(../images/personpopup/balloon_topLeft.png) no-repeat; } .personPopupPopup .bottomLeft { background: url(../images/personpopup/balloon_bottomLeft.png) no-repeat; } .personPopupPopup .left { background: url(../images/personpopup/balloon_left.png) repeat-y; } .personPopupPopup .right { background: url(../images/personpopup/balloon_right.png) repeat-y; } .personPopupPopup .topRight { background: url(../images/personpopup/balloon_topRight.png) no-repeat; } .personPopupPopup .bottomRight { background: url(../images/personpopup/balloon_bottomRight.png) no-repeat; } .personPopupPopup .top { background: url(../images/personpopup/balloon_top.png) repeat-x; } .personPopupPopup .bottom { background: url(../images/personpopup/balloon_bottom.png) repeat-x; text-align: center; }
Images
Ajax Helper Page
I'm not going to include a code sample for this since it's very specific to our application. It basically accepts query string parameters of "page" and "guid" and spits out details about that person.
Note: my example expects the ajax helper to return the content in a div with a css class of 'personPopupResult'. Also, it returns the person's guid in the result to verify that the returned result is for the same person currently selected since an ajax request might take a while in some cases and return an old result.
Why not use a pageID attribute and a currentID attribute instead of hijacking the rel attribute?
ReplyDelete@configurator no real reason... it just didn't feel right to add non-standard html attributes. Easy enough to modify the code to use named attributes for data.
ReplyDeleteProbably should try and cancel any running ajax call, before making a new one.
ReplyDeleteAlso you could check to see if the information is already loaded before calling again. However if its cached, shouldn't be an issue..
@Anonymous Can you provide a link on how to cancel an ajax call in jQuery? I looked a little bit earlier but didn't see an easy way to do it.
ReplyDelete@Caleb, Try something like
ReplyDeletevar x;
if(x) {x = null; x.abort(); }
x = $.ajax({ type: 'GET', url: aspx'});
sorry,
ReplyDeletevar x;
if(x) {x.abort(); x = null; }
x = $.ajax({ type: 'GET', url: aspx'});
@Anonymous thanks for the tip, I'll give that a try
ReplyDeletewhat html is your personajax.aspx page returning?
ReplyDelete@Andrew It's returning html that has the content wrapped inside of a div with a css class of 'personPopupResult'.
ReplyDeleteExample:
[div class="personPopupResult"]
First Name: Caleb
Last Name: Tucker
etc, etc...
[/div]
(Blogger doesn't like HTML in comments)
very nice, thanks!
ReplyDeleteThanks for great work. Could you create a sample for this, I don't know how to use it
ReplyDeleteVery nice!
ReplyDeleteCould you create a sample for this?? I tried make one but does´nt worked
Could you send by email?
ReplyDeletetonis.04@gmail.com
Got a problem, I've got everything set up and can see the tooltip box pop up when hovered, however it's blank ( no data ) - when using firebug i can see that the call hits the script and is responding with data - just doesnt seem to be making its way to the content div - any ideas?
ReplyDelete@Toni sorry, I'm not going to be able to create a sample as the code I'm using is part of my application. I don't have enough time to separate it and create a working sample.
ReplyDelete@Jon you might want to modify the success: function after the ajax call. The code I posted expects the HTML to be formatted a certain way (a div with class "personPopupResult"). You could remove this or make your returned HTML contain that.
ReplyDeletei like this script.
ReplyDeleteVery good.
My site uses it
do you have sample in php!
ReplyDeleteAlidad
When trying to implement this in Adobe AIR i get following error message!
ReplyDeleteTypeError: Value undefined does not allow function calls. When this function is initialized
$('.personPopupTrigger').live('mouseover', function()..
Any idea?
this is very nice but under asp.net, I wish you could also develop for php and ajax too. can you!
ReplyDeleteAM
@Andy my example just uses ASP.NET to retrieve information from the server. You could easily replace PersonAjax.aspx with a reference to a PHP file on your server that returns a result.
ReplyDeleteI guess i have the same problem as feriz... firebug returns an error $(".personPopupTrigger").live is not a function...
ReplyDeleteThanks
ReplyDeletethanks for the post..
ReplyDeleteGreat work. Any chance the popup could follow (move with) the mouse pointer?
ReplyDelete@Anonymous, we didn't want it to move with the mouse pointer so that some of the items (like email address are clickable. You should be able to tweak it to make it move with the mouse, though.
ReplyDeleteDear All,
ReplyDeleteIs any one ever used jsPdf.js,
I like to implement those script at my page.
but the sample that present at the website doesn't work. Can you guys help me please...
If it's possible please send me at my mail boy.beans@gmail.com.
Thanks for the help
B
for those that had problems with live not a function, make sure you upgrade to jquery 1.3. live() was only introduced recently.
ReplyDeletewe've got it live on our site now.. have a look here:
ReplyDeletehttp://www.puntersparadise.com.au/kingtipping/
See the little ? marks near the BUY buttons... that's it!
You are voted!
ReplyDeleteTrack back from WebDevVote.com
Great job on this.
ReplyDeleteIs there a way to make the actual link clickable at the same time that the tooltip is applied?
I noticed in your sample code you do specify a link in the element.
Great tooltip, thanks for sharing.
ReplyDeleteThis is a nice piece. Thank you!
ReplyDeleteBut I am having a problem with IE6 where the transparency image is not working properly. Has anyone find a fix for it?
I cant make this work in any way...what do i miss in here?
ReplyDeleteAny live demo?
Thanks...
I got it working ok but have a problem:
ReplyDeleteOnce the tooltip pops (mouseover) and then closes (mouseout) I click on other hyperlinks in my asp.net 3.5 page unrelated to this tooltip and I get redirected to a full screen page that displays the tooltip header. HOW Wierd?
Is anyone else getting this problem? (bug?).
It would be really nice to have at least some basic demo setup. I cant figure out how to get it working... For example it says "Run this at startup" and i honestly have no idea what to run :-(
ReplyDelete@ExtremeHobo "Run this at startup" means run the JavaScript code at the beginning of your page. e.g., create a [script type="text/javascript"][/script] block and put it in that.
ReplyDelete(Change [script] to proper html tag, of course)
Sorry, I'm unable to put together a working sample.
Hi Celeb,
ReplyDeleteI am getting the blank tooltip from the helper page eventhough I give the div class="personPopupResult".
My helper page looks like this...
|div class="personPopupResult"|
First Name: Caleb
Last Name: Tucker
/div|
I am calling somepage.cfm as helper page and that page has got the above code. Can you please correct me if I m missing something here...I love this tooltip. But its returning blank.
Pl...help
-Champ
@Champ, my code expects the person's ID to also be in the returned html somewhere as a hidden variable since you can have multiple active ajax requests open at the same time. Look at the line "if (data.indexOf(currentID) > 0)" in the jQuery. That's probably where you're having trouble.
ReplyDeleteThanks a lot for very quick response...Yeap..that worked...one more quick question...Its not working in IE. You said, turn off the fade animation. where can find that?
ReplyDeleteI really appreaciate your help.
-Champ
@Champ, I was just saying that the Coda Popup I based the code on (http://jqueryfordesigners.com/coda-popup-bubbles/) didn't work well in IE because it was doing a fade animation.
ReplyDeleteI'm not having any trouble in IE with the popup. Maybe some old JavaScript is being cached in your browser.
Still, I couldnt make it work in IE. Even in other browsers, I had to code this way by removing find('.personPopupResult').
ReplyDeleteI m giving this way
var text = $(data).html();
rather than
var text = $(data).find('.personPopupResult').html();
Here is code in my helper page.
[cfoutput]
[div id="personPopupResult"]
Passed ID: #guid#
[input type="hidden" name="currentID" value="#guid#">
[/div>
[/cfoutput>
if you have time, please help me
ReplyDelete@Caleb,
ReplyDeleteI messed around with it for a while and read a bit more on jQuery and got it working. I also messed around with your code and images and made it appear more like a speech bubble coming up from the pointer. Thanks for getting me started!
pon la fuente menso
ReplyDeleteHi Caleb,
ReplyDeleteSorry, I don't get it.
I can't make tooltip with data.
I tried this one.. please correct my mistake
[html]
[a class="..." href=".." rel="1,2"]test[/a]
[ajax html]
[html]
[body]
[div = "personPopupResult"]
firstname = MYFN
lastname = MYLN
[input type="hidden" name="currentID" value="2"]
[/div]
[/body]
[/html]
please correct my mistake.
thanks
monn