Saturday, April 26, 2008

Retaining Middle-Click Functionality With JavaScript

posted by Jonah Dempcy
Image used under a Creative Commons license. Source

Browsers such as Firefox and Internet Explorer 7 allow users to middle-click on links to open them in a new tab. Oftentimes, JavaScript will be used to add an onclick event to a link that supersedes the link itself. But, when a Firefox or IE7 user middle-clicks the link they may be confronted with any number of sub-par user experiences, such as the new tab containing the same page they were on, or a blank page.

In order to prevent this from happening, a non-JavaScript fallback must be present. Some people only think about the small (estimated less than 5%) amount of users who have JavaScript disabled, and choose not to support those users. That's a conscious decision and I certainly understand it, and condone it as long as there are clearly defined messages to the JS-disabled user that they need to enable JavaScript to use the page.

However, what some people fail to recognize is that even the 95% of users with JavaScript enabled will have sub-par user experiences if there are not non-JS fallbacks cases like this, where middle-clicking a link does nothing but waste the user's time. In order to offer the best user experience and fully utilize the capabilities of the browser, a conscientious JavaScript developer must take a number of measures that might not be apparent at first glance.

When Should Middle-Click Functionality Be Retained?

There are many uses for JavaScript onclick events. Not all of them will need to support the middle click. To determine if it does, ask yourself if it appears as a link to the user. If it looks like a link (be it text, image or button that appears to lead to another page) then it should support middle-click behavior. If it is another use for an onclick event, for instance, a button that clearly performs an action on the current page, then no alternative is necessary.

Take the case of a fancy search results widget that heavily uses JavaScript and Ajax to present search results. Users with JavaScript disabled are presented with instructions on how to enable it, and informed that they won't be able to use the site with out it. So far so good, but unless the search results have a non-JavaScript fallback (unlikely) then middle-clicking them will not have the expected result of some users, which is to take the user to a product detail page.

A real life example: I was interested in tickets to a jazz show and visited the tickets page for that festival. It has a link saying "Watch a video of this artist." I middle-clicked the link, as I usually do, but the next tab contained the exact same information I had just seen-- no video in sight. I inspected the HTML to see that it was a link, but the href was set to "#", in other words, an empty name on the same page.

When I left-clicked the anchor tag, it executed a JavaScript function and opened a popup showing the video. But, middle-clicking proved pointless, as it was a "dummy" anchor tag that should have been a span with cursor: pointer in the CSS.

In fact, I quite commonly see JavaScript onclick events on anchor tags, where the anchor tag serves no other purpose than to make the mouse cursor a pointer. Take the following code, for example:

<a href="#" onclick="showMoreResults();"gt;Show more results</a

If you left-click the link, it will take you to the top of the page (implicit in the "#" name) and execute the showMoreResults() function, which does an Ajax call to fetch more search results, let's say. So far so good. But if you middle-click the link, it will take you to the same page without executing the function. Furthermore, if it's a complex JavaScript application, the state of the application may be lost. In the case of search results, the newly-opened tab may have reset back to the original state of the application, frustrating the user. Of course they can just close the tab and use their existing tab, so all is not lost, but it is still a waste of time from the user's perspective.

If it is truly a JavaScript feature which does not appear as a link, the wise developer would remove the user's ability to middle-click it. By changing it to a span tag, middle-clicking would do nothing.

On the other hand, if it actually looks like a link (and in this case, I think it does), then a middle-click fallback would be nice. Say the user wants to open the next page of search results in the new tab, but stay on the existing page in this tab. Middle-clicking should work, right?

How to Retain Middle-Click Functionality

The way to do this is to set the href attribute of your anchor tags to a page presenting the user with the same content they would get by left-clicking. So, if your app uses Ajax to load in new results by left-clicking, then middle-clicking should take them to the same results on a new page. This is accomplished by setting the href attribute with the correct URL to the content. If you're on search-results.php?page=1 then you would need to set the href to be search-results.php?page=2. The solution varies from app to app but the general idea is to make sure that the href of the link leads to the content advertised in the link.

Next, you need to make sure the onclick event returns false. If you're using inline event registration, it looks like this:

<a href="search-results.php?page=2" onclick="showMoreResults(); return false;">Show more results</a>

This is the same as a non-JavaScript fallback for links. It's a good idea to do this anyway, regardless of middle-click support, as approximately 1-in-20 users will have JS disabled anyway (although this number may vary depending on your particular target market).

