24ways

Custom SQL query returning 14 rows (hide)

Query parameters

rowidtitlecontentsyearauthorauthor_slugpublishedurltopic
145 The Neverending (Background Image) Story Everyone likes candy for Christmas, and there’s none better than eye candy. Well, that, and just more of the stuff. Today we’re going to combine both of those good points and look at how to create a beautiful background image that goes on and on… forever! Of course, each background image is different, so instead of agonising over each and every pixel, I’m going to concentrate on five key steps that you can apply to any of your own repeating background images. In this example, we’ll look at the Miami Beach background image used on the new FOWA site, which I’m afraid is about as un-festive as you can get. 1. Choose your image wisely I find there are three main criteria when judging photos you’re considering for repetition manipulation (or ‘repetulation’, as I like to say)… simplicity (beware of complex patterns) angle and perspective (watch out for shadows and obvious vanishing points) consistent elements (for easy cloning) You might want to check out this annotated version of the image, where I’ve highlighted elements of the photo that led me to choose it as the right one. The original image purchased from iStockPhoto. The Photoshopped version used on the FOWA site. 2. The power of horizontal lines With the image chosen and your cursor poised for some Photoshop magic, the most useful thing you can do is drag out the edge pixels from one side of the image to create a kind of rough colour ‘template’ on which to work over. It doesn’t matter which side you choose, although you might find it beneficial to use the one with the simplest spread of colour and complex elements. Click and hold on the marquee tool in the toolbar and select the ‘single column marquee tool’, which will span the full height of your document but will only be one pixel wide. Make the selection right at the edge of your document, press ctrl-c / cmd-c to copy the selection you made, create a new layer, and hit ctrl-v / cmd-v to paste the selection onto your new layer. using free transform (ctrl-t / cmd-t), drag out your selection so that it becomes as wide as your entire canvas. A one-pixel-wide selection stretched out to the entire width of the canvas. 3. Cloning It goes without saying that the trusty clone tool is one of the most important in the process of creating a seamlessly repeating background image, but I think it’s important to be fairly loose with it. Always clone on to a new layer so that you’ve got the freedom to move it around, but above all else, use the eraser tool to tweak your cloned areas: let that handle the precision stuff and you won’t have to worry about getting your clones right first time. In the example below, you can see how I overcame the problem of the far-left tree shadow being chopped off by cloning the shadow from the tree on its right. The edge of the shadow is cut off and needs to be ‘made’ from a pre-existing element. The successful clone completes the missing shadow. The two elements are obviously very similar but it doesn’t look like a clone because the majority of the shape is ‘genuine’ and only a small part is a duplicate. Also, after cloning I transformed the duplicate, erased parts of it, used gradients, and — ooh, did someone mention gradients? 4. Never underestimate a gradient For this image, I used gradients in a similar way to a brush: covering large parts of the canvas with a colour that faded out to a desired point, before erasing certain parts for accuracy. Several of the gradients and brushes that make up the ‘customised’ part of the image, visible when the main photograph layer is hidden. The full composite. Gradients are also a bit of an easy fix: you can use a gradient on one side of the image, flip it horizontally, and then use it again on the opposite side to make a more seamless join. Speaking of which… 5. Sewing the seams No matter what kind of magic Photoshop dust you sprinkle over your image, there will still always be the area where the two edges meet: that scary ‘loop’ point. Fret ye not, however, for there’s help at hand in the form of a nice little cheat. Even though the loop point might still be apparent, we can help hide it by doing something to throw viewers off the scent. The seam is usually easy to spot because it’s a blank area with not much detail or colour variation, so in order to disguise it, go against the rule: put something across it! This isn’t quite as challenging as it may sound, because if we intentionally make our own ‘object’ to span the join, we can accurately measure the exact halfway point where we need to split it across the two sides of the image. This is exactly what I did with the FOWA background image: I made some clouds! A sky with no clouds in an unhappy one. A simple soft white brush creates a cloud-like formation in the sky. After taking the cloud’s opacity down to 20%, I used free transform to highlight the boundaries of the layer. I then moved it over to the right, so that the middle of the layer perfectly aligned with the right side of the canvas. Finally, I duplicated the layer and did the same in reverse: dragging the layer over to the left and making sure that the middle of the duplicate layer perfectly aligned with the left side of the canvas. And there you have it! Boom! Ta-da! Et Voila! To see the repeating background image in action, visit futureofwebapps.com on a large widescreen monitor or see a simulation of the effect. Thanks for reading, folks. Have a great Christmas! 2007 Elliot Jay Stocks elliotjaystocks 2007-12-03T00:00:00+00:00 https://24ways.org/2007/the-neverending-background-image-story/ code
154 Diagnostic Styling We’re all used to using CSS to make our designs live and breathe, but there’s another way to use CSS: to find out where our markup might be choking on missing accessibility features, targetless links, and just plain missing content. Note: the techniques discussed here mostly work in Firefox, Safari, and Opera, but not Internet Explorer. I’ll explain why that’s not really a problem near the end of the article — and no, the reason is not “everyone should just ignore IE anyway”. Basic Diagnostics To pick a simple example, suppose you want to call out all holdover font and center elements in a site. Simple: you just add the following to your styles. font, center {outline: 5px solid red;} You could take it further and add in a nice lime background or some such, but big thick red outlines should suffice. Now you’ll be able to see the offenders wherever as you move through the site. (Of course, if you do this on your public server, everyone else will see the outlines too. So this is probably best done on a development server or local copy of the site.) Not everyone may be familiar with outlines, which were introduced in CSS2, so a word on those before we move on. Outlines are much like borders, except outlines don’t affect layout. Eh? Here’s a comparison. On the left, you have a border. On the right, an outline. The border takes up layout space, pushing other content around and generally being a nuisance. The outline, on the other hand, just draws into quietly into place. In most current browsers, it will overdraw any content already onscreen, and will be overdrawn by any content placed later — which is why it overlaps the images above it, and is overlapped by those below it. Okay, so we can outline deprecated elements like font and center. Is that all? Oh no. Attribution Let’s suppose you also want to find any instances of inline style — that is, use of the style attribute on elements in the markup. This is generally discouraged (outside of HTML e-mails, which I’m not going to get anywhere near), as it’s just another side of the same coin of using font: baking the presentation into the document structure instead of putting it somewhere more manageable. So: *[style], font, center {outline: 5px solid red;} Adding that attribute selector to the rule’s grouped selector means that we’ll now be outlining any element with a style attribute. There’s a lot more that attribute selectors will let use diagnose. For example, we can highlight any images that have empty alt or title text. img[alt=""] {border: 3px dotted red;} img[title=""] {outline: 3px dotted fuchsia;} Now, you may wonder why one of these rules calls for a border, and the other for an outline. That’s because I want them to “add together” — that is, if I have an image which possesses both alt and title, and the values of both are empty, then I want it to be doubly marked. See how the middle image there has both red and fuchsia dots running around it? (And am I the only one who sorely misses the actual circular dots drawn by IE5/Mac?) That’s due to its markup, which we can see here in a fragment showing the whole table row. <tr> <th scope="row">empty title</th> <td><img src="comic.gif" title="" /></td> <td><img src="comic.gif" title="" alt="" /></td> <td><img src="comic.gif" title="" alt="comical" /></td> </tr> Right, that’s all well and good, but it misses a rather more serious situation: the selector img[alt=""] won’t match an img element that doesn’t even have an alt attribute. How to tackle this problem? Not a Problem Well, if you want to select something based on a negative, you need a negative selector. img:not([alt]) {border: 5px solid red;} This is really quite a break from the rest of CSS selection, which is all positive: “select anything that has these characteristics”. With :not(), we have the ability to say (in supporting browsers) “select anything that hasn’t these characteristics”. In the above example, only img elements that do not have an alt attribute will be selected. So we expand our list of image-related rules to read: img[alt=""] {border: 3px dotted red;} img[title=""] {outline: 3px dotted fuchsia;} img:not([alt]) {border: 5px solid red;} img:not([title]) {outline: 5px solid fuchsia;} With the following results: We could expand this general idea to pick up tables who lack a summary, or have an empty summary attribute. table[summary=""] {outline: 3px dotted red;} table:not([summary]) {outline: 5px solid red;} When it comes to selecting header cells that lack the proper scope, however, we have a trickier situation. Finding headers with no scope attribute is easy enough, but what about those that have a scope attribute with an incorrect value? In this case, we actually need to pull an on-off maneuver. This has us setting all th elements to have a highlight style, and then turn it off for the elements that meet our criteria. th {border: 2px solid red;} th[scope="col"], th[scope="row"] {border: none;} This was necessary because of the way CSS selectors work. For example, consider this: th:not([scope="col"]), th:not([scope="row"]) {border: 2px solid red;} That would select…all th elements, regardless of their attrributes. That’s because every th element doesn’t have a scope of col, doesn’t have a scope of row, or doesn’t have either. There’s no escaping this selector o’ doom! This limitation arises because :not() is limited to containing a single “thing” within its parentheses. You can’t, for example, say “select all elements except those that are images which descend from list items”. Reportedly, this limitation was imposed to make browser implementation of :not() easier. Still, we can make good use of :not() in the service of further diagnosing. Calling out links in trouble is a breeze: a[href]:not([title]) {border: 5px solid red;} a[title=""] {outline: 3px dotted red;} a[href="#"] {background: lime;} a[href=""] {background: fuchsia;} Here we have a set that will call our attention to links missing title information, as well as links that have no valid target, whether through a missing URL or a JavaScript-driven page where there are no link fallbacks in the case of missing or disabled JavaScript (href="#"). And What About IE? As I said at the beginning, much of what I covered here doesn’t work in Internet Explorer, most particularly :not() and outline. (Oh, so basically everything? -Ed.) I can’t do much about the latter. For the former, however, it’s possible to hack your way around the problem by doing some layered on-off stuff. For example, for images, you replace the previously-shown rules with the following: img {border: 5px solid red;} img[alt][title] {border-width: 0;} img[alt] {border-color: fuchsia;} img[alt], img[title] {border-style: double;} img[alt=""][title], img[alt][title=""] {border-width: 3px;} img[alt=""][title=""] {border-style: dotted;} It won’t have exactly the same set of effects, given the inability to use both borders and outlines, but will still highlight troublesome images. It’s also the case that IE6 and earlier lack support for even attribute selectors, whereas IE7 added pretty much all the attribute selector types there are, so the previous code block won’t have any effect previous to IE7. In a broader sense, though, these kinds of styles probably aren’t going to be used in the wild, as it were. Diagnostic styles are something only you see as you work on a site, so you can make sure to use a browser that supports outlines and :not() when you’re diagnosing. The fact that IE users won’t see these styles is irrelevant since users of any browser probably won’t be seeing these styles. Personally, I always develop in Firefox anyway, thanks to its ability to become a full-featured IDE through the addition of extensions like Firebug and the Web Developer Toolbar. Yeah, About That… It’s true that much of what I describe in this article is available in the WDT. I feel there are two advantages to writing your own set of diagnostic styles. When you write your own styles, you can define exactly what the visual results will be, and how they will interact. The WDT doesn’t let you make its outlines thicker or change their colors. You can combine a bunch of diagnostics into a single set of rules and add it to your site’s style sheet during the diagnostic portion, thus ensuring they persist as you surf around. This can be done in the WDT, but it isn’t as easy (and, at least for me, not as reliable). It’s also true that a markup validator will catch many of the errors I mentioned, such as missing alt and summary attributes. For some, that’s sufficient. But it won’t catch everything diagnostic styles can, like empty alt values or untargeted links, which are perfectly valid, syntactically speaking. Diagnosis Complete? I hope this has been a fun look at the concept of diagnostic styling as well as a quick introduction into possibly new concepts like :not() and outlines. This isn’t all there is to say, of course: there is plenty more that could be added to a diagnostic style sheet. And everyone’s diagnostics will be different, tuned to meet each person’s unique situation. Mostly, though, I hope this small exploration triggers some creative thinking about the use of CSS to do more than just lay out pages and colorize text. Given the familiarity we acquire with CSS, it only makes sense to use it wherever it might be useful, and setting up visible diagnostic flags is just one more place for it to help us. 2007 Eric Meyer ericmeyer 2007-12-20T00:00:00+00:00 https://24ways.org/2007/diagnostic-styling/ process
162 Conditional Love “Browser.” The four-letter word of web design. I mean, let’s face it: on the good days, when things just work in your target browsers, it’s marvelous. The air smells sweeter, birds’ songs sound more melodious, and both your design and your code are looking sharp. But on the less-than-good days (which is, frankly, most of them), you’re compelled to tie up all your browsers in a sack, heave them into the nearest river, and start designing all-imagemap websites. We all play favorites, after all: some will swear by Firefox, Opera fans are allegedly legion, and others still will frown upon anything less than the latest WebKit nightly. Thankfully, we do have an out for those little inconsistencies that crop up when dealing with cross-browser testing: CSS patches. Spare the Rod, Hack the Browser Before committing browsercide over some rendering bug, a designer will typically reach for a snippet of CSS fix the faulty browser. Historically referred to as “hacks,” I prefer Dan Cederholm’s more client-friendly alternative, “patches”. But whatever you call them, CSS patches all work along the same principle: supply the proper property value to the good browsers, while giving higher maintenance other browsers an incorrect value that their frustrating idiosyncratic rendering engine can understand. Traditionally, this has been done either by exploiting incomplete CSS support: #content { height: 1%; // Let's force hasLayout for old versions of IE. line-height: 1.6; padding: 1em; } html>body #content { height: auto; // Modern browsers get a proper height value. } or by exploiting bugs in their rendering engine to deliver alternate style rules: #content p { font-size: .8em; /* Hide from Mac IE5 \*/ font-size: .9em; /* End hiding from Mac IE5 */ } We’ve even used these exploits to serve up whole stylesheets altogether: @import url("core.css"); @media tty { i{content:"\";/*" "*/}} @import 'windows-ie5.css'; /*";} }/* */ The list goes on, and on, and on. For every browser, for every bug, there’s a patch available to fix some rendering bug. But after some time working with standards-based layouts, I’ve found that CSS patches, as we’ve traditionally used them, become increasingly difficult to maintain. As stylesheets are modified over the course of a site’s lifetime, inline fixes we’ve written may become obsolete, making them difficult to find, update, or prune out of our CSS. A good patch requires a constant gardener to ensure that it adds more than just bloat to a stylesheet, and inline patches can be very hard to weed out of a decently sized CSS file. Giving the Kids Separate Rooms Since I joined Airbag Industries earlier this year, every project we’ve worked on has this in the head of its templates: <link rel="stylesheet" href="-/css/screen/main.css" type="text/css" media="screen, projection" /> <!--[if lt IE 7]> <link rel="stylesheet" href="-/css/screen/patches/win-ie-old.css" type="text/css" media="screen, projection" /> <![endif]--> <!--[if gte IE 7]> <link rel="stylesheet" href="-/css/screen/patches/win-ie7-up.css" type="text/css" media="screen, projection" /> <![endif]--> The first element is, simply enough, a link element that points to the project’s main CSS file. No patches, no hacks: just pure, modern browser-friendly style rules. Which, nine times out of ten, will net you a design that looks like spilled eggnog in various versions of Internet Explorer. But don’t reach for the mulled wine quite yet. Immediately after, we’ve got a brace of conditional comments wrapped around two other link elements. These odd-looking comments allow us to selectively serve up additional stylesheets just to the version of IE that needs them. We’ve got one for IE 6 and below: <!--[if lt IE 7]> <link rel="stylesheet" href="-/css/screen/patches/win-ie-old.css" type="text/css" media="screen, projection" /> <![endif]--> And another for IE7 and above: <!--[if gte IE 7]> <link rel="stylesheet" href="-/css/screen/patches/win-ie7-up.css" type="text/css" media="screen, projection" /> <![endif]--> Microsoft’s conditional comments aren’t exactly new, but they can be a valuable alternative to cooking CSS patches directly into a master stylesheet. And though they’re not a W3C-approved markup structure, I think they’re just brilliant because they innovate within the spec: non-IE devices will assume that the comments are just that, and ignore the markup altogether. This does, of course, mean that there’s a little extra markup in the head of our documents. But this approach can seriously cut down on the unnecessary patches served up to the browsers that don’t need them. Namely, we no longer have to write rules like this in our main stylesheet: #content { height: 1%; // Let's force hasLayout for old versions of IE. line-height: 1.6; padding: 1em; } html>body #content { height: auto; // Modern browsers get a proper height value. } Rather, we can simply write an un-patched rule in our core stylesheet: #content { line-height: 1.6; padding: 1em; } And now, our patch for older versions of IE goes in—you guessed it—the stylesheet for older versions of IE: #content { height: 1%; } The hasLayout patch is applied, our design’s repaired, and—most importantly—the patch is only seen by the browser that needs it. The “good” browsers don’t have to incur any added stylesheet weight from our IE patches, and Internet Explorer gets the conditional love it deserves. Most importantly, this “compartmentalized” approach to CSS patching makes it much easier for me to patch and maintain the fixes applied to a particular browser. If I need to track down a bug for IE7, I don’t need to scroll through dozens or hundreds of rules in my core stylesheet: instead, I just open the considerably slimmer IE7-specific patch file, make my edits, and move right along. Even Good Children Misbehave While IE may occupy the bulk of our debugging time, there’s no denying that other popular, modern browsers will occasionally disagree on how certain bits of CSS should be rendered. But without something as, well, pimp as conditional comments at our disposal, how do we bring the so-called “good browsers” back in line with our design? Assuming you’re loving the “one patch file per browser” model as much as I do, there’s just one alternative: JavaScript. function isSaf() { var isSaf = (document.childNodes && !document.all && !navigator.taintEnabled && !navigator.accentColorName) ? true : false; return isSaf; } function isOp() { var isOp = (window.opera) ? true : false; return isOp; } Instead of relying on dotcom-era tactics of parsing the browser’s user-agent string, we’re testing here for support for various DOM objects, whose presence or absence we can use to reasonably infer the browser we’re looking at. So running the isOp() function, for example, will test for Opera’s proprietary window.opera object, and thereby accurately tell you if your user’s running Norway’s finest browser. With scripts such as isOp() and isSaf() in place, you can then reasonably test which browser’s viewing your content, and insert additional link elements as needed. function loadPatches(dir) { if (document.getElementsByTagName() && document.createElement()) { var head = document.getElementsByTagName("head")[0]; if (head) { var css = new Array(); if (isSaf()) { css.push("saf.css"); } else if (isOp()) { css.push("opera.css"); } if (css.length) { var link = document.createElement("link"); link.setAttribute("rel", "stylesheet"); link.setAttribute("type", "text/css"); link.setAttribute("media", "screen, projection"); for (var i = 0; i < css.length; i++) { var tag = link.cloneNode(true); tag.setAttribute("href", dir + css[0]); head.appendChild(tag); } } } } } Here, we’re testing the results of isSaf() and isOp(), one after the other. For each function that returns true, then the name of a new stylesheet is added to the oh-so-cleverly named css array. Then, for each entry in css, we create a new link element, point it at our patch file, and insert it into the head of our template. Fire it up using your favorite onload or DOMContentLoaded function, and you’re good to go. Scripteat Emptor At this point, some of the audience’s more conscientious ‘scripters may be preparing to lob figgy pudding at this author’s head. And that’s perfectly understandable; relying on JavaScript to patch CSS chafes a bit against the normally clean separation we have between our pages’ content, presentation, and behavior layers. And beyond the philosophical concerns, this approach comes with a few technical caveats attached: Browser detection? So un-133t. Browser detection is not something I’d typically recommend. Whenever possible, a proper DOM script should check for the support of a given object or method, rather than the device with which your users view your content. It’s JavaScript, so don’t count on it being available. According to one site, roughly four percent of Internet users don’t have JavaScript enabled. Your site’s stats might be higher or lower than this number, but still: don’t expect that every member of your audience will see these additional stylesheets, and ensure that your content’s still accessible with JS turned off. Be a constant gardener. The sample isSaf() and isOp() functions I’ve written will tell you if the user’s browser is Safari or Opera. As a result, stylesheets written to patch issues in an old browser may break when later releases repair the relevant CSS bugs. You can, of course, add logic to these simple little scripts to serve up version-specific stylesheets, but that way madness may lie. In any event, test your work vigorously, and keep testing it when new versions of the targeted browsers come out. Make sure that a patch written today doesn’t become a bug tomorrow. Patching Firefox, Opera, and Safari isn’t something I’ve had to do frequently: still, there have been occasions where the above script’s come in handy. Between conditional comments, careful CSS auditing, and some judicious JavaScript, browser-based bugs can be handled with near-surgical precision. So pass the ‘nog. It’s patchin’ time. 2007 Ethan Marcotte ethanmarcotte 2007-12-15T00:00:00+00:00 https://24ways.org/2007/conditional-love/ code
155 Minification: A Christmas Diet The festive season is generally more about gorging ourselves than staying thin but we’re going to change all that with a quick introduction to minification. Performance has been a hot topic this last year. We’re building more complex sites and applications but at the same time trying to make then load faster and behave more responsively. What is a discerning web developer to do? Minification is the process of make something smaller, in the case of web site performance we’re talking about reducing the size of files we send to the browser. The primary front-end components of any website are HTML, CSS, Javascript and a sprinkling of images. Let’s find some tools to trim the fat and speed up our sites. For those that want to play along at home you can download the various utilities for Mac or Windows. You’ll want to be familiar with running apps on the command line too. HTMLTidy HTMLTidy optimises and strips white space from HTML documents. It also has a pretty good go at correcting any invalid markup while it’s at it. tidy -m page.html CSSTidy CSSTidy takes your CSS file, optimises individual rules (for instance transforming padding-top: 10px; padding-bottom: 10px; to padding: 10px 0;) and strips unneeded white space. csstidy style.css style-min.css JSMin JSMin takes your javascript and makes it more compact. With more and more websites using javascript to power (progressive) enhancements this can be a real bandwidth hog. Look out for pre-minified versions of libraries and frameworks too. jsmin <script.js >script-min.js Remember to run JSLint before you run JSMin to catch some common problems. OptiPNG Images can be a real bandwidth hog and making all of them smaller with OptiPNG should speed up your site. optipng image.png All of these tools have an often bewildering array of options and generally good documentation included as part of the package. A little experimentation will get you even more bang for your buck. For larger projects you likely won’t want to be manually minifying all your files. The best approach here is to integrate these tools into your build process and have your live website come out the other side smaller than it went in. You can also do things on the server to speed things up; GZIP compression for instance or compilation of resources to reduce the number of HTTP requests. If you’re interested in performance a good starting point is the Exceptional Performance section on the Yahoo Developer Network and remember to install the YSlow Firebug extension while you’re at it. 2007 Gareth Rushgrove garethrushgrove 2007-12-06T00:00:00+00:00 https://24ways.org/2007/minification-a-christmas-diet/ process
147 Christmas Is In The AIR That’s right, Christmas is coming up fast and there’s plenty of things to do. Get the tree and lights up, get the turkey, buy presents and who know what else. And what about Santa? He’s got a list. I’m pretty sure he’s checking it twice. Sure, we could use an existing list making web site or even a desktop widget. But we’re geeks! What’s the fun in that? Let’s build our own to-do list application and do it with Adobe AIR! What’s Adobe AIR? Adobe AIR, formerly codenamed Apollo, is a runtime environment that runs on both Windows and OSX (with Linux support to follow). This runtime environment lets you build desktop applications using Adobe technologies like Flash and Flex. Oh, and HTML. That’s right, you web standards lovin’ maniac. You can build desktop applications that can run cross-platform using the trio of technologies, HTML, CSS and JavaScript. If you’ve tried developing with AIR before, you’ll need to get re-familiarized with the latest beta release as many things have changed since the last one (such as the API and restrictions within the sandbox.) To get started To get started in building an AIR application, you’ll need two basic things: The AIR runtime. The runtime is needed to run any AIR-based application. The SDK. The software development kit gives you all the pieces to test your application. Unzip the SDK into any folder you wish. You’ll also want to get your hands on the JavaScript API documentation which you’ll no doubt find yourself getting into before too long. (You can download it, too.) Also of interest, some development environments have support for AIR built right in. Aptana doesn’t have support for beta 3 yet but I suspect it’ll be available shortly. Within the SDK, there are two main tools that we’ll use: one to test the application (ADL) and another to build a distributable package of our application (ADT). I’ll get into this some more when we get to that stage of development. Building our To-do list application The first step to building an application within AIR is to create an XML file that defines our default application settings. I call mine application.xml, mostly because Aptana does that by default when creating a new AIR project. It makes sense though and I’ve stuck with it. Included in the templates folder of the SDK is an example XML file that you can use. The first key part to this after specifying things like the application ID, version, and filename, is to specify what the default content should be within the content tags. Enter in the name of the HTML file you wish to load. Within this HTML file will be our application. <content>ui.html</content> Create a new HTML document and name it ui.html and place it in the same directory as the application.xml file. The first thing you’ll want to do is copy over the AIRAliases.js file from the frameworks folder of the SDK and add a link to it within your HTML document. <script type="text/javascript" src="AIRAliases.js"></script> The aliases create shorthand links to all of the Flash-based APIs. Now is probably a good time to explain how to debug your application. Debugging our application So, with our XML file created and HTML file started, let’s try testing our ‘application’. We’ll need the ADL application located in BIN folder of the SDK and tell it to run the application.xml file. /path/to/adl /path/to/application.xml You can also just drag the XML file onto ADL and it’ll accomplish the same thing. If you just did that and noticed that your blank application didn’t load, you’d be correct. It’s running but isn’t visible. Which at this point means you’ll have to shut down the ADL process. Sorry about that! Changing the visibility You have two ways to make your application visible. You can do it automatically by setting the placing true in the visible tag within the application.xml file. <visible>true</visible> The other way is to do it programmatically from within your application. You’d want to do it this way if you had other startup tasks to perform before showing the interface. To turn the UI on programmatically, simple set the visible property of nativeWindow to true. <script type="text/javascript"> nativeWindow.visible = true; </script> Sandbox Security Now that we have an application that we can see when we start it, it’s time to build the to-do list application. In doing so, you’d probably think that using a JavaScript library is a really good idea — and it can be but there are some limitations within AIR that have to be considered. An HTML document, by default, runs within the application sandbox. You have full access to the AIR APIs but once the onload event of the window has fired, you’ll have a limited ability to make use of eval and other dynamic script injection approaches. This limits the ability of external sources from gaining access to everything the AIR API offers, such as database and local file system access. You’ll still be able to make use of eval for evaluating JSON responses, which is probably the most important if you wish to consume JSON-based services. If you wish to create a greater wall of security between AIR and your HTML document loading in external resources, you can create a child sandbox. We won’t need to worry about it for our application so I won’t go any further into it but definitely keep this in mind. Finally, our application Getting tired of all this preamble? Let’s actually build our to-do list application. I’ll use jQuery because it’s small and should suit our needs nicely. Let’s begin with some structure: <body> <input type="text" id="text" value=""> <input type="button" id="add" value="Add"> <ul id="list"></ul> </body> Now we need to wire up that button to actually add a new item to our to-do list. <script type="text/javascript"> $(document).ready(function(){ // make sure the application is visible nativeWindow.visible = true; $('#add').click(function(){ var t = $('#text').val(); if(t) { // use DOM methods to create the new list item var li = document.createElement('li'); // the extra space at the end creates a buffer between the text // and the delete link we're about to add li.appendChild(document.createTextNode(t + ' ')); // create the delete link var del = document.createElement('a'); // this makes it a true link. I feel dirty doing this. del.setAttribute('href', '#'); del.addEventListener('click', function(evt){ this.parentNode.parentNode.removeChild(this.parentNode); }); del.appendChild(document.createTextNode('[del]')); li.appendChild(del); // append everything to the list $('#list').append(li); //reset the text box $('#text').val(''); } }) }); </script> And just like that, we’ve got a to-do list! That’s it! Just never close your application and you’ll remember everything. Okay, that’s not very practical. You need to have some way of storing your to-do items until the next time you open up the application. Storing Data You’ve essentially got 4 different ways that you can store data: Using the local database. AIR comes with SQLLite built in. That means you can create tables and insert, update and select data from that database just like on a web server. Using the file system. You can also create files on the local machine. You have access to a few folders on the local system such as the documents folder and the desktop. Using EcryptedLocalStore. I like using the EcryptedLocalStore because it allows you to easily save key/value pairs and have that information encrypted. All this within just a couple lines of code. Sending the data to a remote API. Our to-do list could sync up with Remember the Milk, for example. To demonstrate some persistence, we’ll use the file system to store our files. In addition, we’ll let the user specify where the file should be saved. This way, we can create multiple to-do lists, keeping them separate and organized. The application is now broken down into 4 basic tasks: Load data from the file system. Perform any interface bindings. Manage creating and deleting items from the list. Save any changes to the list back to the file system. Loading in data from the file system When the application starts up, we’ll prompt the user to select a file or specify a new to-do list. Within AIR, there are 3 main file objects: File, FileMode, and FileStream. File handles file and path names, FileMode is used as a parameter for the FileStream to specify whether the file should be read-only or for write access. The FileStream object handles all the read/write activity. The File object has a number of shortcuts to default paths like the documents folder, the desktop, or even the application store. In this case, we’ll specify the documents folder as the default location and then use the browseForSave method to prompt the user to specify a new or existing file. If the user specifies an existing file, they’ll be asked whether they want to overwrite it. var store = air.File.documentsDirectory; var fileStream = new air.FileStream(); store.browseForSave("Choose To-do List"); Then we add an event listener for when the user has selected a file. When the file is selected, we check to see if the file exists and if it does, read in the contents, splitting the file on new lines and creating our list items within the interface. store.addEventListener(air.Event.SELECT, fileSelected); function fileSelected() { air.trace(store.nativePath); // load in any stored data var byteData = new air.ByteArray(); if(store.exists) { fileStream.open(store, air.FileMode.READ); fileStream.readBytes(byteData, 0, store.size); fileStream.close(); if(byteData.length > 0) { var s = byteData.readUTFBytes(byteData.length); oldlist = s.split(“\r\n”); // create todolist items for(var i=0; i < oldlist.length; i++) { createItem(oldlist[i], (new Date()).getTime() + i ); } } } } Perform Interface Bindings This is similar to before where we set the click event on the Add button but we’ve moved the code to save the list into a separate function. $('#add').click(function(){ var t = $('#text').val(); if(t){ // create an ID using the time createItem(t, (new Date()).getTime() ); } }) Manage creating and deleting items from the list The list management is now in its own function, similar to before but with some extra information to identify list items and with calls to save our list after each change. function createItem(t, id) { if(t.length == 0) return; // add it to the todo list todolist[id] = t; // use DOM methods to create the new list item var li = document.createElement('li'); // the extra space at the end creates a buffer between the text // and the delete link we're about to add li.appendChild(document.createTextNode(t + ' ')); // create the delete link var del = document.createElement('a'); // this makes it a true link. I feel dirty doing this. del.setAttribute('href', '#'); del.addEventListener('click', function(evt){ var id = this.id.substr(1); delete todolist[id]; // remove the item from the list this.parentNode.parentNode.removeChild(this.parentNode); saveList(); }); del.appendChild(document.createTextNode('[del]')); del.id = 'd' + id; li.appendChild(del); // append everything to the list $('#list').append(li); //reset the text box $('#text').val(''); saveList(); } Save changes to the file system Any time a change is made to the list, we update the file. The file will always reflect the current state of the list and we’ll never have to click a save button. It just iterates through the list, adding a new line to each one. function saveList(){ if(store.isDirectory) return; var packet = ''; for(var i in todolist) { packet += todolist[i] + '\r\n'; } var bytes = new air.ByteArray(); bytes.writeUTFBytes(packet); fileStream.open(store, air.FileMode.WRITE); fileStream.writeBytes(bytes, 0, bytes.length); fileStream.close(); } One important thing to mention here is that we check if the store is a directory first. The reason we do this goes back to our browseForSave call. If the user cancels the dialog without selecting a file first, then the store points to the documentsDirectory that we set it to initially. Since we haven’t specified a file, there’s no place to save the list. Hopefully by this point, you’ve been thinking of some cool ways to pimp out your list. Now we need to package this up so that we can let other people use it, too. Creating a Package Now that we’ve created our application, we need to package it up so that we can distribute it. This is a two step process. The first step is to create a code signing certificate (or you can pay for one from Thawte which will help authenticate you as an AIR application developer). To create a self-signed certificate, run the following command. This will create a PFX file that you’ll use to sign your application. adt -certificate -cn todo24ways 1024-RSA todo24ways.pfx mypassword After you’ve done that, you’ll need to create the package with the certificate adt -package -storetype pkcs12 -keystore todo24ways.pfx todo24ways.air application.xml . The important part to mention here is the period at the end of the command. We’re telling it to package up all files in the current directory. After that, just run the AIR file, which will install your application and run it. Important things to remember about AIR When developing an HTML application, the rendering engine is Webkit. You’ll thank your lucky stars that you aren’t struggling with cross-browser issues. (My personal favourites are multiple backgrounds and border radius!) Be mindful of memory leaks. Things like Ajax calls and event binding can cause applications to slowly leak memory over time. Web pages are normally short lived but desktop applications are often open for hours, if not days, and you may find your little desktop application taking up more memory than anything else on your machine! The WebKit runtime itself can also be a memory hog, usually taking about 15MB just for itself. If you create multiple HTML windows, it’ll add another 15MB to your memory footprint. Our little to-do list application shouldn’t be much of a concern, though. The other important thing to remember is that you’re still essentially running within a Flash environment. While you probably won’t notice this working in small applications, the moment you need to move to multiple windows or need to accomplish stuff beyond what HTML and JavaScript can give you, the need to understand some of the Flash-based elements will become more important. Lastly, the other thing to remember is that HTML links will load within the AIR application. If you want a link to open in the users web browser, you’ll need to capture that event and handle it on your own. The following code takes the HREF from a clicked link and opens it in the default web browser. air.navigateToURL(new air.URLRequest(this.href)); Only the beginning Of course, this is only the beginning of what you can do with Adobe AIR. You don’t have the same level of control as building a native desktop application, such as being able to launch other applications, but you do have more control than what you could have within a web application. Check out the Adobe AIR Developer Center for HTML and Ajax for tutorials and other resources. Now, go forth and create your desktop applications and hopefully you finish all your shopping before Christmas! Download the example files. 2007 Jonathan Snook jonathansnook 2007-12-19T00:00:00+00:00 https://24ways.org/2007/christmas-is-in-the-air/ code
148 Typesetting Tables Tables have suffered in recent years on the web. They were used for laying out web pages. Then, following the Web Standards movement, they’ve been renamed by the populous as `data tables’ to ensure that we all know what they’re for. There have been some great tutorials for the designing tables using CSS for presentation and focussing on the semantics in the displaying of data in the correct way. However, typesetting tables is a subtle craft that has hardly had a mention. Table design can often end up being a technical exercise. What data do we need to display? Where is the data coming from and what form will it take? When was the last time your heard someone talk about lining numerals? Or designing to the reading direction? Tables are not read like sentences When a reader looks at, and tries to understand, tabular data, they’re doing a bunch of things at the same time. Generally, they’re task based; they’re looking for something. They are reading horizontally AND vertically Reading a table is not like reading a paragraph in a novel, and therefore shouldn’t be typeset in the same way. Designing tables is information design, it’s functional typography—it’s not a time for eye candy. Typesetting tables Typesetting great looking tables is largely an exercise in restraint. Minimal interference with the legibility of the table should be in the forefront of any designers mind. When I’m designing tables I apply some simple rules: Plenty of negative space Use the right typeface Go easy on the background tones, unless you’re giving reading direction visual emphasis Design to the reading direction By way of explanation, here are those rules as applied to the following badly typeset table. Your default table This table is a mess. There is no consideration for the person trying to read it. Everything is too tight. The typeface is wrong. It’s flat. A grim table indeed. Let’s see what we can do about that. Plenty of negative space The badly typeset table has been set with default padding. There has been little consideration for the ascenders and descenders in the type interfering with the many horizontal rules. The first thing we do is remove most of the lines, or rules. You don’t need them – the data in the rows forms its own visual rules. Now, with most of the rules removed, the ones that remain mean something; they are indicating some kind of hierarchy to the help the reader understand what the different table elements mean – in this case the column headings. Now we need to give the columns and rows more negative space. Note the framing of the column headings. I’m giving them more room at the bottom. This negative space is active—it’s empty for a reason. The extra air in here also gives more hierarchy to the column headings. Use the right typeface The default table is set in a serif typeface. This isn’t ideal for a couple of reasons. This serif typeface has a standard set of text numerals. These dip below the baseline and are designed for using figures within text, not on their own. What you need to use is a typeface with lining numerals. These align to the baseline and are more legible when used for tables. Sans serif typefaces generally have lining numerals. They are also arguably more legible when used in tables. Go easy on the background tones, unless you’re giving reading direction visual emphasis We’ve all seen background tones on tables. They have their use, but my feeling is that use should be functional and not decorative. If you have a table that is long, but only a few columns wide, then alternate row shading isn’t that useful for showing the different lines of data. It’s a common misconception that alternate row shading is to increase legibility on long tables. That’s not the case. Shaded rows are to aid horizontal reading across multiple table columns. On wide tables they are incredibly useful for helping the reader find what they want. Background tone can also be used to give emphasis to the reading direction. If we want to emphasis a column, that can be given a background tone. Hierarchy As I said earlier, people may be reading a table vertically, and horizontally in order to find what they want. Sometimes, especially if the table is complex, we need to give them a helping hand. Visually emphasising the hierarchy in tables can help the reader scan the data. Column headings are particularly important. Column headings are often what a reader will go to first, so we need to help them understand that the column headings are different to the stuff beneath them, and we also need to give them more visual importance. We can do this by making them bold, giving them ample negative space, or by including a thick rule above them. We can also give the row titles the same level of emphasis. In addition to background tones, you can give emphasis to reading direction by typesetting those elements in bold. You shouldn’t use italics—with sans serif typefaces the difference is too subtle. So, there you have it. A couple of simple guidelines to make your tables cleaner and more readable. 2007 Mark Boulton markboulton 2007-12-07T00:00:00+00:00 https://24ways.org/2007/typesetting-tables/ design
164 My Other Christmas Present Is a Definition List A note from the editors: readers should note that the HTML5 redefinition of definition lists has come to pass and is now à la mode. Last year, I looked at how the markup for tag clouds was generally terrible. I thought this year I would look not at a method of marking up a common module, but instead just at a simple part of HTML and how it generally gets abused. No, not tables. Definition lists. Ah, definition lists. Often used but rarely understood. Examining the definition of definitions To start with, let’s see what the HTML spec has to say about them. Definition lists vary only slightly from other types of lists in that list items consist of two parts: a term and a description. The canonical example of a definition list is a dictionary. Words can have multiple descriptions (even the word definition has at least five). Also, many terms can share a single definition (for example, the word colour can also be spelt color, but they have the same definition). Excellent, we can all grasp that. But it very quickly starts to fall apart. Even in the HTML specification the definition list is mis-used. Another application of DL, for example, is for marking up dialogues, with each DT naming a speaker, and each DD containing his or her words. Wrong. Completely and utterly wrong. This is the biggest flaw in the HTML spec, along with dropping support for the start attribute on ordered lists. “Why?”, you may ask. Let me give you an example from Romeo and Juliet, act 2, scene 2. <dt>Juliet</dt> <dd>Romeo!</dd> <dt>Romeo</dt> <dd>My niesse?</dd> <dt>Juliet</dt> <dd>At what o'clock tomorrow shall I send to thee?</dd> <dt>Romeo</dt> <dd>At the hour of nine.</dd> Now, the problem here is that a given definition can have multiple descriptions (the DD). Really the dialog “descriptions” should be rolled up under the terms, like so: <dt>Juliet</dt> <dd>Romeo!</dd> <dd>At what o'clock tomorrow shall I send to thee?</dd> <dt>Romeo</dt> <dd>My niesse?</dd> <dd>At the hour of nine.</dd> Suddenly the play won’t make anywhere near as much sense. (If it’s anything, the correct markup for a play is an ordered list of CITE and BLOCKQUOTE elements.) This is the first part of the problem. That simple example has turned definition lists in everyone’s mind from pure definitions to more along the lines of a list with pre-configured heading(s) and text(s). Screen reader, enter stage left. In many screen readers, a simple definition list would be read out as “definition term equals definition description”. So in our play excerpt, Juliet equals Romeo! That’s not right, either. But this also leads a lot of people astray with definition lists to believing that they are useful for key/value pairs. Behaviour and convention The WHAT-WG have noticed the common mis-use of the DL, and have codified it into the new spec. In the HTML5 draft, a definition list is no longer a definition list. The dl element introduces an unordered association list consisting of zero or more name-value groups (a description list). Each group must consist of one or more names (dt elements) followed by one or more values (dd elements). They also note that the “dl element is inappropriate for marking up dialogue, since dialogue is ordered”. So for that example they have created a DIALOG (sic) element. Strange, then, that they keep DL as-is but instead refer to it an “association list”. They have not created a new AL element, and kept DL for the original purpose. They have chosen not to correct the usage or to create a new opportunity for increased specificity in our HTML, but to “pave the cowpath” of convention. How to use a definition list Given that everyone else is using a DL incorrectly, should we? Well, if they all jumped off a bridge, would you too? No, of course you wouldn’t. We don’t have HTML5 yet, so we’re stuck with the existing semantics of HTML4 and XHTML1. Which means that: Listing dialogue is not defining anything. Listing the attributes of a piece of hardware (resolution = 1600×1200) is illustrating sample values, not defining anything (however, stating what ‘resolution’ actually means in this context would be a definition). Listing the cast and crew of a given movie is not defining the people involved in making movies. (Stuart Gordon may have been the director of Space Truckers, but that by no means makes him the true definition of a director.) A menu of navigation items is simply a nested ordered or unordered list of links, not a definition list. Applying styling handles to form labels and elements is not a good use for a definition list. And so on. Living by the specification, a definition list should be used for term definitions – glossaries, lexicons and dictionaries – only. Anything else is a crime against markup. 2007 Mark Norman Francis marknormanfrancis 2007-12-05T00:00:00+00:00 https://24ways.org/2007/my-other-christmas-present-is-a-definition-list/ code
153 JavaScript Internationalisation or: Why Rudolph Is More Than Just a Shiny Nose Dunder sat, glumly staring at the computer screen. “What’s up, Dunder?” asked Rudolph, entering the stable and shaking off the snow from his antlers. “Well,” Dunder replied, “I’ve just finished coding the new reindeer intranet Santa Claus asked me to do. You know how he likes to appear to be at the cutting edge, talking incessantly about Web 2.0, AJAX, rounded corners; he even spooked Comet recently by talking about him as if he were some pushy web server. “I’ve managed to keep him happy, whilst also keeping it usable, accessible, and gleaming — and I’m still on the back row of the sleigh! But anyway, given the elves will be the ones using the site, and they come from all over the world, the site is in multiple languages. Which is great, except when it comes to the preview JavaScript I’ve written for the reindeer order form. Here, have a look…” As he said that, he brought up the textileRef:8234272265470b85d91702:linkStartMarker:“order form in French”:/examples/javascript-internationalisation/initial.fr.html on the screen. (Same in English). “Looks good,” said Rudolph. “But if I add some items,” said Dunder, “the preview appears in English, as it’s hard-coded in the JavaScript. I don’t want separate code for each language, as that’s just silly — I thought about just having if statements, but that doesn’t scale at all…” “And there’s more, you aren’t displaying large numbers in French properly, either,” added Rudolph, who had been playing and looking at part of the source code: function update_text() { var hay = getValue('hay'); var carrots = getValue('carrots'); var bells = getValue('bells'); var total = 50 * bells + 30 * hay + 10 * carrots; var out = 'You are ordering ' + pretty_num(hay) + ' bushel' + pluralise(hay) + ' of hay, ' + pretty_num(carrots) + ' carrot' + pluralise(carrots) + ', and ' + pretty_num(bells) + ' shiny bell' + pluralise(bells) + ', at a total cost of <strong>' + pretty_num(total) + '</strong> gold pieces. Thank you.'; document.getElementById('preview').innerHTML = out; } function pretty_num(n) { n += ''; var o = ''; for (i=n.length; i>3; i-=3) { o = ',' + n.slice(i-3, i) + o; } o = n.slice(0, i) + o; return o; } function pluralise(n) { if (n!=1) return 's'; return ''; } “Oh, botheration!” cried Dunder. “This is just so complicated.” “It doesn’t have to be,” said Rudolph, “you just have to think about things in a slightly different way from what you’re used to. As we’re only a simple example, we won’t be able to cover all possibilities, but for starters, we need some way of providing different information to the script dependent on the language. We’ll create a global i18n object, say, and fill it with the correct language information. The first variable we’ll need will be a thousands separator, and then we can change the pretty_num function to use that instead: function pretty_num(n) { n += ''; var o = ''; for (i=n.length; i>3; i-=3) { o = i18n.thousands_sep + n.slice(i-3, i) + o; } o = n.slice(0, i) + o; return o; } “The i18n object will also contain our translations, which we will access through a function called _() — that’s just an underscore. Other languages have a function of the same name doing the same thing. It’s very simple: function _(s) { if (typeof(i18n)!='undefined' && i18n[s]) { return i18n[s]; } return s; } “So if a translation is available and provided, we’ll use that; otherwise we’ll default to the string provided — which is helpful if the translation begins to lag behind the site’s text at all, as at least something will be output.” “Got it,” said Dunder. “ _('Hello Dunder') will print the translation of that string, if one exists, ‘Hello Dunder’ if not.” “Exactly. Moving on, your plural function breaks even in English if we have a word where the plural doesn’t add an s — like ‘children’.” “You’re right,” said Dunder. “How did I miss that?” “No harm done. Better to provide both singular and plural words to the function and let it decide which to use, performing any translation as well: function pluralise(s, p, n) { if (n != 1) return _(p); return _(s); } “We’d have to provide different functions for different languages as we employed more elves and got more complicated — for example, in Polish, the word ‘file’ pluralises like this: 1 plik, 2-4 pliki, 5-21 plików, 22-24 pliki, 25-31 plików, and so on.” (More information on plural forms) “Gosh!” “Next, as different languages have different word orders, we must stop using concatenation to construct sentences, as it would be impossible for other languages to fit in; we have to keep coherent strings together. Let’s rewrite your update function, and then go through it: function update_text() { var hay = getValue('hay'); var carrots = getValue('carrots'); var bells = getValue('bells'); var total = 50 * bells + 30 * hay + 10 * carrots; hay = sprintf(pluralise('%s bushel of hay', '%s bushels of hay', hay), pretty_num(hay)); carrots = sprintf(pluralise('%s carrot', '%s carrots', carrots), pretty_num(carrots)); bells = sprintf(pluralise('%s shiny bell', '%s shiny bells', bells), pretty_num(bells)); var list = sprintf(_('%s, %s, and %s'), hay, carrots, bells); var out = sprintf(_('You are ordering %s, at a total cost of <strong>%s</strong> gold pieces.'), list, pretty_num(total)); out += ' '; out += _('Thank you.'); document.getElementById('preview').innerHTML = out; } “ sprintf is a function in many other languages that, given a format string and some variables, slots the variables into place within the string. JavaScript doesn’t have such a function, so we’ll write our own. Again, keep it simple for now, only integers and strings; I’m sure more complete ones can be found on the internet. function sprintf(s) { var bits = s.split('%'); var out = bits[0]; var re = /^([ds])(.*)$/; for (var i=1; i<bits.length; i++) { p = re.exec(bits[i]); if (!p || arguments[i]==null) continue; if (p[1] == 'd') { out += parseInt(arguments[i], 10); } else if (p[1] == 's') { out += arguments[i]; } out += p[2]; } return out; } “Lastly, we need to create one file for each language, containing our i18n object, and then include that from the relevant HTML. Here’s what a blank translation file would look like for your order form: var i18n = { thousands_sep: ',', "%s bushel of hay": '', "%s bushels of hay": '', "%s carrot": '', "%s carrots": '', "%s shiny bell": '', "%s shiny bells": '', "%s, %s, and %s": '', "You are ordering %s, at a total cost of <strong>%s</strong> gold pieces.": '', "Thank you.": '' }; “If you implement this across the intranet, you’ll want to investigate the xgettext program, which can automatically extract all strings that need translating from all sorts of code files into a standard .po file (I think Python mode works best for JavaScript). You can then use a different program to take the translated .po file and automatically create the language-specific JavaScript files for us.” (e.g. German .po file for PledgeBank, mySociety’s .po-.js script, example output) With a flourish, Rudolph finished editing. “And there we go, localised JavaScript in English, French, or German, all using the same main code.” “Thanks so much, Rudolph!” said Dunder. “I’m not just a pretty nose!” Rudolph quipped. “Oh, and one last thing — please comment liberally explaining the context of strings you use. Your translator will thank you, probably at the same time as they point out the four hundred places you’ve done something in code that only works in your language and no-one else’s…” Thanks to Tim Morley and Edmund Grimley Evans for the French and German translations respectively. 2007 Matthew Somerville matthewsomerville 2007-12-08T00:00:00+00:00 https://24ways.org/2007/javascript-internationalisation/ code
159 How Media Studies Can Massage Your Message A young web designer once told his teacher ‘just get to the meat already.’ He was frustrated with what he called the ‘history lessons and name-dropping’ aspects of his formal college course. He just wanted to learn how to build Web sites, not examine the reasons why. Technique and theory are both integrated and necessary portions of a strong education. The student’s perspective has direct value, but also holds a distinct sorrow: Knowing the how without the why creates a serious problem. Without these surrounding insights we cannot tap into the influence of the history and evolved knowledge that came before. We cannot properly analyze, criticize, evaluate and innovate beyond the scope of technique. History holds the key to transcending former mistakes. Philosophy encourages us to look at different points of view. Studying media and social history empowers us as Web workers by bringing together myriad aspects of humanity in direct relation to the environment of society and technology. Having an understanding of media, communities, communication arts as well as logic, language and computer savvy are all core skills of the best of web designers in today’s medium. Controlling the Message ‘The computer can’t tell you the emotional story. It can give you the exact mathematical design, but what’s missing is the eyebrows.’ – Frank Zappa Media is meant to express an idea. The great media theorist Marshall McLuhan suggests that not only is media interesting because it’s about the expression of ideas, but that the media itself actually shapes the way a given idea is perceived. This is what McLuhan meant when he uttered those famous words: ‘The medium is the message.’ If instead of actually serving a steak to a vegetarian friend, what might a painting of the steak mean instead? Or a sculpture of a cow? Depending upon the form of media in question, the message is altered. Figure 1 Must we know the history of cows to appreciate the steak on our plate? Perhaps not, but if we begin to examine how that meat came to be on the plate, the social, cultural and ideological associations of that cow, we begin to understand the complexity of both medium and message. A piece of steak on my plate makes me happy. A vegetarian friend from India might disagree and even find that that serving her a steak was very insensitive. Takeaway: Getting the message right involves understanding that message in order to direct it to your audience accordingly. A Separate Piece If we revisit the student who only wants technique, while he might become extremely adept at the rendering of an idea, without an understanding of the medium, how is he to have greater control over how that idea is perceived? Ultimately, his creativity is limited and his perspective narrowed, and the teacher has done her student a disservice without challenging him, particularly in a scholastic environment, to think in liberal, creative and ultimately innovative terms. For many years, web pundits including myself have promoted the idea of separation as a core concept within creating effective front-end media for the Web. By this, we’ve meant literal separation of the technologies and documents: Markup for content; CSS for presentation; DOM Scripting for behavior. While the message of separation was an important part of understanding and teaching best practices for manageable, scalable sites, that separation is really just a separation of pieces, not of entire disciplines. For in fact, the medium of the Web is an integrated one. That means each part of the desired message must be supported by the media silos within a given site. The visual designer must study the color, space, shape and placement of visual objects (including type) into a meaningful expression. The underlying markup is ideally written as semantically as possible, promote the meaning of the content it describes. Any interaction and functionality must make the process of the medium support, not take away from, the meaning of the site or Web application. Examination: The Glass Bead Game Figure 2 Figure 2 shows two screenshots of CoreWave’s historic ‘Glass Bead Game.’ Fashioned after Herman Hesse’s novel of the same name, the game was an exploration of how ideas are connected to other ideas via multiple forms of media. It was created for the Web in 1996 using server-side randomization with .htmlx files in order to allow players to see how random associations are in fact not random at all. Takeaway: We can use the medium itself to explore creative ideas, to link us from one idea to the next, and to help us better express those ideas to our audiences. Computers and Human Interaction Since our medium involves computers and human interaction, it does us well to look to foundations of computers and reason. Not long ago I was chatting with Jared Spool on IM about this and that, and he asked me ‘So how do you feel about that?’ This caused me no end of laughter and I instantly quipped back ‘It’s okay by me ELIZA.’ We both enjoyed the joke, but then I tried to share it with another dare I say younger colleague, and the reference was lost. Raise your hand if you got the reference! Some of you will, but many people who come to the Web medium do not get the benefit of such historical references because we are not formally educated in them. Joseph Weizenbaum created the ELIZA program, which emulates a Rogerian Therapist, in 1966. It was an early study of computers and natural human language. I was a little over 2 years old, how about you? Conversation with Eliza There are fortunately a number of ELIZA emulators on the Web. I found one at http://www.chayden.net/eliza/Eliza.html that actually contains the source code (in Java) that makes up the ELIZA script. Figure 3 shows a screen shot of the interaction. ELIZA first welcomes me, says ‘Hello, How do you do. Please state your problem’ and we continue in a short loop of conversation, the computer using cues from my answers to create new questions and leading fragments of conversation. Figure 3 Albeit a very limited demonstration of how humans could interact with a computer in 1966, it’s amusing to play with now and compare it to something as richly interactive as the Microsoft Surface (Figure 4). Here, we see clear Lucite blocks that display projected video. Each side of the block has a different view of the video, so not only does one have to match up the images as they are moving, but do so in the proper directionality. Figure 4 Takeway: the better we know our environment, the more we can alter it to emulate, expand and even supersede our message. Leveraging Holiday Cheer Since most of us at least have a few days off for the holidays now that Christmas is upon us, now’s a perfect time to reflect on ones’ environment and examine the messages within it. Convince your spouse to find you a few audio books for stocking stuffers. Find interactive games to play with your kids and observe them, and yourself, during the interaction. Pour a nice egg-nog and sit down with a copy of Marshall McLuhan’s ‘The Medium is the Massage.’ Leverage that holiday cheer and here’s to a prosperous, interactive new year. 2007 Molly Holzschlag mollyholzschlag 2007-12-22T00:00:00+00:00 https://24ways.org/2007/how-media-studies-can-massage-your-message/ ux
167 Back To The Future of Print By now we have weathered the storm that was the early days of web development, a dangerous time when we used tables, inline CSS and separate pages for print only versions. We can reflect in a haggard old sea-dog manner (“yarrr… I remember back in the browser wars…”) on the bad practices of the time. We no longer need convincing that print stylesheets are the way to go1, though some of the documentation for them is a little outdated now. I am going to briefly cover 8 tips and 4 main gotchas when creating print stylesheets in our more enlightened era. Getting started As with regular stylesheets, print CSS can be included in a number of ways2, for our purposes we are going to be using the link element. <link rel="stylesheet" type="text/css" media="print" href="print.css"> This is still my favourite way of linking to CSS files, its easy to see what files are being included and to what media they are being applied to. Without the media attribute specified the link element defaults to the media type ‘all’ which means that the styles within then apply to print and screen alike. The media type ‘screen’ only applies to the screen and wont be picked up by print, this is the best way of hiding styles from print. Make sure you include your print styles after all your other CSS, because you will need to override certain rules and this is a lot easier if you are flowing with the cascade than against it! Another thing you should be thinking is ‘does it need to be printed’. Consider the context3, if it is not a page that is likely to be printed, such as a landing page or a section index then the print styles should resemble the way the page looks on the screen. Context is really important for the design of your print stylesheet, all the tips and tricks that follow should be taken in the context of the page. If for example you are designing a print stylesheet for an item in a shopping cart, it is irrelevant for the user to know the exact url of the link that takes them to your checkout. Tips and tricks During these tip’s we are going to build up print styles for a textileRef:11112857385470b854b8411:linkStartMarker:“simple example”:/examples/back-to-the-future-of-print/demo-1.html 1. Remove the cruft First things first, navigation, headers and most page furniture are pretty much useless and dead space in print so they will need to be removed, using display:none;. 2. Linearise your content Content doesn’t work so well in columns in print, especially if the content columns are long and intend to stretch over multiple columns (as mentioned in the gotcha section below). You might want to consider Lineariseing the content to flow down the page. If you have your source order in correct priority this will make things a lot easier4. 3. Improve your type Once you have removed all the useless cruft and jiggled things about a bit, you can concentrate more on the typography of the page. Typography is a complex topic5, but simply put serif-ed fonts such as Georgia work better on print and sans serif-ed fonts such as Verdana are more appropriate for screen use. You will probably want to increase font size and line height and change from px to pt (which is specifically a print measurement). 4. Go wild on links There are some incredibly fun things you can do with links in print using CSS. There are two schools of thought, one that consider it is best to disguise inline links as body text because they are not click-able on paper. Personally I believe it is useful to know for reference that the document did link to somewhere originally. When deciding which approach to take, consider the context of your document, do people need to know where they would have gone to? will it help or hinder them to know this information? Also for an alternative to the below, take a look at Aaron Gustafson’s article on generating footnotes for print6. Using some clever selector trickery and CSS generated content you can have the location of the link generated after the link itself. HTML: <p>I wish <a href="http://www.google.com/">Google</a> could find <a href="/photoOfMyKeys.jpg">my keys</a></p> CSS: a:link:after, a:visited:after, a:hover:after, a:active:after { content: " <" attr(href) "> "; } But this is not perfect, in the above example the content of the href is just naively plonked after the link text: I wish Google <http://www.google.com/> would find my keys </photoOfMyKeys.jpg> As looking back over this printout the user is not immediately aware of the location of the link, a better solution is to use even more crazy selectors to deal with relative links. We can also add a style to the generated content so it is distinguishable from the link text itself. CSS: a:link:after, a:visited:after, a:hover:after, a:active:after { content: " <" attr(href) "> "; color: grey; font-style: italic; font-weight: normal; } a[href^="/"]:after { content: " <http://www.example.com"attr(href)"> "; } The output is now what we were looking for (you will need to replace example.com with your own root URL): I wish Google <http://www.google.com/> would find my keys <http://www.example.com/photoOfMyKeys.jpg> Using regular expressions on the attribute selectors, one final thing you can do is to suppress the generated content on mailto: links, if for example you know the link text always reflects the email address. Eg: HTML: <a href="mailto:me@example.com">me@example.com</a> CSS: a[href^="mailto"]:after { content: ""; } This example shows the above in action. Of course with this clever technique, there are the usual browser support issues. While it won’t look as intended in browsers such as Internet Explorer 6 and 7 (IE6 and IE7) it will not break either and will just degrade gracefully because IE cannot do generated content. To the best of my knowledge Safari 2+ and Opera 9.X support a colour set on generated content whereas Firefox 2 & Camino display this in black regardless of the link or inherited text colour. 5. Jazz your headers for print This is more of a design consideration, don’t go too nuts though; there are a lot more limitations in print media than on screen. For this example we are going to go for is having a bottom border on h2’s and styling other headings with graduating colors or font sizes. And here is the example complete with jazzy headers. 6. Build in general hooks If you are building a large site with many different types of page, you may find it useful to build into your CSS structure, classes that control what is printed (e.g. noprint and printonly). This may not be semantically ideal, but in the past I have found it really useful for maintainability of code across large and varied sites 7. For that extra touch of class When printing pages from a long URL, even if the option is turned on to show the location of the page in the header, browsers may still display a truncated (and thus useless) version. Using the above tip (or just simply setting to display:none in screen and display:block in print) you can insert the URL of the page you are currently on for print only, using JavaScript’s window.location.href variable. function addPrintFooter() { var p = document.createElement('p'); p.className = 'print-footer'; p.innerHTML = window.location.href; document.body.appendChild(p); } You can then call this function using whichever onload or ondomready handler suits your fancy. Here is our familiar demo to show all the above in action 8. Tabular data across pages If you are using tabled data in your document there are a number of things you can do to increase usability of long tables over several pages. If you use the <thead> element this should repeat your table headers on the next page should your table be split. You will need to set thead {display: table-header-group;} explicitly for IE even though this should be the default value. Also if you use tr {page-break-inside: avoid;} this should (browser support depending) stop your table row from breaking across two pages. For more information on styling tables for print please see the CSS discuss wiki7. Gotchas 1. Where did all my content go? Absolutely the most common mistake I see with print styles is the truncated content bug. The symptom of this is that only the first page of a div’s content will be printed, the rest will look truncated after this. Floating long columns may still have this affect, as mentioned in Eric Meyer’s article on ‘A List Apart’ article from 20028; though in testing I am no longer able to replicate this. Using overflow:hidden on long content in Firefox however still causes this truncation. Overflow hidden is commonly used to clear floats9. A simple fix can be applied to resolve this, if the column is floated you can override this with float:none similarly overflow:hidden can be overridden with overflow:visible or the offending rules can be banished to a screen only stylesheet. Using position:absolute on long columns also has a very similar effect in truncating the content, but can be overridden in print with position:static; Whilst I only recommend having a print stylesheet for content pages on your site; do at least check other landing pages, section indexes and your homepage. If these are inaccessible in print possibly due to the above gotcha, it might be wise to provide a light dusting of print styles or move the offending overflow / float rules to a screen only stylesheet to fix the issues. 2. Damn those background browser settings One of the factors of life you now need to accept is that you can’t control the user’s browser settings, no more than you can control whether or not they use IE6. Most browsers by default will not print background colours or images unless explicitly told to by the user. Naturally this causes a number of problems, for starters you will need to rethink things like branding. At this point it helps if you are doing the print styles early in the project so that you can control the logo not being a background image for example. Where colour is important to the meaning of the document, for example a status on an invoice, bear in mind that a textural representation will also need to be supplied as the user may be printing in black and white. Borders will print however regardless of setting, so assuming the user is printing in colour you can always use borders to indicate colour. Check the colour contrast of the text against white, this may need to be altered without backgrounds. You should check how your page looks with backgrounds turned on too, for consistency with the default browser settings you may want to override your background anyway. One final issue with backgrounds being off is list items. It is relatively common practice to suppress the list-style-type and replace with a background image to finely control the bullet positioning. This technique doesn’t translate to print, you will need to disable this background bullet and re-instate your trusty friend the list-style-type. 3. Using JavaScript in your CSS? … beware IE6 Internet explorer has an issue that when Javascript is used in a stylesheet it applies this to all media types even if only applied to screen. For example, if you happen to be using expressions to set a width for IE, perhaps to mimic min-width, a simple width:100% !important rule can overcome the effects the expression has on your print styles10. 4. De-enhance your Progressive enhancements Quite a classic “doh” moment is when you realise that, of course paper doesn’t support Javascript. If you have any dynamic elements on the page, for example a document collapsed per section, you really should have been using Progressive enhancement techniques11 and building for browsers without Javascript first, adding in the fancy stuff later. If this is the case it should be trivial to override your wizzy JS styles in your print stylesheet, to display all your content and make it accessible for print, which is by far the best method of achieving this affect. And Finally… I refer you back to the nature of the document in hand, consider the context of your site and the page. Use the tips here to help you add that extra bit of flair to your printed media. Be careful you don’t get caught out by the common gotchas, keep the design simple, test cross browser and relish in the medium of print. Further Reading 1 For more information constantly updated, please see the CSS discuss wiki on print stylesheets 2 For more information on media types and ways of including CSS please refer to the CSS discuss wiki on Media Stylesheets 3 Eric Meyer talks to ThinkVitamin about the importance of context when designing your print strategy. 4 Mark Boulton describes how he applies a newspaper like print stylesheet to an article in the Guardian website. Mark also has some persuasive arguments that print should not be left to last 5 Richard Rutter Has a fantastic resource on typography which also applies to print. 6 Aaron Gustafson has a great solution to link problem by creating footnotes at the end of the page. 7 The CSS discuss wiki has more detailed information on printing tables and detailed browser support 8 This ‘A List Apart’ article is dated May 10th 2002 though is still mostly relevant 9 Float clearing technique using ‘overflow:hidden’ 10 Autistic Cuckoo describes the interesting insight with regards to expressions specified for screen in IE6 remaining in print 11 Wikipedia has a good article on the definition of progressive enhancement 12 For a really neat trick involving a dynamically generated column to displaying <abbr> and <dfn> meanings (as well as somewhere for the user to write notes), try print previewing on Brian Suda’s site 2007 Natalie Downe nataliedowne 2007-12-09T00:00:00+00:00 https://24ways.org/2007/back-to-the-future-of-print/ design
158 10 Ways To Get Design Approval One of the most challenging parts of the web design process is getting design sign off. It can prove time consuming, demoralizing and if you are not careful can lead to a dissatisfied client. What is more you can end up with a design that you are ashamed to include in your portfolio. How then can you ensure that the design you produce is the one that gets built? How can you get the client to sign off on your design? Below are 10 tips learnt from years of bitter experience. 1. Define the role of the client and designer Many of the clients you work with will not have been involved in a web project before. Even if they have they may have worked in a very different way to what you would expect. Take the time at the beginning of the project to explain their role in the design of the site. The best approach is to emphasis that their job is to focus on the needs of their users and business. They should concentrate on the broad issues, while you worry about the details of layout, typography and colour scheme. By clarifying what you expect from the client, you help them to provide the right kind of input throughout the process. 2. Understand the business Before you open up Photoshop or put pen to paper, take the time to make sure you properly understand not only the brief but the organization behind the site. By understanding their business objectives, organizational structure and marketing strategy your design decisions will be better informed. You cannot rely upon the brief to provide all of the information you need. It is important to dig deeper and get as good an understanding of their business as possible. This information will prove invaluable when justifying your design decisions. 3. Understand the users We all like to think of ourselves as user centric designers, but exactly how much effort do you put into knowing your users before beginning the design process? Take the time to really understand them the best you can. Try to meet with some real prospective users and get to know their needs. Failing that work with the client to produce user personas to help picture exactly what kind of people they are. Understanding your users not only improves the quality of your work, but also helps move the discussion away from the personal preferences of the client, to the people who’s opinion really matters. 4. Avoid multiple concepts Many clients like the idea of having the option to choose between multiple design concepts. However, although on the surface this might appear to be a good idea it can ultimately be counterproductive for design sign off. In a world of limited budgets it is unwise to waste money on producing designs that are ultimately going to be thrown away. The resources would be better spent refining a single design through multiple iterations. What is more, multiple concepts often cause confusion rather than clarity. It is common for a client to request one element from one design and another from the second. As any designer knows this seldom works. 5. Use mood boards Clients are often better at expressing what they don’t like than what they do. This is one of the reasons why they favour producing multiple design concepts. An alternative less costly approach is to create a series of mood boards. These boards contain a collection of colours, typography and imagery which represent different “moods” or directions, which the design could take. Mood boards are quick and easy to produce allowing you to try out various design approaches with the client without investing the time needed to produce complete design concepts. This means that by the time you develop a concept the client and designer have already established an understanding about the direction of the design. 6. Say what you like It is not uncommon for a client to ask for a design that looks similar to another site they like. The problem is that it can often be hard to establish exactly what it is about the site that attracts them. Also in many cases the sites they like are not something you are keen to emulate! A better approach that was suggested to me by Andy Budd is to show them sites that you think the design should emulate. Keep a collection of screen captures from well designed sites and pick out a few that are relevant to that particular client. Explain why you feel these designs might suit their project and ask for their feedback. If they don’t like your choices then expose them to more of your collection and see what they pick out. 7. Wireframe the homepage Often clients find it hard to distinguish between design and content and so sometimes reject a design on the basis that the content is not right. This is particularly true when signing off the homepage. You may therefore find it useful to establish the homepage content before producing the design. That way once they see the design they will not be distracted by the content. One of the best ways to do this is by producing a basic wireframe consisting of a series of content boxes. Once this has been approved you will find the sign off of design much easier. 8. Present your designs Although it is true that a good design should speak for itself it still needs presenting to the client. The client needs to understand why you have made the design decisions you have, otherwise they will judge the design purely on personal preference. Talk them through the design explaining how it meets the needs of their users and business objectives. Refer to the mood boards and preferred sites the client approved and explain how the design is a continuation of those. Never simply email the design through and hope the client interprets your work correctly! 9. Provide written supporting material Unfortunately, no matter how well you justify the design to the client he is almost certain to want to show it to others. He may need his bosses approval or require internal buy in. At the very least he is going to want to get a second opinion from a friend or colleague. The problem with this is that you are not going to be there to present to these people in the same way you did for the client. You cannot expect the client to present your ideas as well as you did. The reality is that you have lost control of how the design is perceived. One way to minimize this problem is to provide written documentation supporting the design. This can be a summary of the presentation you gave to the client and allows him to distribute this along with the design. By putting a written explanation with the design you ensure that everybody who sees it gets the same message. 10. Control the feedback My final piece of advice for managing design sign off is to control the way you receive feedback. A clients natural inclination will be to give you his personal opinion on the design. This is reinforced because you ask them what they think of the design. Instead ask them what their users will think of the design. Encourage them to think from the users perspective. Also encourage them to keep that overarching focus I talked about in my first tip. Their tendency will be to try to improve the design, however that should be your problem not theirs. The role of a client should be to defend the needs of their users and business not do the design. Encourage the client to make comments such as “I am not sure that my female users will like the masculine colours” rather than “can we make the whole design pink.” It is down to them to identify the problems and for you as the designer to find the most appropriate solution. So there you have it. My 10 tips to improve design sign off. Will this ensure design approval every time? Unfortunately not. However it should certainly help smooth the way. 2007 Paul Boag paulboag 2007-12-10T00:00:00+00:00 https://24ways.org/2007/10-ways-to-get-design-approval/ business
146 Increase Your Font Stacks With Font Matrix Web pages built in plain old HTML and CSS are displayed using only the fonts installed on users’ computers (@font-face implementations excepted). To enable this, CSS provides the font-family property for specifying fonts in order of preference (often known as a font stack). For example: h1 {font-family: 'Egyptienne F', Cambria, Georgia, serif} So in the above rule, headings will be displayed in Egyptienne F. If Egyptienne F is not available then Cambria will be used, failing that Georgia or the final fallback default serif font. This everyday bit of CSS will be common knowledge among all 24 ways readers. It is also a commonly held belief that the only fonts we can rely on being installed on users’ computers are the core web fonts of Arial, Times New Roman, Verdana, Georgia and friends. But is that really true? If you look in the fonts folder of your computer, or even your Mum’s computer, then you are likely to find a whole load of fonts besides the core ones. This is because many software packages automatically install extra typefaces. For example, Office 2003 installs over 100 additional fonts. Admittedly not all of these fonts are particularly refined, and not all are suitable for the Web. However they still do increase your options. The Matrix I have put together a matrix of (western) fonts showing which are installed with Mac and Windows operating systems, which are installed with various versions of Microsoft Office, and which are installed with Adobe Creative Suite. The matrix is available for download as an Excel file and as a CSV. There are no readily available statistics regarding the penetration of Office or Creative Suite, but you can probably take an educated guess based on your knowledge of your readers. The idea of the matrix is that use can use it to help construct your font stack. First of all pick the font you’d really like for your text – this doesn’t have to be in the matrix. Then pick the generic family (serif, sans-serif, cursive, fantasy or monospace) and a font from each of the operating systems. Then pick any suitable fonts from the Office and Creative Suite lists. For example, you may decide your headings should be in the increasingly ubiquitous Clarendon. This is a serif type face. At OS-level the most similar is arguably Georgia. Adobe CS2 comes with Century Old Style which has a similar feel. Century Schoolbook is similar too, and is installed with all versions of Office. Based on this your font stack becomes: font-family: 'Clarendon Std', 'Century Old Style Std', 'Century Schoolbook', Georgia, serif Note the ‘Std’ suffix indicating a ‘standard’ OpenType file, which will normally be your best bet for more esoteric fonts. I’m not suggesting the process of choosing suitable fonts is an easy one. Firstly there are nearly two hundred fonts in the matrix, so learning what each font looks like is tricky and potentially time consuming (if you haven’t got all the fonts installed on a machine to hand you’ll be doing a lot of Googling for previews). And it’s not just as simple as choosing fonts that look similar or have related typographic backgrounds, they need to have similar metrics as well, This is especially true in terms of x-height which gives an indication of how big or small a font looks. Over to You The main point of all this is that there are potentially more fonts to consider than is generally accepted, so branch out a little (carefully and tastefully) and bring a little variety to sites out there. If you come up with any novel font stacks based on this approach, please do blog them (tagged as per the footer) and at some point they could all be combined in one place for everyone to consider. Appendix What about Linux? The only operating systems in the matrix are those from Microsoft and Apple. For completeness, Linux operating systems should be included too, although these are many and varied and very much in a minority, so I omitted them for time being. For the record, some Linux distributions come packaged with Microsoft’s core fonts. Others use the Vera family, and others use the Liberation family which comprises fonts metrically identical to Times New Roman and Arial. Sources The sources of font information for the matrix are as follows: Windows XP SP2 Windows Vista Office 2003 Office 2007 Mac OSX Tiger Mac OSX Leopard (scroll down two thirds) Office 2004 (Mac) by inspecting my Microsoft Office 2004/Office/Fonts folder Office 2008 (Mac) is expected to be as Office 2004 with the addition of the Vista ClearType fonts Creative Suite 2 (see pdf link in first comment) Creative Suite 3 2007 Richard Rutter richardrutter 2007-12-17T00:00:00+00:00 https://24ways.org/2007/increase-your-font-stacks-with-font-matrix/ design
168 Unobtrusively Mapping Microformats with jQuery Microformats are everywhere. You can’t shake an electronic stick these days without accidentally poking a microformat-enabled site, and many developers use microformats as a matter of course. And why not? After all, why invent your own class names when you can re-use pre-defined ones that give your site extra functionality for free? Nevertheless, while it’s good to know that users of tools such as Tails and Operator will derive added value from your shiny semantics, it’s nice to be able to reuse that effort in your own code. We’re going to build a map of some of my favourite restaurants in Brighton. Fitting with the principles of unobtrusive JavaScript, we’ll start with a semantically marked up list of restaurants, then use JavaScript to add the map, look up the restaurant locations and plot them as markers. We’ll be using a couple of powerful tools. The first is jQuery, a JavaScript library that is ideally suited for unobtrusive scripting. jQuery allows us to manipulate elements on the page based on their CSS selector, which makes it easy to extract information from microformats. The second is Mapstraction, introduced here by Andrew Turner a few days ago. We’ll be using Google Maps in the background, but Mapstraction makes it easy to change to a different provider if we want to later. Getting Started We’ll start off with a simple collection of microformatted restaurant details, representing my seven favourite restaurants in Brighton. The full, unstyled list can be seen in restaurants-plain.html. Each restaurant listing looks like this: <li class="vcard"> <h3><a class="fn org url" href="http://www.riddleandfinns.co.uk/">Riddle & Finns</a></h3> <div class="adr"> <p class="street-address">12b Meeting House Lane</p> <p><span class="locality">Brighton</span>, <abbr class="country-name" title="United Kingdom">UK</abbr></p> <p class="postal-code">BN1 1HB</p> </div> <p>Telephone: <span class="tel">+44 (0)1273 323 008</span></p> <p>E-mail: <a href="mailto:info@riddleandfinns.co.uk" class="email">info@riddleandfinns.co.uk</a></p> </li> Since we’re dealing with a list of restaurants, each hCard is marked up inside a list item. Each restaurant is an organisation; we signify this by placing the classes fn and org on the element surrounding the restaurant’s name (according to the hCard spec, setting both fn and org to the same value signifies that the hCard represents an organisation rather than a person). The address information itself is contained within a div of class adr. Note that the HTML <address> element is not suitable here for two reasons: firstly, it is intended to mark up contact details for the current document rather than generic addresses; secondly, address is an inline element and as such cannot contain the paragraphs elements used here for the address information. A nice thing about microformats is that they provide us with automatic hooks for our styling. For the moment we’ll just tidy up the whitespace a bit; for more advanced style tips consult John Allsop’s guide from 24 ways 2006. .vcard p { margin: 0; } .adr { margin-bottom: 0.5em; } To plot the restaurants on a map we’ll need latitude and longitude for each one. We can find this out from their address using geocoding. Most mapping APIs include support for geocoding, which means we can pass the API an address and get back a latitude/longitude point. Mapstraction provides an abstraction layer around these APIs which can be included using the following script tag: <script type="text/javascript" src="http://mapstraction.com/src/mapstraction-geocode.js"></script> While we’re at it, let’s pull in the other external scripts we’ll be using: <script type="text/javascript" src="jquery-1.2.1.js"></script> <script src="http://maps.google.com/maps?file=api&v=2&key=YOUR_KEY" type="text/javascript"></script> <script type="text/javascript" src="http://mapstraction.com/src/mapstraction.js"></script> <script type="text/javascript" src="http://mapstraction.com/src/mapstraction-geocode.js"></script> That’s everything set up: let’s write some JavaScript! In jQuery, almost every operation starts with a call to the jQuery function. The function simulates method overloading to behave in different ways depending on the arguments passed to it. When writing unobtrusive JavaScript it’s important to set up code to execute when the page has loaded to the point that the DOM is available to be manipulated. To do this with jQuery, pass a callback function to the jQuery function itself: jQuery(function() { // This code will be executed when the DOM is ready }); Initialising the map The first thing we need to do is initialise our map. Mapstraction needs a div with an explicit width, height and ID to show it where to put the map. Our document doesn’t currently include this markup, but we can insert it with a single line of jQuery code: jQuery(function() { // First create a div to host the map var themap = jQuery('<div id="themap"></div>').css({ 'width': '90%', 'height': '400px' }).insertBefore('ul.restaurants'); }); While this is technically just a single line of JavaScript (with line-breaks added for readability) it’s actually doing quite a lot of work. Let’s break it down in to steps: var themap = jQuery('<div id="themap"></div>') Here’s jQuery’s method overloading in action: if you pass it a string that starts with a < it assumes that you wish to create a new HTML element. This provides us with a handy shortcut for the more verbose DOM equivalent: var themap = document.createElement('div'); themap.id = 'themap'; Next we want to apply some CSS rules to the element. jQuery supports chaining, which means we can continue to call methods on the object returned by jQuery or any of its methods: var themap = jQuery('<div id="themap"></div>').css({ 'width': '90%', 'height': '400px' }) Finally, we need to insert our new HTML element in to the page. jQuery provides a number of methods for element insertion, but in this case we want to position it directly before the <ul> we are using to contain our restaurants. jQuery’s insertBefore() method takes a CSS selector indicating an element already on the page and places the current jQuery selection directly before that element in the DOM. var themap = jQuery('<div id="themap"></div>').css({ 'width': '90%', 'height': '400px' }).insertBefore('ul.restaurants'); Finally, we need to initialise the map itself using Mapstraction. The Mapstraction constructor takes two arguments: the first is the ID of the element used to position the map; the second is the mapping provider to use (in this case google ): // Initialise the map var mapstraction = new Mapstraction('themap','google'); We want the map to appear centred on Brighton, so we’ll need to know the correct co-ordinates. We can use www.getlatlon.com to find both the co-ordinates and the initial map zoom level. // Show map centred on Brighton mapstraction.setCenterAndZoom( new LatLonPoint(50.82423734980143, -0.14007568359375), 15 // Zoom level appropriate for Brighton city centre ); We also want controls on the map to allow the user to zoom in and out and toggle between map and satellite view. mapstraction.addControls({ zoom: 'large', map_type: true }); Adding the markers It’s finally time to parse some microformats. Since we’re using hCard, the information we want is wrapped in elements with the class vcard. We can use jQuery’s CSS selector support to find them: var vcards = jQuery('.vcard'); Now that we’ve found them, we need to create a marker for each one in turn. Rather than using a regular JavaScript for loop, we can instead use jQuery’s each() method to execute a function against each of the hCards. jQuery('.vcard').each(function() { // Do something with the hCard }); Within the callback function, this is set to the current DOM element (in our case, the list item). If we want to call the magic jQuery methods on it we’ll need to wrap it in another call to jQuery: jQuery('.vcard').each(function() { var hcard = jQuery(this); }); The Google maps geocoder seems to work best if you pass it the street address and a postcode. We can extract these using CSS selectors: this time, we’ll use jQuery’s find() method which searches within the current jQuery selection: var streetaddress = hcard.find('.street-address').text(); var postcode = hcard.find('.postal-code').text(); The text() method extracts the text contents of the selected node, minus any HTML markup. We’ve got the address; now we need to geocode it. Mapstraction’s geocoding API requires us to first construct a MapstractionGeocoder, then use the geocode() method to pass it an address. Here’s the code outline: var geocoder = new MapstractionGeocoder(onComplete, 'google'); geocoder.geocode({'address': 'the address goes here'); The onComplete function is executed when the geocoding operation has been completed, and will be passed an object with the resulting point on the map. We just want to create a marker for the point: var geocoder = new MapstractionGeocoder(function(result) { var marker = new Marker(result.point); mapstraction.addMarker(marker); }, 'google'); For our purposes, joining the street address and postcode with a comma to create the address should suffice: geocoder.geocode({'address': streetaddress + ', ' + postcode}); There’s one last step: when the marker is clicked, we want to display details of the restaurant. We can do this with an info bubble, which can be configured by passing in a string of HTML. We’ll construct that HTML using jQuery’s html() method on our hcard object, which extracts the HTML contained within that DOM node as a string. var marker = new Marker(result.point); marker.setInfoBubble( '<div class="bubble">' + hcard.html() + '</div>' ); mapstraction.addMarker(marker); We’ve wrapped the bubble in a div with class bubble to make it easier to style. Google Maps can behave strangely if you don’t provide an explicit width for your info bubbles, so we’ll add that to our CSS now: .bubble { width: 300px; } That’s everything we need: let’s combine our code together: jQuery(function() { // First create a div to host the map var themap = jQuery('<div id="themap"></div>').css({ 'width': '90%', 'height': '400px' }).insertBefore('ul.restaurants'); // Now initialise the map var mapstraction = new Mapstraction('themap','google'); mapstraction.addControls({ zoom: 'large', map_type: true }); // Show map centred on Brighton mapstraction.setCenterAndZoom( new LatLonPoint(50.82423734980143, -0.14007568359375), 15 // Zoom level appropriate for Brighton city centre ); // Geocode each hcard and add a marker jQuery('.vcard').each(function() { var hcard = jQuery(this); var streetaddress = hcard.find('.street-address').text(); var postcode = hcard.find('.postal-code').text(); var geocoder = new MapstractionGeocoder(function(result) { var marker = new Marker(result.point); marker.setInfoBubble( '<div class="bubble">' + hcard.html() + '</div>' ); mapstraction.addMarker(marker); }, 'google'); geocoder.geocode({'address': streetaddress + ', ' + postcode}); }); }); Here’s the finished code. There’s one last shortcut we can add: jQuery provides the $ symbol as an alias for jQuery. We could just go through our code and replace every call to jQuery() with a call to $(), but this would cause incompatibilities if we ever attempted to use our script on a page that also includes the Prototype library. A more robust approach is to start our code with the following: jQuery(function($) { // Within this function, $ now refers to jQuery // ... }); jQuery cleverly passes itself as the first argument to any function registered to the DOM ready event, which means we can assign a local $ variable shortcut without affecting the $ symbol in the global scope. This makes it easy to use jQuery with other libraries. Limitations of Geocoding You may have noticed a discrepancy creep in to the last example: whereas my original list included seven restaurants, the geocoding example only shows five. This is because the Google Maps geocoder incorporates a rate limit: more than five lookups in a second and it starts returning error messages instead of regular results. In addition to this problem, geocoding itself is an inexact science: while UK postcodes generally get you down to the correct street, figuring out the exact point on the street from the provided address usually isn’t too accurate (although Google do a pretty good job). Finally, there’s the performance overhead. We’re making five geocoding requests to Google for every page served, even though the restaurants themselves aren’t likely to change location any time soon. Surely there’s a better way of doing this? Microformats to the rescue (again)! The geo microformat suggests simple classes for including latitude and longitude information in a page. We can add specific points for each restaurant using the following markup: <li class="vcard"> <h3 class="fn org">E-Kagen</h3> <div class="adr"> <p class="street-address">22-23 Sydney Street</p> <p><span class="locality">Brighton</span>, <abbr class="country-name" title="United Kingdom">UK</abbr></p> <p class="postal-code">BN1 4EN</p> </div> <p>Telephone: <span class="tel">+44 (0)1273 687 068</span></p> <p class="geo">Lat/Lon: <span class="latitude">50.827917</span>, <span class="longitude">-0.137764</span> </p> </li> As before, I used www.getlatlon.com to find the exact locations – I find satellite view is particularly useful for locating individual buildings. Latitudes and longitudes are great for machines but not so useful for human beings. We could hide them entirely with display: none, but I prefer to merely de-emphasise them (someone might want them for their GPS unit): .vcard .geo { margin-top: 0.5em; font-size: 0.85em; color: #ccc; } It’s probably a good idea to hide them completely when they’re displayed inside an info bubble: .bubble .geo { display: none; } We can extract the co-ordinates in the same way we extracted the address. Since we’re no longer geocoding anything our code becomes a lot simpler: $('.vcard').each(function() { var hcard = $(this); var latitude = hcard.find('.geo .latitude').text(); var longitude = hcard.find('.geo .longitude').text(); var marker = new Marker(new LatLonPoint(latitude, longitude)); marker.setInfoBubble( '<div class="bubble">' + hcard.html() + '</div>' ); mapstraction.addMarker(marker); }); And here’s the finished geo example. Further reading We’ve only scratched the surface of what’s possible with microformats, jQuery (or just regular JavaScript) and a bit of imagination. If this example has piqued your interest, the following links should give you some more food for thought. The hCard specification Notes on parsing hCards jQuery for JavaScript programmers – my extended tutorial on jQuery. Dann Webb’s Sumo – a full JavaScript library for parsing microformats, based around some clever metaprogramming techniques. Jeremy Keith’s Adactio Austin – the first place I saw using microformats to unobtrusively plot locations on a map. Makes clever use of hEvent as well. 2007 Simon Willison simonwillison 2007-12-12T00:00:00+00:00 https://24ways.org/2007/unobtrusively-mapping-microformats-with-jquery/ code
157 Capturing Caps Lock One of the more annoying aspects of having to remember passwords (along with having to remember loads of them) is that if you’ve got Caps Lock turned on accidentally when you type one in, it won’t work, and you won’t know why. Most desktop computers alert you in some way if you’re trying to enter your password to log on and you’ve enabled Caps Lock; there’s no reason why the web can’t do the same. What we want is a warning – maybe the user wants Caps Lock on, because maybe their password is in capitals – rather than something that interrupts what they’re doing. Something subtle. But that doesn’t answer the question of how to do it. Sadly, there’s no way of actually detecting whether Caps Lock is on directly. However, there’s a simple work-around; if the user presses a key, and it’s a capital letter, and they don’t have the Shift key depressed, why then they must have Caps Lock on! Simple. DOM scripting allows your code to be notified when a key is pressed in an element; when the key is pressed, you get the ASCII code for that key. Capital letters, A to Z, have ASCII codes 65 to 90. So, the code would look something like: on a key press if the ASCII code for the key is between 65 and 90 *and* if shift is pressed warn the user that they have Caps Lock on, but let them carry on end if end keypress The actual JavaScript for this is more complicated, because both event handling and keypress information differ across browsers. Your event handling functions are passed an event object, except in Internet Explorer where you use the global event object; the event object has a which parameter containing the ASCII code for the key pressed, except in Internet Explorer where the event object has a keyCode parameter; some browsers store whether the shift key is pressed in a shiftKey parameter and some in a modifiers parameter. All this boils down to code that looks something like this: keypress: function(e) { var ev = e ? e : window.event; if (!ev) { return; } var targ = ev.target ? ev.target : ev.srcElement; // get key pressed var which = -1; if (ev.which) { which = ev.which; } else if (ev.keyCode) { which = ev.keyCode; } // get shift status var shift_status = false; if (ev.shiftKey) { shift_status = ev.shiftKey; } else if (ev.modifiers) { shift_status = !!(ev.modifiers & 4); } // At this point, you have the ASCII code in “which”, // and shift_status is true if the shift key is pressed } Then it’s just a check to see if the ASCII code is between 65 and 90 and the shift key is pressed. (You also need to do the same work if the ASCII code is between 97 (a) and 122 (z) and the shift key is not pressed, because shifted letters are lower-case if Caps Lock is on.) if (((which >= 65 && which <= 90) && !shift_status) || ((which >= 97 && which <= 122) && shift_status)) { // uppercase, no shift key /* SHOW THE WARNING HERE */ } else { /* HIDE THE WARNING HERE */ } The warning can be implemented in many different ways: highlight the password field that the user is typing into, show a tooltip, display text next to the field. For simplicity, this code shows the warning as a previously created image, with appropriate alt text. Showing the warning means creating a new <img> tag with DOM scripting, dropping it into the page, and positioning it so that it’s next to the appropriate field. The image looks like this: You know the position of the field the user is typing into (from its offsetTop and offsetLeft properties) and how wide it is (from its offsetWidth properties), so use createElement to make the new img element, and then absolutely position it with style properties so that it appears in the appropriate place (near to the text field). The image is a transparent PNG with an alpha channel, so that the drop shadow appears nicely over whatever else is on the page. Because Internet Explorer version 6 and below doesn’t handle transparent PNGs correctly, you need to use the AlphaImageLoader technique to make the image appear correctly. newimage = document.createElement('img'); newimage.src = "http://farm3.static.flickr.com/2145/2067574980_3ddd405905_o_d.png"; newimage.style.position = "absolute"; newimage.style.top = (targ.offsetTop - 73) + "px"; newimage.style.left = (targ.offsetLeft + targ.offsetWidth - 5) + "px"; newimage.style.zIndex = "999"; newimage.setAttribute("alt", "Warning: Caps Lock is on"); if (newimage.runtimeStyle) { // PNG transparency for IE newimage.runtimeStyle.filter += "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://farm3.static.flickr.com/2145/2067574980_3ddd405905_o_d.png',sizingMethod='scale')"; } document.body.appendChild(newimage); Note that the alt text on the image is also correctly set. Next, all these parts need to be pulled together. On page load, identify all the password fields on the page, and attach a keypress handler to each. (This only needs to be done for password fields because the user can see if Caps Lock is on in ordinary text fields.) var inps = document.getElementsByTagName("input"); for (var i=0, l=inps.length; i The “create an image” code from above should only be run if the image is not already showing, so instead of creating a newimage object, create the image and attach it to the password field so that it can be checked for later (and not shown if it’s already showing). For safety, all the code should be wrapped up in its own object, so that its functions don’t collide with anyone else’s functions. So, create a single object called capslock and make all the functions be named methods of the object: var capslock = { ... keypress: function(e) { } ... } Also, the “create an image” code is saved into its own named function, show_warning(), and the converse “remove the image” code into hide_warning(). This has the advantage that developers can include the JavaScript library that has been written here, but override what actually happens with their own code, using something like: <script src="jscapslock.js" type="text/javascript"></script> <script type="text/javascript"> capslock.show_warning(target) { // do something different here to warn the user } capslock.hide_warning(target) { // hide the warning that we created in show_warning() above } </script> And that’s all. Simply include the JavaScript library in your pages, override what happens on a warning if that’s more appropriate for what you’re doing, and that’s all you need. See the script in action. 2007 Stuart Langridge stuartlangridge 2007-12-04T00:00:00+00:00 https://24ways.org/2007/capturing-caps-lock/ code