Advanced Tooltips It’s good to know how to build a simple tooltip, but we also know that we can do better.. Our advanced tooltip Our tooltip markup will consist of two nested span elem
Trang 1Licensed to JamesCarlson@aol.com
Finally, we need to respond to mouse movement by updating the tooltip’s location
This way the tooltip will follow the mouse around, just like the browser’s built-in
tooltips do And presto! We’ve replaced the default tooltips with our own, and we’re
fully in control of their appearance and animation
Advanced Tooltips
It’s good to know how to build a simple tooltip, but we also know that we can do
better For more sophisticated tooltips, which can include other markup (such as
images or links) inside the content, we’ll need to move them from the titleattribute
into our actual markup We’ll hide them with CSS, then reveal and position them
using jQuery as required The final effect is illustrated in Figure 5.11
Figure 5.11 Our advanced tooltip Our tooltip markup will consist of two nested span elements, which will sit inside
the element we want to use to trigger the tooltip This may occasionally require
some creativity with your markup, but helps us to position the tooltip, as we can
give it an absolute position offset from the parent element It also helps us handle
triggering events, since, if the user moves the mouse over the tooltip, it will still be
over the triggering element, so no additional hover event handling is required
Here’s an example of the tooltip markup:
Trang 2Licensed to JamesCarlson@aol.com
When we’re done, the tooltip will look great and will contain a link to the disclaimer
page But let’s not hang around; we’ve considered a few tooltip methods, so let’s
drive straight in
As we’ll see when we start writing the code, our tooltip will be quite clever, posi
tioning itself on whichever side of the target element that has enough room for it
to be displayed In order to make a cool text-bubble graphic work in this context,
we’ll need four different bubbles: above-left, above-right, left, and
below-right We’ll be using a single sprite for each of the tooltip’s four possible states, as
illustrated in Figure 5.12
Figure 5.12 Our tooltip sprite These tooltips require some fairly complex jQuery code We’ll go over it bit by bit,
but don’t worry if you have trouble understanding all of it right now There’s a bit
of a leap from writing quick two- or three-line scripts which replace and highlight
content on the page to writing a more complex UI widget At the beginning of the
next chapter, we’ll have a look at some of the ways we can try to minimize complex
ity and keep our code readable, even as it becomes longer and more involved
Trang 3Licensed to JamesCarlson@aol.com
For the moment, try to focus on seeing the bits of jQuery you already know employed
in a larger context; this should give you an idea of how you can go about combining
small pieces of logic into a larger picture that performs really impressively
Our first task is to create a TT object that will contain all our code We set a delay
variable at the top of the object (this will make it easier to modify the configuration
of the widget without hunting through the code to find where this variable was set):
Then we add a function called setTips, which we’ll run when the page loads or is
resized This function will find all the tooltips on the page and determine their
position by looking at their parent elements It will also set up a hover event on
each of them so that they’re displayed on mouseover Here’s the hover event:
That’s a fairly dense block of code, so let’s see if we can make some sense of what’s
happening:
We’ve attached the hover event to the parent of the tooltip span If you look back at the markup, you’ll see this is correct: we put the tooltip inside the ele
ment we want to attach it to
Trang 4Licensed to JamesCarlson@aol.com
We store a reference to that parent element inside a variable What’s unusual here is that we’re using a property of our TT object instead of a global variable
We’ll come back to this in the next chapter, but for now just know that it’s much the same as writing var current = $(this);
We’re using the familiar setTimeoutfunction, except that this time we’re saving the timer to a variable This is so we can turn it off by name if we need to
We’re accessing the delay property we set before as the second parameter for
setTimeout As we’ve seen, this is how long the browser will wait before dis
playing the tooltip
When the user mouses off the target, we want to stop the timer so that the tooltip will stay hidden after the delay expires We do this with the JavaScript
clearTimeout method, passing in the reference to our timer
Okay, so now that our hover handler is set up, we need to determine the position
of each of our tooltips We’ll use each() to loop over them, but first we’ll grab the
height and width of the screen If we were to do this inside the loop, jQuery would
need to calculate those values once for each tooltip, even though they’re always the
same By storing the values outside the loop, we avoid this wasteful calculation and
improve the performance of our script:
Trang 5Licensed to JamesCarlson@aol.com
var totalHeight = $container.height() + $(this).outerHeight();
var width = $(this).outerWidth();
var offset = $container.offset();
This part of the code should be a little easier to understand We loop over each
tooltip on the page, and first store a reference to the container element just to avoid
having to write $(this).parent() over and over Notice that the variable name
starts with $: this is just to help us remember that the variable contains a jQuery
selection Here’s a breakdown of the contents of the loop:
We check to see if the parent element has position: absolute;or position:
fixed; It has to be positioned, since we’ll be using position: absolute; to offset the tooltip from it If it already has absoluteor fixed, we’ll leave it that way If it doesn’t, though, we’ll give it position: relative;
We need to know the total combined height of both the tooltip and the parent element, so we store that in a variable to use later
By default, we give the tooltip a topposition equal to the height of the container
This means it will appear directly below the container (since it is offset from
the top by exactly the container’s height)
Trang 6Licensed to JamesCarlson@aol.com
Logical Operators
In JavaScript, when you’re testing for values in an if statement, you can use the
&& operator to mean and So in the above example, the contents of the if block will only execute if both conditions (on either side of &&) are met
You can also write || (two pipe symbols) to mean or If we’d used that instead of
&&above, the contents of the if block would execute if either condition was met
This is good work so far! We’re almost done, but we need to fix one small problem:
what if the tooltip’s position takes it off the screen? If the target element is right at
the bottom of the screen, and we want the tooltip to appear below it, the tooltip will
remain unseen!
It’s time for a little collision detection We need to find out if the tooltip is hitting
either the bottom or right edge of the screen Let’s have a look at how we accomplish
this:
We check to see if the tip’s horizontal or vertical offset, plus its width or height, is
greater than the width or height of the screen (which we calculated earlier) If it is,
we modify the top or left property respectively, and assign a class that we’ll use
to display the appropriate part of our background image sprite
Trang 7Licensed to JamesCarlson@aol.com
The final (and easiest) step is to use the css action to assign the calculated top and
left properties to the tips:
We’ll call our setTipsmethod on document-ready, and also each time the window
is resized, to ensure that our tips are always adequately positioned:
With that code in place, we just need to add some CSS to position our background
sprite appropriately, based on the classes we assigned:
Trang 8Licensed to JamesCarlson@aol.com
IE6 Support
Although jQuery does a fantastic job of handling cross-browser issues in our JavaScript code, it’s not so good for our CSS The above style rules rely on chaining several class selectors together This will confuse Internet Explorer 6, which will only see the last class in any style rule Moreover, our PNG image relies on alpha-channel transparency for the tooltip’s drop shadow, and this is also unsup
ported by IE6
Over the last few years, several major sites (including YouTube and Facebook) began phasing out support for IE6 This doesn’t mean that they totally ignore this browser, rather that they accept that IE6 users will receive a degraded experience (perhaps similar to what visitors without JavaScript will see)
For our tooltip example, we could use conditional comments5 to target some styles specifically to IE6 and provide it with the same tooltip functionality—except using
a flat background image without a thought-bubble style or a drop shadow This way, the background position would be inconsequential, and the PNG issue solved
And there you have it! The final tooltip not only waits to see if you really meant
for it to display, but it also shifts its position to make sure it’s fully visible
whenever it does display! Because we’ve avoided linking this code to anything
specific on our page, it’s easy to reuse this script on any other page—you just need
to include a few spans with a tooltip class, and you’re off to the races This is an
important lesson: you should always try to structure your code in such a way that
you can reuse it later This will save you work in the long run, and give you more
time to experiment with cool new functionality instead of rebuilding the same old
widgets every time you need them
5 http://reference.sitepoint.com/css/conditionalcomments/
Trang 9Licensed to JamesCarlson@aol.com
Whew! That was a hard sprint to the finish line Over the course of this chapter,
we’ve ramped up our jQuery know-how and used it to move beyond simple hiding
and revealing, and well into the territory of the true UI ninja You’ve learned how
to reduce complexity on the screen by packaging up links and widgets into collapsing
menus, accordions, panels, and tooltips
In the next chapter, we’ll look at reducing complexity in our code, and then tackle
what’s ostensibly the most important part of jQuery: Ajax!
Trang 10Licensed to JamesCarlson@aol.com
Chapter
6
Construction, Ajax, and Interactivity
Throughout the preceding chapters we’ve wowed and dazzled our client with a
cornucopia of visual effects and optical magic tricks, giving his site a lifelike appear
ance Unfortunately, our client is becoming savvy: as well as wanting his pages
looking Web 2.0, he wants them acting Web 2.0 as well And having pages act Web
2.0 means one thing: Ajax!
And not just a little bit—he wants the works: inline text editing, Twitter widgets,
endlessly scrolling image galleries … he wants StarTrackr! to have more Ajax-enabled
bells and whistles than Facebook, Flickr, and Netvibes combined
That’s fine by us Implementing client-side Ajax functionality is easy, especially
with jQuery as our framework But these cool new features come at a cost of increased
complexity Some simple tasks (such as loading in a snippet of HTML) are no
problem—but as we start to tackle the business of creating advanced Ajax compon
ents, the risk of making a mess of unmaintainable spaghetti code grows large So
before we jump into the deep end, we’ll review some ways we can manage complex
ity, and write well-behaved code that will impress our peers
Trang 11Licensed to JamesCarlson@aol.com
Construction and Best Practices
JavaScript is a wonderful language Don’t let anyone tell you any different Its his
torically poor reputation stems from years of misunderstanding and misuse: an al
most infinite collection of inline scripts, displaying little regard for good coding
practices like encapsulation and reuse But the past few years have ushered in a
new era for this underdog of the Web Developers have begun to respect (and con
quer) the language, and the result has been some great code libraries—including
our favorite, jQuery
jQuery has greatly simplified the process of dealing with Ajax and the DOM, but it
hasn’t changed the benefits of writing nice clean JavaScript code There’s no need
for us to become masters of JavaScript—but there are a few steps we should take to
ensure we’re writing the kind of code that will make future developers and main
tainers of our projects want to buy us a beer
Cleaner jQuery
We’ve done a fairly good job of steering clear of any involved JavaScript code—that’s
a testament to how good jQuery is at doing what it does But as our jQuery compon
ents and effects grow more complex, we need to start thinking about how to best
structure our code Once again we should remember that, under the hood, jQuery
is just JavaScript—so it’ll serve us well to steal a few best practices from the world
of JavaScript We already saw a bit of this kind of code organization when we built
our advanced tooltip script at the end of Chapter 5 Now let’s reveal the whys and
hows of writing cleaner jQuery code
Code Comments
Just like HTML and CSS, JavaScript provides you with a way to comment your code
Any line that you begin with two slashes (//) will be ignored by the browser, so
you can safely include explanations about what your code is doing For example,
in this snippet the first line will be ignored, and only the second will be processed
as code:
Trang 12Licensed to JamesCarlson@aol.com
If you need to write comments which stretch over multiple lines, you can begin
them with /* and end them with */ For example:
Comments go a long way to making your code reusable and maintainable: they help
you see at a glance what each line or section is doing when you revisit code you
wrote months ago
Map Objects
We’ve been dealing with key/value pair objects since all the way back in Chapter 2
We use them to pass multiple options into a single jQuery action, for example:
They aren’t special jQuery constructs—once again, it’s just plain old JavaScript—but
they’re great for encapsulating data to pass around in your own functions and
widgets For example, if you pull data out from some form fields, you can package
them up into key/value mapped pairs that you can then process further:
With the data all wrapped up, we’re able to easily pass it around and use it however
we like To access any one of an object’s values, we simply need to type the object’s
name, followed by a period (.), followed by the key associated with that value we
wish to access For example, given the data object defined above, if you wanted to
Trang 13Licensed to JamesCarlson@aol.com
check to see if the type property contained the string 'person', and alert the name
property if so, you’d write:
Namespacing Your Code
Even the nicest of libraries still lets you write the nastiest of spaghetti code—and
jQuery is no exception Sorting through 20 pages of hover and toggle commands
will end up driving you crazy, so to save your sanity you’ll want to group logical
chunks of code together
We already had a go at doing this in the section called “Advanced Tooltips” in
Chapter 5—and if you go back and have a look at that example, you’ll notice that
almost all of the code is wrapped up in an object named TT This object is much the
same as the data object above (and all the object literals we’ve been working with
so far), except that it also contains functions, as well as static variables
So when we wrote setTips: function() { … }, we were assigning that function
to the setTips property of the TT object Once that’s done, we can write
TT.set-Tips() to execute the function Now every function we write that has to do with
tooltips can be contained inside TT Because the only object we’re declaring in the
global scope (more on this in a second) is TT, we can rest assured that none of our
functions or variables will conflict with other JavaScript code on the page We refer
to this technique as namespacing, and refer to all our TT variables and methods as
being part of the TT namespace
Our namespace object can be given any name so long as it’s a valid variable name
This means it can start with a dollar sign, underscore, or any alphabetical charac
ter—lowercase or uppercase
Additionally, the more unique, short, and helpful the name is, the more successful
it will be We’re looking to avoid clashes in function names, so making namespaces
that are likely to clash will only escalate the problem
TRKRwould be a good choice for StarTrackr! It’s short, helpful in that it alludes back
to our site name, and fairly unique