Something else to keep in mind is that if you are outputting the href from the server and then showing new results with JavaScript, you'll need to update the href tag as well. Otherwise, you could go to page 1, click "Show more results" and view page 2's results, and then middle-click "Show more results" and be presented with page 2's results again. Why? Because the href was set on page 1 to show page 2's results, and the JavaScript loaded in new results using Ajax. The solution to this is to make sure you update the href to display the correct content each time the application state changes, be it with Ajax or otherwise.

It's fairly straightforward-- if you're on page 10, then when the showMoreResults() function is called you load in new results for page 11 with Ajax, and set the href of the "Show more results" link to point to page 12.

This is just one example using search results, but the same principle applies to any JavaScript actions that the user perceives as links. When middle-clicking the link, or clicking it with JavaScript disabled, the user should be presented with the right content. Otherwise, they will feel like the site is broken, or like they did something wrong. Instead, make sure you've always placed non-JavaScript and middle-click fallbacks in your pages.

Whenever adding an onclick, ask yourself if it looks like a link or a button that would take the user to a new page. Unless the onclick is operating directly on the contents of the current page and it's obvious to the user, consider using the methods outlined above.

Labels: ,

Friday, April 25, 2008

IE Plugin to Detect JavaScript Memory Leaks

posted by Jonah Dempcy

Microsoft has released a free beta version of a plugin for Internet Explorer that looks for memory leaks. The plugin, plainly titled JavaScript Memory Leak Detector (download), is available immediately and tests for both IE6 and IE7 leaks. Apparently, IE6 has far more leaks -- any time when you have a circular reference between DOM objects and JavaScript objects a leak would occur.

Why are leaks a problem? Because the RAM leaked by a page will remain in use until the browser is closed. For developers of websites with heavy JavaScript usage, memory leaks can be a nightmare. I once worked on a site that would climb to hundreds of megabytes after a few pages of data loaded in through Ajax. Then, the RAM would continue to stay in use until I closed and restarted the browser.

Excerpt from the article "JavaScript Memory Leak Detector"

The IE team has been working hard to solve the problem. With the initial versions of Internet Explorer 6 practically all circular references between Javascript and DOM objects caused a memory leak.

It seems than an update to IE6 alleviated this problem somewhat, but there are probably still many users who haven't upgraded, and even the later versions of IE6 and current versions of IE7 still have problems. Most of the problems are caused by closures, which really shouldn't be an issue, as Firefox, Opera and Safari generally handle them fine. I use closures all the time in my code so I shudder to think of what leaks lie beneath. But, I'm about to download this leak detector and give it a try. From the screenshots, it doesn't look too appealing, but I'm curious to find what it will detect on the past few sites I've made.

Thursday, April 24, 2008

Using Blogger for Content Management

posted by Jonah Dempcy

Despite the name, Blogger isn't just limited to blogs. It actually makesfor a nifty Content Management System (CMS) for a wide range of sites, including e-commerce, informational and educational sites.

I've found it to be an easy and inexpensive (well, free) solution for simple content management, albeit a comparatively inflexible one. You won't get the rich plugin library of blogging platforms like WordPress or CMS's like Joomla, Pligg or Drupal. But you will get the benefit of having a very fast setup time. I estimate once you get good at it, it only takes 15 minutes to be up and running with Blogger (not counting time developing the template of course).

If you already have an existing site layout, adding in Blogger functionality is a snap. Blogger uses special markup that is compartmentalized and easily pasted into an existing HTML layout.

Also, when deploying to your own web server, Blogger plays nice with with PHP. I'm not sure about other web languages such as ASP or Perl/Mason, but I imagine as long as it's an interpreted language, it should be fine.

Blogger is completely transparent to use. In other words, your users will have no idea you're using Blogger. One caveat: If you enable comments on your posts then it will be revealed when a user clicks to add a comment.

Setting up a Blog

You have three options for hosting the blog. You can get free hosting and a sub-domain at blogspot.com, for instance, thetruetribe.blogspot.com. Or, you can register your own domain name with Blogger for a small fee (e.g. thetruetribe.com). Or, if you already have hosting and a domain name with another provider, you can publish to that domain from Blogger.

By far, most people have a sub-domain at blogspot.com. But, except for personal blogs, this isn't really appropriate most of the time. Although I do see e-commerce sites have blogs at blogspot.com addresses, this is somewhat of a faux pas in my opinion.

