But eventually, if you write enough code, you’ll need to do some occasional browser sniffing.. Walk a Straight Code Path Most importantly, write code without unnecessary kinks and contor
Trang 1So we must search the browser’s user-agent string to see whether it uses the affected
engine—then we must look at the release year of the engine to figure out whether it’s old
enough to be affected
Of course, Prototype fixes this so that you, as a developer, need not worry about it
And the quirks you’ll encounter probably won’t be so tenacious But eventually, if you
write enough code, you’ll need to do some occasional browser sniffing Do it, apologize
to yourself, and move on
If it makes you queasy, good! It should make you queasy That’ll stop you from using
it more often than you ought to And the fact that you’re ashamed of your old poetry
sim-ply affirms the sophistication of your adult tastes
If You Must
So if you’ve got no other options yes, it’s OK to sniff But you’ve got to do it right
Perform the following steps or else suffer the wrath of the browser gods
Get a Second Opinion
First, assuage your guilt Find a friend or coworker—across the hall, on Instant
Messen-ger, in IRC—and summarize your dilemma Often your consultant will suggest an
approach you hadn’t thought of But if he can’t think of a better way, you’ll feel more
secure in your decision
Take Notes
Write a comment that explains the problem and why you’ve got to sniff Put it as close as
possible to the offending line of code Be verbose This is for your own benefit: six months
from now you won’t remember why you wrote that code the way you did, so think of it as
a message to Future You
Walk a Straight Code Path
Most importantly, write code without unnecessary kinks and contortions If Internet
Explorer needs one thing, but all other browsers need another, write your function to
handle the other browsers If one approach uses the DOM standard and the other uses
a proprietary Internet Explorer method, write your function to use the DOM standard—
then, at the beginning of the function, send Internet Explorer into a different function
to handle the weird case
Trang 2The purpose is to avoid the “black holes” that come from excessive sniffing Consider this code:
function handleFoo() {
if (navigator.userAgent.match(/Gecko\//))
return handleFoo_Firefox();
if (navigator.userAgent.match(/MSIE/))
return handleFoo_MSIE();
}
Safari, Opera, OmniWeb, iCab, and browsers far off the beaten path will fall straight through this function—because your code never “caught” them Again, you’re not
con-cerned with what the browser is; you’re concon-cerned with what it says it can do You can’t
possibly test in every browser on earth, so embrace standards as a compromise: if a browser follows web standards, it ought to be able to read your code, even if you didn’t code with it in mind
Holding Up Your End of the Bargain
Even though I’m giving you permission to write “dirty” code once in a while, I mean to open only the tiniest of loopholes The early days of JavaScript taught us that bad things happen when developers abuse user-agent sniffing I’d recommend against it altogether
if it weren’t for the handful of edge cases that require sniffing
In other words, when we as developers sniff unnecessarily, it’s our fault When we
discover situations in which sniffing is the only option, it’s the browser maker’s fault So
think of developing with web standards as a social contract between developers and vendors: do your part, and we’ll do ours Make your scripting environment behave pre-dictably and rationally, and we won’t need to take drastic steps to code around bugs
Making and Sharing a Library
Written something useful? Something you think would be valuable to others? All the major JavaScript toolkits have an ecosystem of plug-ins and add-ons If you’ve created
a script that makes your life easier, there’s a good chance it will make someone else’s life easier, too
Maybe you’ve done something big, like a really cool UI widget or client-side charting Maybe it’s big enough to warrant a Google Code project and a release schedule Or maybe you’ve written a way to do simple input validation in 30 lines of code and just want to put
it on the Web, as is, so that others can learn from it
Here are some best practices for releasing your add-on Most of them relate to the
difficulty of writing code that satisfies both your needs and the needs of the public.
Trang 3Make Your Code Abstract
The hardest thing about turning your code into public code is handling abstraction.
When you first wrote it, you might have embraced the conventions of your own
circum-stances in order to simplify things; now you’ve got to go back and handle scenarios you
didn’t foresee
Do One Thing Well (or Else Go Modular)
Don’t try to be a Swiss Army knife Code that does one thing well is easier to understand,
easier to set up, and faster for the end user to download It’s one thing to write a 5 KB
script that depends on Prototype; it’s another thing to write 80 KB of JavaScript that
depends on Prototype, most of which John Q Developer won’t even need.
I should clarify: it’s fine to do many things well, as long as those things are not
interdependent If your script does three unrelated things, break it up into three
unre-lated scripts Bundle them together if you like, but don’t require all three unless you’ve
got a very good reason Notice that script.aculo.us is modular: you don’t have to load all
the UI stuff if the effects are all you want
Embrace Convention
With apologies to Jakob Nielsen, a developer will spend far more time working with
other people’s code than with your code Each major framework has a distinct coding
style that can be seen in the structure of its API, the order of arguments, and even its
code formatting (Spaces, not tabs! No—tabs, not spaces!)
Follow those conventions! Make your add-on feel like a part of Prototype Design
your classes to take an optionsargument Embrace the patterns, coding style, and
lexi-con Nobody reads the documentation (at least not at first), so even the smallest details
of code writing can help a person intuit how to use your code
Make Things Configurable
All of the classes in Prototype and script.aculo.us share a powerful design principle:
they’re able to be exhaustively configurable and dead-simple to use at the same time.
Is there anything about your code that someone might need to tailor to his needs?
Does your widget set a background color? Make it a configurable option Does your date
picker control support arbitrary date formats (like DD/MM/YYYY, which is the most
common format outside of North America)? If not, write it now; someone will ask for that
feature Does your widget fade out over a span of 0.5 seconds? Someone will argue
pas-sionately for it to be 1 second instead.
Trang 4That takes care of the “exhaustively configurable” part To make your add-on
dead-simple to use, take care to hide this complexity below the surface until it’s needed Make
as many options as you like, but give an intelligent default for each one.
Make sure the optionsargument can be omitted together They’re called options
because they’re optional; if a particular parameter can’t have a default and can’t be
omit-ted, move it out of the optionsobject and into a positional argument
Add Hooks
Often your add-on will be an almost-but-not-quite-perfect solution to someone’s
prob-lem “If only the tooltips stayed in the DOM tree after they fade out!” “This would be perfect if the widget could follow my cursor around.”
These requests are usually too complex or obscure than can be solved with extra configuration, but they’re important nonetheless Don’t bring your fellow developer
95 percent of the way to his destination, and then leave him stranded in the mysterious town of Doesn’t-Quite-Do-What-I-Want Give him the tools to travel that final 5 percent
on his own
There are two prevailing ways to empower the user to make those tweaks: callbacks and custom events
We’ve seen callbacks before in nearly all the script.aculo.us controls They’re func-tions the user can define that will get called at a certain point in the control’s operation They can be called at certain points in your add-on’s life cycle
For instance, we can leverage the Configurablemixin we wrote earlier to set up default callbacks that are “empty,” letting the user override them if necessary:
var Widget = Class.create(Configurable, {
initialize: function(element, options) {
this.element = $(element);
this.setOptions(options);
this.options.onCreate();
}
});
Widget.DEFAULT_OPTIONS = {
onCreate: Prototype.emptyFunction
};
// using the callback
var someWidget = new Widget('some_element', {
onCreate: function() { console.log('creating widget'); }
});
Trang 5Notice the reference to Prototype.emptyFunction: it’s a function that does nothing.
Instead of checking whether the onCreateoption exists, we include it in the default
options; this way, whether the user specifies it or not, the onCreateproperty refers to
a function
Custom events are a way to approach this problem from a different angle Imagine
atooltip:hiddenevent that a developer can listen for in order to apply her own logic for
dealing with hidden tooltips Firing a custom event is a lot like invoking a callback:
var Widget = Class.create({
initialize: function(element, options) {
this.element = $(element);
// fire an event and pass this instance as a property of
// the event object
this.element.fire("widget:created", { widget: this });
}
});
// using the custom event
$('some_element').observe('widget:created', function() {
console.log("widget created");
});
// or observe document-wide:
document.observe("widget:created", function() {
console.log("widget created");
});
var someWidget = new Widget('some_element');
They’re a little more work to set up, but they’re also more robust Notice that we can
listen for the event on the element itself or on any one of its parent nodes, all the way up
to document Callbacks don’t provide that kind of flexibility, nor do they allow you to easily
attach more than one listener
Whichever way you go, be sure to document your hooks They won’t get used if
nobody knows they exist Be clear about when the hook fires and what information
accompanies it—parameters passed to the callback or properties attached to the event
object
Trang 6In this chapter, we explored a number of ways to turn code that’s useful to you into code that’s useful to others as well Conciseness, modularity, documentation, and extensibility
are the only things separating the scripts you write from libraries like Prototype and script.aculo.us
Have you got an idea for a script? A UI control, a clever use of Ajax, or even a way to automate a repetitive task? Write it! Get your code out into the wild! Focusing on the pol-ish needed to write scripts for the public will make you a better developer