One reason not to have your blog on a separate domain is that it divides resources. If you have both yoursite.com and yoursite.blogspot.com, the SEO will be twice as difficult as keyword relevance and backlinks will be split between the two web properties. From an SEO standpoint (and probably from a user's standpoint as well), it's ideal to have the blog or news page be on the same domain as the main site. You could also publish the blog to a sub-domain of the site, for instance blog.yourdomain.com, without ill effect.

My recommendation is to go for one of the other two options unless it's a personal site, as you will want to have a custom domain in order to rank well for SEO and also for users to be able to easily recall the domain and access it.

The second option, registering the domain through Blogger and having them host it, has the benefit that you get free hosting and can use their powerful XML templates. The XML templates were released in 2007 and seem to have been complete rewrite of their templating engine. You don't get this functionality when publishing to your own FTP site, so this is the best option if you want to fully utilize what Blogger has to offer. Blogger's XML templates allow easy drag-and-drop rearranging of widgets on the page, as well as a variety of built in widgets including RSS feed aggregators, image galleries and so on.

The third option, publishing to your own FTP site, is appropriate if you already have a domain name and hosting or if you wish to utilize the flexibility having your own hosting can give you. But, take note that you will be limited to only using "Blogger 1.0" templates, without any of the new XML features or widgets. Still, this is usually sufficient for simple content management.

Publishing Via FTP from Blogger

Here's a walkthrough to getting set up with Blogger on your own FTP server.
  1. Go to www.blogger.com and click "Create blog now"
  2. Don't bother filling out any of the information just yet. Instead, click on "Advanced Blog Setup"
  3. On the following page you can enter account details for your FTP server. You can choose what path and filename to publish to, as well as whether to publish using FTP or SFTP. You can choose to publish to a .html file (e.g. index.html) or a .php file if your hosting supports it.

That's it! Assuming all the details have been entered correctly, Blogger should be set up to publish files to your FTP server now. It will publish the main page at the URL you specified, as well as archive pages and individual "permalink" pages for posts.

Customizing the Blogger Template

After setting up the FTP details, The following page presents readymade templates for Blogger. I'll assume you already have a layout you'd like to use. If not, then now would be a good time to make one.

Since we don't need to use the template for any layout, CSS or HTML other than the Blogger-specific code, it doesn't matter a great deal which template you choose. But for now, just choose Minima, so we're working with the same code base.

Select Minima, continue, and publish a post (the obligatory "Hello, world!" of your new blog). The click on the Template tab to get down to business, cut-and-paste style.

The next step is to open up your layout file in the editor of your choice (I prefer SciTE and Aptana personally) and get ready to cut-and-paste.

The first tags to grab are <$BlogPageTitle$> and <$BlogMetaData$>, which output the title of your blog and hook up the RSS feeds, respectively. The <$BlogPageTitle$> tag can be outputted as the page title (i.e. in <title></title> tags) as well as the header, in an <h1>.

The resulting code should look like this:

<head> <title><$BlogPageTitle$></title> <$BlogMetaData$> </head>

The <$BlogMetaData$> tag outputs a lot of good stuff including all flavors of RSS feeds (RSS 2.0, RSD, Atom).

Next, grab everything between and including the <Blogger></Blogger> tags. You'll want to paste this into the main wrapper of you site, where you want the dynamic content to be. This is typically something like <div id="main-content">. It's a lot of code to paste, and the indentation may be a bit off, but if you've correctly copied it, it should work fine.

As for styling, you can inspect the resulting HTML using Firebug and see the pre-defined classes. Or you can always add classes yourself to the Blogger code. Just make sure not to add any ids since the code will be repeated once for each post.

The Sidebar: Recent Posts, Archives, Profile

You may want to include archived posts, recent posts or your Blogger profile. It's quite simple, really. Scrolling down towards the bottom of the Minima HTML/Blogger tag source, you'll find an area marked with the comment "Begin #sidebar." Here is where we'll find the tags:

<$BlogMemberProfile$> This one outputs your Blogger profile pic and description. Nothing too useful but I guess it's nice for a blog site.

<ul id="recently"> <BloggerPreviousItems> <li><a href="<$BlogItemPermalinkURL$>"><$BlogPreviousItemTitle$></a></li> </BloggerPreviousItems> </ul>

This code outputs the recent posts, with links to their individual permalink pages.

<ul class="archive-list"> <BloggerArchives> <li><a href="<$BlogArchiveURL$>"><$BlogArchiveName$></a></li> </BloggerArchives> </ul>

This code links to the archive pages.

Hopefully this post has proven helpful and will give you the confidence to publish your own Blogger-powered dynamically driven sites! It's nothing fancy, but it gets the job done and is a breeze to set up. Post comments with any other tweaks you've found or issues you run into. Blogger certainly has its shortcomings but for the price (i.e. free), I'm not complaining, and it does a lot of things right too.

Have fun and happy bloggin'!

Labels:

Wednesday, April 23, 2008

PHP Starter Template

posted by Jonah Dempcy

Since Alex posted his HTML Starter Template article last week I was inspired to condense some of my starter code for building sites in PHP.

I use Aptana to code, so whenever I make a new project, I start by copying in files from 2 template projects I've made, xhtml-template and php-template. Each project has its own file structure and stub files. Since Alex already posted a thorough XHTML starter, I'll focus on the PHP template today.

The files and folders I include are as follows:

  • components
    • banner.php
    • doctype.php
    • footer.php
    • global.php
    • head.php
    • sidebar.php
  • css
    • styles.css
  • images
  • js
    • main.js
  • offline-files
    • docs
    • mocks
  • index.php
All of the PHP files in the components folder can be used by the pages on the site if need be, or deleted if not.

This is how I include the files on the homepage, index.php:


<?
   include($_SERVER['DOCUMENT_ROOT'] . '/components/global.php');

 $metaTags['keywords'] = '';
 $metaTags['description'] = '';
 $metaTags['author'] = '';
 $metaTags['copyright'] = '';
 $metaTags['charset'] = 'UTF-8';
 
 $pageTitle = '';
 $pageId = '';

   include($_SERVER['DOCUMENT_ROOT'] . '/components/head.php');
?>

<body>
   <? include($_SERVER['DOCUMENT_ROOT'] . '/components/banner.php'); ?>


   <? include($_SERVER['DOCUMENT_ROOT'] . '/components/footer.php'); ?> 
</body>

Inside the Components Directory

Here are the default contents of those files:

global.php

<?php
 $metaTags = new ArrayObject();
 $metaTags['keywords'] = '';
 $metaTags['description'] = '';
 $metaTags['author'] = '';
 $metaTags['copyright'] = '';
 $metaTags['charset'] = 'UTF-8';
 
 $pageTitle = '';
?>

This file contains global variables used by the whole site. You could store constants here as well. The $metaTags array stores the default meta tags for the site, which can be overridden on a per-page basis.

This page also contains the default page title for the whole site, stored in the $pageTitle variable. Again, this should be overridden on a page by page basis (for best SEO benefits) but it's just a fallback in case you forget to define the title of that page.

banner.php

<div id="banner">
 
</div>

The banner file doesn't actually contain any PHP code, just an empty div tag. It's a stub, ready to be added to if the site needs a banner (and really, most sites do).

You could go a step farther by adding in a default image, or h1 and h2 tags with CSS image replacement, but for me, this is sufficient.

doctype.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

You could combine the doctype file with the head file. It just comes down to preference for how granular you like the files to be, really.

footer.php

<div id="footer">
 
</div>

This is another stub. You could put default information such as copyright (using the &copy; HTML entity perhaps), or a homepage link. But I just leave it empty and customize it for whichever site I'm working on.

head.php


<?php include('doctype.php'); ?>

<head>
 
 <!-- CSS -->
 <link rel="stylesheet" href="css/styles.css" />

 <!-- Meta tags --> 
 <meta name="keywords" content="<?php echo $metaTags['keywords'] ?>" />
 <meta name="description" content="<?php echo $metaTags['description'] ?>" />
 <meta name="author" content="<?php echo $metaTags['author'] ?>" />
 <meta name="copyright" content="<?php echo $metaTags['copyright'] ?>" />
 <meta name="robots" content="FOLLOW,INDEX" />
 <meta http-equiv="Content-Type" content="text/html; charset=<?php echo $metaTags['charset'] ?>" />
 <meta name="MSSmartTagsPreventParsing" content="true" />
       <meta http-equiv="imagetoolbar" content="no" />  
 <!-- Title -->
 <title><?php echo $pageTitle; ?></title>
 
 <!-- Shortcut icon -->
 <link rel="shortcut icon" href="/favicon.ico"  />

</head>

The head has some fun features. Let's go through one by one. First we include the doctype, as shown above. Then we start the head and include the default CSS file, styles.css.

Next we include a number of meta tags, including a few you may not have heard about before. For instance, we include <meta http-equiv="imagetoolbar" content="no" /> which causes IE to stop showing that annoying disk icon in the bottom right of images when you hover over them. We also include copyright and author tags, and another one to inform MS apps not to try to parse it with SmartTags. (I got this one from Blogger templates).

After that, we just define the title (that's a must) and link the default favicon file.

An observant reader will notice that we didn't define the JavaScript in the head. The reason for this is that it usually hurts page performance to define it in the head. In this page template, I didn't include the link to the template, but I recommend either adding it to the footer.php file after the closing div or creating a new file like so:

javascript-includes.php

<script src="/js/main.js" type="text/javascript"></script>



Default JavaScript to Include

As for JavaScript, I don't have a lot of helper methods or anything that I like to include since that is all taken care of by the library, ideally. But I do include this one piece of code which enables CSS background-image caching in IE:
// IE6 does not cache CSS background images by default.
// This code enables CSS background caching for the lifespan of the browsing session in IE6.

try {
    document.execCommand('BackgroundImageCache', false,true);
} catch(e) {};

Other than that, I don't really include any boilerplate code. But suggestions are welcome! What default JavaScript templates do you use? Feel free to post your own in the comments.

Default CSS to Include

I am a big fan of Eric Meyer's reset styles. My default CSS pretty much looks like that:
/* Reset styles (adapted from Eric Meyer's Reset Reloaded) */
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
 margin: 0;
 padding: 0;
 border: 0;
 outline: 0;
 font-weight: inherit;
 font-style: inherit;
 font-size: 100%;
 font-family: inherit;
 vertical-align: baseline;
}
/* remember to define focus styles! */
:focus {
 outline: 0;
}
body {
 line-height: 1;
 color: black;
 background: white;
}
ol, ul, li {
 list-style: none;
}
/* tables still need 'cellspacing="0"' in the markup */
table {
 border-collapse: separate;
 border-spacing: 0;
}
caption, th, td {
 text-align: left;
 font-weight: normal;
}
blockquote:before, blockquote:after,
q:before, q:after {
 content: "";
}
blockquote, q {
 quotes: "" "";
}

/* Site styles */

/* Legend:


*/

/* Sitewide */

body {
 font-size: 11px;
 font-family: Helvetica, Arial, sans-serif;
 color: #000000;
}

h1 {
 font-weight: bold;
 font-size: 27px;
}

h2 {
 font-weight: bold;
 font-size: 16px;
 color: #000000;
}

h3 {
 color: #000000;
 font-size: 15px;
 font-weight: bold;
}

h4 {
 font-size: 12px;
 font-weight: bold;

}

h5 {
 color: #000000;
 font-size: 11px;
}


a:link {
 color: #000000;
 text-decoration: none;
}
a:visited {
 color: #000000;
 text-decoration: none;
}

a:hover {
 color: #000000;
 text-decoration: underline;
}

a:active {
 color: #000000;
 text-decoration: underline;
}

/* wrapper */

div#wrapper {
    
}

/* banner */

div#banner {
    
}

/* footer */

div#footer {
    
}

I could probably stand to update the CSS a bit. Right now the stubs are, well, just stubs-- I intend to add stronger default styles that are consistent with most site defaults nowadays. Maybe that will be the topic of a future article. Regardless, this is a nice blank slate to start out with.

What is your default template for starting websites? Comments are encouraged.

Labels: , ,

Monday, April 21, 2008

MooTools 1.1 Cheatsheet

posted by Jonah Dempcy
Get the mootools 1.1 cheat sheet by visiting this linkThis is probably old news to most MooTools developers out there but if you are just getting into MooTools, it's a life-saver. In June 2007, Maik Vlcek of the mediaVROG Blog posted this excellent MooTools cheatsheet.

Prior to this, I used the also-excellent MooTools beta cheatsheets released by snook.ca.

Excerpt:

mootools 1.1 - cheat sheet | mediaVROG Blog

Nine hours have passed and I'm finally up to present the mootools 1.1 cheat sheet.

All classes documented have found their way into this sheet. Well, I had to embezzle the plugins as it would have gone beyond the scope of creating a compact overview of mootools.

For all you mootools lovers, here it is! Go print and paste it on your walls ;) Enjoy!

Link

Monday, April 14, 2008

HTML Template Starter

posted by Alex Grande
Dreamweaver, Aptana, and all the other editors have a base HTML code for the <html>, <head>, and <body>. I don't think any of these editors supply the web developer with all the need for a successful websites. I made one with comments that you can copy and paste in.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- Just in case someone integrates a CMS that uses old tags like <font> or <b> -->
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta name="keywords" content="" /> <!-- Keywords relevent to your website or for SEO purposes go here. -->
  <meta name="description" content="" /> <!-- Sentences that describe your site and will be shown on search engines go here.  -->
  <meta name="author" content="" /> <!-- The author of the website, or webmaster's name goes here -->
  <meta name="copyright" content="Copyright © 2007 YOUR_COMPANY_HERE." /> <!-- This way you are more legally protected -->
  <meta name="robots" content="FOLLOW,INDEX" /> <!-- This makes sure search engines check out your website -->
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  <link rel="stylesheet" href="styles.css" /> <!-- You'll need a stylesheet. Most people call it styles.css. Which folder it goes in is up to you -->
  <title></title>
 </head>
 <body>
 <!--[if lte IE 5]>  <!-- These are IE conditional comment style classes to fix all those dumb IE problems -->
 <div class="ie">
 <![endif]-->
 <!--[if IE 6]>
 <div class="ie ie6">
 <![endif]-->
 <!--[if IE 7]>
 <div class="ie ie7">
 <![endif]-->



 <script src="http://www.savethedevelopers.org/say.no.to.ie.6.js"></script> <!-- You'll probably need some sort of script and it should go to the bottom of the page; usually called main.js or jquery.js. This will warn your users they are using IE6. Bad user; no internet. -->
 <!--[if IE]> <!-- End of IE conditional comments -->
 </div>
 <![endif]-->  
 </body>
</html>

Labels:

Thursday, April 10, 2008

What Are Good Styles to Use in All CSS Stylesheets?

posted by Alex Grande
These are a list of styles you can't live without. I suggest using these in all your stylesheets to help in a variety of situations. Because headers matter A LOT in SEO. It is important to avoid using h1, h2, h3, etc for ambiguous content such as "Contact Me" or "Support" titles that sit at the top of your page. What's more important for headers is to use them for things specific to your site. For The True Tribe we would use headers for "CSS Good Practices" or "Alex Grande" , but not "Our Heros." At least we should... The way around this is to make classes called h1, h2, h3, etc and give them properties that are similar to headers.
.h1 {
   font-size: 22px;
   font-weight: 600;
}

.h2 {
   font-size: 20px;
   font-weight: 600;
}

.h3 {
   font-size: 18px;
   font-weight: 600;
}

.h4 {
   font-size: 16px;
}

.h5 {
   font-size: 14px;
}
Other font related styles include, but not limited to...
.bold {
   font-weight: 600; /* I use this one a lot! Instead of using the b tag use 
I'm Bold...that's what she said...*/
}

.underline {
   text-decoration: underline;
}
For positioning styles... This is when all the children of a parent are removed from the page flow and need something to fill the wrapper parent.

.clearFix { 
 clear: both;
 height: 0;
 width: 0;
 border: 0;
}
There are more but these are ones I use almost everyday for my clients. I hope you'll find it useful as well.

Labels: ,

Redefining console.log() For Browsers Without Firebug

posted by Jonah Dempcy
Firebug

I use Firebug's console.log() method extensively when developing code. But, when viewing the site in Internet Explorer, Safari, Opera or other browsers that don't have Firebug, console.log() throws an error. Rather than wrap each log statement in a try/catch, I just add this bit of code during development that checks for Firebug and redefines the console.log() method if Firebug isn't installed:

 // When logging messages with console.log()
 // if user doesn't have Firebug, write them to the DOM instead
 if (typeof console == "undefined") {
 
  // Create an unordered list to display log messages
  var logsOutput = document.createElement('ul');
  document.getElementsByTagName('body')[0].appendChild(logsOutput);
  
  // Define console.log() function
  console = {
   log: function(msg) {
    logsOutput.innerHTML += '<li>' + msg + '</li>';
   }
  };
 }

Logging Statements in Production Code

If you don't want to have to remove your console.log() statements in production code, you can set a "development mode" flag that ignores log messages.
var DEVELOPMENT_MODE = true;

// When logging messages with console.log()
// if user doesn't have Firebug, write them to the DOM instead
if (typeof console == "undefined" && DEVELOPMENT_MODE == true) {
 
  // Create an unordered list to display log messages
  var logsOutput = document.createElement('ul');
  document.getElementsByTagName('body')[0].appendChild(logsOutput);
  
  // Define console.log() function
  console = {
    log: function(msg) {
      logsOutput.innerHTML += '<li>' + msg + '</li>';
    }
  };
} else if (DEVELOPMENT_MODE == false) {
  console = {
    log: function() {} // Do nothing
  };
}
I made the flag uppercase since it follows naming conventions for constants. Now you can set the DEVELOPMENT_MODE flag when deploying to production environments and leave your logging messages intact, if you desire.

Take note that this adds unnecessary page-weight from the logging messages, so to fully optimize the code, logging should be removed. But, leaving some amount of logging in the production code is probably acceptable, as long as it doesn't add more than a few kilobytes to the page weight. As long as you're using GZip, minifying your JavaScript and serving it with far future expires headers, a few extra k aren't going to hurt anything.

Redefining All Firebug Methods

If you use other Firebug methods, such as assert(), trace() or error(), then you may want to use the following code, provided by the developers of Firebug as part of the Firebug Lite project.

This code redefines all Firebug methods as empty functions when Firebug isn't installed on the browser:
if (!window.console || !console.firebug) {
  var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
  "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];

  window.console = {};
  for (var i = 0; i < names.length; ++i)
    window.console[names[i]] = function() {}
}

Labels: , ,

Sunday, April 6, 2008

How do I make tabs in Jquery? (Without jQ Tabs plugin)

posted by Alex Grande
Strategies and Technologies: This is the beginning of a series where I build a tab presented component from scratch. Each series on the tab will increase in functionality and ux. For starters lets just build it. To see the finishing product of this lesson please check out this link to see a tab example. Because we are using CSS Sprites there is only one image for the tabs. Check out this link to see the image.

HTML

<!-- This is to deal with Internet Explorer-->
<!--[if lte IE 5]><br /><div class="ie"><br /><![endif]-->
<!--[if IE 6]><br /><div class="ie ie6"><br /><![endif]-->
<!--[if IE 7]><br /><div class="ie ie7"><br /><![endif]-->

<!-- This is the list of tabs. Notice the id begins with the content in the tab. This can be easily replaced with 1, 2, 3 using a simple js loop. Or your backend developer can supply you with a variable to make it scalable. -->
<div id="tabSelection">
   <div class="tab-selects" id="salsa-tab">
       <h3>salsa</h3>
   </div>
   <div class="tab-selects" id="fern-tab">
       <h3>fern</h3>
   </div>
   <div class="tab-selects" id="jazz-tab">
       <h3>jazz</h3> 
   </div>
   <div class="tab-selects" id="sodo-tab">
       <h3>sodo</h3>
   </div>
</div>

<!-- This is the content of the tabs. See how the id matches the name in the tab?-->
<div id="content">
   <div id="salsa-tab-content" class="view-content">
 <img src="http://farm3.static.flickr.com/2205/2346526197_81a11816fa.jpg?v=0" />
</div>
   <div id="fern-tab-content" class="view-nonselected view-content">
 <img src="http://farm1.static.flickr.com/188/404190495_7c193a1873.jpg?v=0" />
</div>
   <div id="jazz-tab-content" class="view-nonselected view-content">
 <img src="http://farm4.static.flickr.com/3207/2338432523_c9029c38b9.jpg?v=0" />
</div>
   <div id="sodo-tab-content" class="view-nonselected view-content">
 <img src="http://farm4.static.flickr.com/3113/2339197062_01d0950920.jpg?v=0" />
</div>

</div>
<!--[if IE]><br /></div><br /><![endif]-->

CSS

body {
margin-top:50px;
}

/* Here are the sprites. Only the left part of the image is shown for the selected tab. I love sprites! */
div.tab-selects {
width:111px;
background:url(tab-select-nonselect.gif) no-repeat;
background-position:left center;
float:left;
margin:-40px 10px 0pt;
position:relative;
cursor:pointer;
height:51px;
}

/* Here is a non selected tab. The image on the right! */
div.tab-nonselected {
background-position:right center;
}

div.view-wrapper {
position:relative;
margin-top:74px;
}

/* IE sucks */
.ie div#content {
clear:both;
margin-top:-10px;
}

div#content {
width:610px;
position:relative;
cursor:default;
background-color:#5C81AD;
margin-top:-1px;
border-bottom:1px solid #9D8979;
border-right:1px solid #9D8979;
border-left:1px solid #9D8979;
height:410px;
}

div.view-content {
top:0px;
position:absolute;
width:100%;
overflow-y:auto;
height:410px;
}

div.tab-selects {
text-align:center;
}

div.view-content img {
padding:23px;
}

div.view-nonselected {
visibility:hidden;
}

jQuery Javascript Tabs


//The first tab is going to be opened. We need to know that.
$TabIsOpen = $("#tabSelection div:first");  
    
// The click event
 $('.tab-selects').click(
  function() {   
// So if what you click is not the tab that is already opened then lets change its class
   if ($(this) != $TabIsOpen) {
// Look at the CSS to see the properties of this clas
   $TabIsOpen.addClass('tab-nonselected');
    $(this).removeClass('tab-nonselected');
// Now that this tab is selected we gotta remember it.
   $TabIsOpen = $(this);
  
// jQ can be annoying in creating selectors. But basically we take the id of the tab and it is associated with the content item. We just have to add -content to the end. For instance, #salsa-content.
   contentIdChildDiv = "#" + $TabIsOpen.attr("id") + "-content";
// Same deal with the tabs; Hiding and showing.  
 $("div.view-content").addClass("view-nonselected");
   $(contentIdChildDiv).removeClass("view-nonselected");
  }
 });

// We want to make every instance but the first one hidden for both tabs and content. That's what that crazy div:not(div:first) is all about.
$("#content div:not(div:first)").addClass("view-nonselected");
$("#tabSelection div:not(div:first)").addClass("tab-nonselected");

Labels: , ,

Wednesday, April 2, 2008

JavaScript Challenge: Cardinal Numbers

posted by Jonah Dempcy


This challenge is to write functions to add and subtract numbers using the names of numbers as strings. More precisely, the challenge is to write add() and subtract() functions that take two strings as input, and return one string. For example:
add("one", "ten"); // should return "eleven"
subtract("twenty", "fifteen"); // should return "five"
For the purposes of this exercise, we will limit the maximum numbers handled to 99. So, you don't have to handle, say, add("three-thousand-five-hundred-and-fifty-two", "ten-million-and-seven"). Though, you certainly get bonus points if you handle this case as well.

Here is my solution below, which is by no means optimal. The following is my first attempt at solving this issue, coded in roughly 30 minutes, so go easy if you find any SNAFUs.

Note: I prefer 4-space indentation but I set it to 2-space for this article so the code example will fit on the page without horizontal scrolling.
var cardinalNumbers = {
    "one": 1,
    "two": 2,
    "three": 3,
    "four": 4,
    "five": 5,
    "six": 6,
    "seven": 7,
    "eight": 8,
    "nine": 9,
    "ten": 10,
    "eleven": 11,
    "twelve": 12,
    "thirteen": 13,
    "fourteen": 14,
    "fifteen": 15,
    "sixteen": 16,
    "seventeen": 17,
    "eighteen": 18,
    "nineteen": 19
}

var multiplesOfTen = {
    "twenty": 20,
    "thirty": 30,
    "forty": 40,
    "fifty": 50,
    "sixty": 60,
    "seventy": 70,
    "eighty": 80,
    "ninety": 90
}
                        
function convertWordToNumber(word) {
  var number = new Number(); // this is where we will store the result              
  
  if (word.indexOf("-") == -1) { // If it is less than 20
    number = cardinalNumbers[word];
  } else {
    var multipleOfTen = word.split("-")[0];   // e.g. "seventy"
    var cardinalNumber = word.split("-")[1];   // e.g. "six"
    number = multiplesOfTen[multipleOfTen] + cardinalNumbers[cardinalNumber]; 
  } 
  
  return number;
}

function convertNumberToWord(number) {
  var word = new String(); // this is where we will store the result
    
  if (number < 20) {
    for (var cardinalNumber in cardinalNumbers) {
      if (number == cardinalNumbers[cardinalNumber]) {
        word = cardinalNumber;
        break;
      }
    }
  } else if (number < 100) {
    if (number % 10 == 0) { // If the number is a multiple of ten
      for (var multipleOfTen in multiplesOfTen) {
        if (number == multiplesOfTen[multipleOfTen]) {
          word = cardinalNumber;
          break;
        }
      }
    } else { // not a multiple of ten
      for (var multipleOfTen in multiplesOfTen) {
        for (var i = 9; i > 0; i--) {
          if (number == multiplesOfTen[multipleOfTen] + i) {
            word = multipleOfTen + "-" + convertNumberToWord(i);
            break;
          }
        }
      }
    }
  } else {
    alert("We don't handle numbers greater than 99 yet.");
  }
    
  return word;
}

// These functions takes words as inputs, e.g. "one" and "two"

function add(x, y) {
  return convertNumberToWord(convertWordToNumber(x) + convertWordToNumber(y));
}

function subtract(x, y) {
  return convertNumberToWord(convertWordToNumber(x) - convertWordToNumber(y));
}


// Test convertWordToNumber()
alert(convertWordToNumber("three"));               // should alert 3
alert(convertWordToNumber("seventy-three"));       // should alert 73   


// Test convertNumberToWord()
alert(convertNumberToWord(8));                     // should alert "eight"
alert(convertNumberToWord(46));                    // should alert "forty-six"


// Test add() 
alert(add("one", "two"));                          // should alert "three"

// Test subtract() 
alert(subtract("fifteen", "eleven"));              // should alert "four"
Feel free to post your solutions in the comments. Particularly good solutions will get an entire article devoted to them.

Labels: ,