jQuery UI multiple draggable plugin

Sunday, 29 November 2009

View Comments

The jQuery multiple draggable plugin is an extension to the jQuery UI Draggable plugin. This plugin extends the current functionality to allow for elements to be grouped and dragged as a group. The aim of the plugin is to include all of the current functionality listed in the options.

To group and ungroup elements simply control click, the ui-multidraggable extension is applied for styling purposes and also to alert the plugin as to which elements are grouped.


You can view the following demos:

You can also download the plugin and the demos from here.

An update has now been provided for this plugin thanks to Caili Crane @ Pulse Energy allowing the plugin to be used with jQueryUI 1.8.8 and jQuery 1.4.4 and can be downloaded here.

As this is an extension of the base functionality it is also released under a dual GPL and MIT license. You can view copies of these licenses in the package when you download the source from the link above.

Following is a list of all the options from the draggable element and how they work with the multidraggable plugin:

  • addClasses works and is set on an individual level
  • appendTo works and is set on an individual level
  • axis works and is set on an individual level
  • cancel works and is set on an individual level
  • containment works and is set on an individual level
  • cursor works and is set on an individual level
  • cursorAt works and is set on an individual level
  • delay works and is set on an individual level
  • distance works and is set on an individual level
  • grid works and is set on an individual level
  • handle works and is set on an individual level
  • helper works and is set on an individual level
  • iFrameFix works and is set on an individual level
  • opacity works and is set on an individual level
  • refreshPositions works and is set on an individual level
  • revert works and is set on an individual level
  • revertDuration works and is set on an individual level. Note: this currently throws an error if two elements have different revertDurations and you attempt to drag before both have reverted to their original position.
  • scope works and is set on an individual level
  • scroll works and if any in the group have scroll set to true then enclosing element will scroll
  • scrollSensitivity works and is set on an individual level
  • scrollSpeed works and is set on an individual level, but once all elements are outside the original container the speed of the fastest grouped element is used
  • snap works and is set on an individual level
  • snapMode works and is set on an individual level
  • snapTolerance works and is set on an individual level
  • stack works, whichever element from the group is the one being dragged will have the highest zIndex
  • zIndex works and is set on an individual level

The only functionality I am aware of that currently does not work is the connectToSortable which I am working on resolving for the next release.

As you can see from the working options above, these settings are set on an individual but are likely to work more cohesively if they have the same values and options set. This was purposely done to allow for flexibility and usability (you may want certain items to have different opacity etc.).

I have very pleased to be able to release this plugin and hope it is useful. My first attempt at doing this nearly a year ago was miles away from where it is now and I am grateful to this post for teaching me how to do this the correct way.

Please let me know if you have any constructive feedback and if you are using this plugin anywhere.

Enjoy!
more...

How can Chrome OS win market share?

Thursday, 26 November 2009

View Comments

Who really rules the net? I know with most popular culture that it's somewhere between 8 and 18 year olds with an ever increaing dispensible income; they decide our music stars, our fashion and the growing movie takings. They brought us the Spice Girls, then Brittany, Miley and now New Moon. God bless their socks, they have brought us some real shit, but I'm not going to turn this into a rant about how teenage girls should really be listening to Radiohead.


God knows they've tried to influence the net; Facebook, lolcatz and Perez. But real IT companies are not considered with the steady stream of cultural detrits that is floating around the web. What they are more concerned about is how you are viewing it. You see, the folks at Redmond are sitting on one giant nest-egg that has been around for years that according to statcounter around 90% of us worldwide are continuing to use.

So who decides which OS is king? Is it just a matter of cool? I doubt it, I'm not sure where Windows sits on the cool-o-meter and I don't think that operating systems as a whole are the kind of cool that the kids are interested in. They are kind of the opposite to the big guys, they are more interested in what they are looking and not what they are using. I would argue that the driving influence for any computer technology are those of us who actually care.

Why is my Mother now using Firefox when 6 months ago she was stuck on IE6? Because someone who cared bothered to go and install an alternative. A couple of years back I may not have bothered, but what has made this shift in the environment that now so many people are moving away from Explorer and installing Chrome or Firefox or something else. We have! The people who care. The stuff floating around the net may belong to our cultural heroes, but what you are using belongs to us. The developers, the tech writers, the support team and the salesmen, not the guy on the street.

If it were just a matter choice maybe the Mac guys or the Linux guys would have had a better share, but recently there has been a real movement to change and you can see it in the last twelve months of browser statistics. It has been mobilised through blogging and demonstrated by the number of installs on friends and parents machines. So why is this so important to the OS market?

If you have ever tried, you will know how hard it is to convince someone to switch their browser, but once they have switched it is just as hard to switch them back. So, although at most major stores across the world your shiny new PC will come pre-installed with the latest version of Explorer, these same friends and parents will come back and ask you to reinstall their new favourite browser.

I just finished reading Robert Scoble's latest post titled "Why Google Chrome OS has already won":

"Google is in a war over developers with Microsoft. Google wants developers to build for the open web. Microsoft wants developers to build for Silverlight. Those messages are VERY clear coming out of both camps now."


I would argue that Google is not in a war over developers for the sake of building an open web, but for the reasons I have already mentioned. Google's war is not won at the storefront, but at the same place that Firefox won its battle. They want the tech guys to get off their backsides and install their new operating system on as many Friends / Mum and Dad machines as possible. Yes they are battling over developers, not to write code for a specific purpose, although they are trying to win the developers with their code, but they want the developers to be inspired to change.

In the end the cool kids will still use whatever is put in front of them, they can't tell the difference between Greenday and My Chemical Romance. The only way to change what the average Joe is using is if we can be bothered enough to go out and do it ourselves and now that Google knows that, hopefully they can deliver an OS good enough for us to get out there and do it.
more...

jQuery flickr widget

Wednesday, 25 November 2009

View Comments


This tutorial shows you how to build a jQuery widget for your flickr photos or for your favourite flickr group.

Here is a look at the widget we will be building:

You can download the code for this widget here.

To build the widget we need to grab the jQuery hoverscroll plugin which allows us to do the nice scrolling action.

The rest is just a simple call to flickr's JSON API to retrieve the feed and another function to scale the images to size.

If you want to read about importing your flickr feed with jQuery and JSON I suggest you read this helpful post.

To begin with we simply grab our feed via JSON:

$("document").ready(function() {
     $.getJSON("http://api.flickr.com/services/feeds/groups_pool.gne?id=32142572@N00&lang=en-us&format=json&jsoncallback=?", function(data){
          $.each(data.items, function(i,item){
          $("<img/>").attr("src", item.media.m).appendTo("#images")
               .wrap("
<li><a href='" + item.link + "'></a></li>
");
          });
          
          $('#images').hoverscroll({
               vertical: true,
               width: 166,
               height: 400
          });

The main part of the call is the jQuery getJSON call, this calls the flickr API with the group ID of the group you want to display. You can easily get this URL and ID as it is almost identical to the RSS feed of the group. This feed is displayed at the bottom of the page, so it's a matter of clicking on the feed, grabbing the URL and changing the format over to JSON as above.

To view the different kinds of feed that flickr offers follow this link here.

The jQuery hoverscroll plugin is then called to build our lovely scrolling widget. You can view more detail about how the plugin works here.

The next part of the script just loops over the images and scales them accordingly.

$('#images img').each(function() {
               var maxWidth = 160; // Max width for the image
               var maxHeight = 160;    // Max height for the image
               var ratio = 0;  // Used for aspect ratio
               var width = $(this).width();    // Current image width
               var height = $(this).height();  // Current image height
      
               // Check if the current width is larger than the max
               if(width > maxWidth){
                    ratio = maxWidth / width;   // get ratio for scaling image
                    $(this).css("width", maxWidth); // Set new width
                    $(this).css("height", height * ratio);  // Scale height based on ratio
                    height = height * ratio;    // Reset height to match scaled image
                    width = width * ratio;    // Reset width to match scaled image
               }
      
               // Check if current height is larger than max
               if(height > maxHeight){
                    ratio = maxHeight / height; // get ratio for scaling image
                    $(this).css("height", maxHeight);   // Set new height
                    $(this).css("width", width * ratio);    // Scale width based on ratio
                    width = width * ratio;    // Reset width to match scaled image
               }
          }); 

I won't go into detail as to how the above works, it is just some math to scale the image to the provide constraint. Here I have set maxWidth and maxHeight to 160px.

The rest is just a bit of HTML and css.

Enjoy!
more...

Best jQuery games from across the net

Today I'm going to look at some of the best jQuery games on the net. As javascript speeds get faster and the great things you can now do with jQuery make it possible to produce some good quality games. Although speed and sizes are a bit of a restriction to the type of game that can be produced (no 3D shooters yet) these are great games none the less and some are quite addictive.

I hope to show you a range of what is available. Although there are quite a few jQuery games out there, I chose these for their playability and completeness. You can tell these guys put in a decent effort to make a quality product.


I'd like to start with the jQuery pong clone. Why play against your friends when you can play against your browser! This game has a whole bunch of options that you can set yourself if you decide to download it for yourself including ball speed, score to play to and paddle sizes. Click on the image to follow the link.



The next game is extremely addictive, but based on a simple idea. In jCharacterfall, water drips from the ceiling and you stop them from hitting the ground by pressing the matching letter. As the game goes on the drops fall faster and faster. Hours of fun!



Next up is a sudoku game. This is a nice clean implementation and is a real challenge to get your name up in the highscores (not like all the other hacked high-score lists!). Only thing I think is missing is the ability to pencil in smaller guesses.



Sticking with the retro theme is an old fashioned snake clone. You know the score, snake goes round, eats stuff, crashes into walls.



The next one is really special and a lot of effort has gone in to building it. Jon Raasch has built a clone of the old NES game T&C Surf Designs. It looks and handles like the classic Nintendo game and the sprites look great.



If you have any more suggestions for great jQuery games leave a comment.

Have fun and enjoy!
more...

jQuery weather widget

Tuesday, 24 November 2009

View Comments


In this tutorial we are going to build a jQuery weather widget. This widget is pure javascript and requires no back-end/server so you can place it on your blog or anywhere you like.

Please note: Information in this widget should not be taken as accurate under any circumstance and should not be used to make any decisions in any circumstance. It is purely a toy. We make no claim to its accuracy for any purpose.


You can get the code for this tutorial here.

The widget makes two main calls using JSON to retrieve the users geolocation and then to retrieve the local weather for that location. It also uses the jQuery cookie plugin to remember the details so that it doesn't need to make these calls each time the page reloads.

The two JSON calls are to the geoPlugin site and then to the geonames weather webservice using the retrieved longitude and latitude coordinates. Please check the terms and conditions from their site if you want to use this widget on your own site. It also uses Googles weather images.

Let's look at the first part of the javascript code. We begin with the jQuery document ready function:

$("document").ready(function() {
     if ($.cookie('loc_longitude') && $.cookie('loc_latitude')) {
          getWeather();
     } else {
          $.getJSON("http://www.geoplugin.net/json.gp?callback=?", function(data) {
                eval(data);
          });
     }
});

It begins by checking if the geocordinates for this user have been set for this user. If not it calls the geoplugin site to retrieve geolocation details.

function geoPlugin(data) {
     $.cookie('loc_latitude', data.geoplugin_latitude, {expires: 7}); 
     $.cookie('loc_longitude', data.geoplugin_longitude, {expires: 7});
     $.cookie('loc_country', data.geoplugin_countryName, {expires: 7});
     $.cookie('loc_region', data.geoplugin_region, {expires: 7});
     $.cookie('loc_city', data.geoplugin_city, {expires: 7});
     $.cookie('loc_country_code', data.geoplugin_countryCode, {expires: 7});
     getWeather();
}

The eval function above calls the geoPlugin function and sets the geolocation details in the cookie, this will be used in the getWeather function which you will notice is called above if the location details are already set.

function getWeather() {
     var latitude = $.cookie('loc_latitude');
     var longitude = $.cookie('loc_longitude');

     var loc_conditions = $.cookie('loc_conditions');
     var loc_conditions_img = $.cookie('loc_conditions_img');
     var loc_temp = $.cookie('loc_temp');
     var loc_humidity = $.cookie('loc_humidity');

     if (loc_conditions && loc_conditions_img) {
          setConditions(loc_conditions, loc_conditions_img, loc_temp, loc_humidity);
     } else {
          var url = "http://ws.geonames.org/findNearByWeatherJSON?lat=" + latitude + "&lng=" + longitude + "&callback=?";
          $.getJSON(url, function(data) {
               var clouds = data.weatherObservation.clouds;
               var weather = data.weatherObservation.weatherCondition;
               var temp = data.weatherObservation.temperature;
               var humidity = data.weatherObservation.humidity;
   
               var conditions_img = getConditions(clouds, weather);
   
               var conditions = '';
               if (weather == 'n/a') {
                    if (clouds == 'n/a') {
                         conditions = 'fine';
                    } else {
                         conditions = clouds;
                    }
               } else {
                    conditions = weather;
               }
   
                    $.cookie('loc_conditions', conditions); 
                    $.cookie('loc_conditions_img', conditions_img); 
                    $.cookie('loc_temp', temp); 
                    $.cookie('loc_humidity', humidity); 
                    setConditions(conditions, conditions_img, temp, humidity);
          });
     }
}

The getWeather function starts by retrieving all of the geolocation details from the cookie. It then checks the cookie to see if the temperature details have been set in the cookie, if they have it simply goes ahead and displays all the information in the widget by calling the setConditions function.

If the temperature details haven't been set in the cookie it then calls the JSON weather service using the latitude and longitude details from the first JSON call. It then calls the getConditions function to retrieve the correct image corresponding to the current weather condition.

function getConditions(clouds, weather) {
     if (weather == 'n/a') {
          switch (clouds) {
               case 'n/a':
                    return 'sunny.gif';
               case 'clear sky':
                    return 'sunny.gif';
               case 'few clouds':
                    return 'partly_cloudy.gif';
               case 'scattered clouds':
                    return 'partly_cloudy.gif';
               case 'broken clouds':
                    return 'partly_cloudy.gif';
               default:
                    return 'cloudy.gif';
          }
     } else {
          weather = weather.replace('light ', '').replace('heavy ', '').replace(' in vicinity', '');
          switch(weather) {
               case 'drizzle':
                    return 'rain.gif';
               case 'rain':
                    return 'rain.gif';
               case 'snow':
                    return 'snow.gif';
               case 'snow grains':
                    return 'sleet.gif';
               case 'ice crystals':
                    return 'icy.gif';
               case 'ice pellets':
                    return 'icy.gif';
               case 'hail':
                    return 'sleet.gif';
               case 'small hail':
                    return 'sleet.gif';
               case 'snow pellets':
                    return 'sleet.gif';
               case 'unknown precipitation':
                    return 'rain.gif';
               case 'mist':
                    return 'mist.gif';
               case 'fog':
                    return 'fog.gif';
               case 'smoke':
                    return 'smoke.gif';
               case 'volcanic ash':
                    return 'smoke.gif';
               case 'sand':
                    return 'dust.gif';
               case 'haze':
                    return 'haze.gif';
               case 'spray':
                    return 'rain.gif';
               case 'widespread dust':
                    return 'dust.gif';
               case 'squall':
                    return 'flurries.gif';
               case 'sandstorm':
                    return 'dust.gif';
               case 'duststorm':
                    return 'dust.gif';
               case 'well developed dust':
                    return 'dust.gif';
               case 'sand whirls':
                    return 'dust.gif';
               case 'funnel cloud':
                    return 'flurries.gif';
               case 'tornado':
                    return 'storm.gif';
               case 'waterspout':
                    return 'storm.gif';
               case 'showers':
                    return 'storm.gif';
               case 'thunderstorm':
                    return 'thunderstorm.gif';
               default:
                    if (weather.indexOf("rain")) {
                         return 'rain.gif';
                    } else if (weather.indexOf("snow")) {
                         return 'snow.gif';
                    } else if (weather.indexOf("thunder")) {
                         return 'thunderstorm.gif';
                    } else if (weather.indexOf("dust")) {
                         return 'dust.gif';
                    } else {
                         return 'sunny.gif';
                    }
          }
     }
}

getConditions is simply a couple of large switch statements to retrieve the correct image. Once this is returned the image and the conditions details are set in the cookie. The last step is calling the setConditions function to display the details in the widget.

function setConditions(conditions, conditions_img, temp, humidity) {
     var country = $.cookie('loc_country');
     var region = $.cookie('loc_region');
     var city = $.cookie('loc_city');
     var loc_country_code = $.cookie('loc_country_code');
     if (loc_country_code == 'US') {
          temp = parseInt(temp) + 32;
          temp_type = "F";
     } else {
          temp_type = "C";
     }

     $("#weather_widget").append("");
     $("#weather_widget").append("

" + country + "

" + city + ", " + region + "

Temp: " + temp + "° " + temp_type + "

Humidity: " + humidity + "%

" + conditions.substr(0, 1).toUpperCase() + conditions.substr(1) + "

"); }

The rest is just a little bit of css and html.

Enjoy!
more...

Christmas digital art from deviantART

Monday, 23 November 2009

View Comments

Hope everyone is starting to get into the festive spirit. Today I've had a good look through deviantART and selected some of my favourite designs. I hope you like them and they help to brighten your holidays.


The first one I picked is fractal art, there are a whole bunch of really good fractal art Christmas images so if you like this kind of thing make sure you browse the category.



The second picture I chose looks like a movie poster and subtle Christmas reference is just the icing on the cake to an already fantastic image.




I really like this next picture, you have to look at it a few times to decide how you feel about it.



This next one is a great Christmas card design, it's a shame that there isn't a larger version available. Some lucky people will be getting this card.



Here is another great fractal. It looks like Christmas flowers growing out of mould; kind of strange, but Christmas like.



This next image was actually the first one I chose and it really stood out amongst the others. Something stark and beautiful. It uses great Christmas imagery in a different context.



The last one is some art done over the top of an existing photo. A job well done that really captures the moment.



I hope wherever you end up and whatever you believe this Christmas that you have a fantastic time with friends and family.

Take care and enjoy!
more...

What is most important in a browser? Microsoft discuss IE9

Dean Hachamovitch, General Manager, Internet Explorer recently blogged on the future release of Explorer, IE9. Titled 'An Early Look At IE9 for Developers', the post covers the direction that Microsoft are aiming to take with the next release of Explorer. If you want a good idea of the reaction to this post I suggest you check out the posts comments.


A picture says a thousand words, so here are some pictures (and quotes) taken out of context and hopefully they will say something:

One common test of script performance is from Apple's Webkit team, the SunSpider test




Our goal is to deliver better performance across the board for real-world sites, not just benchmarks.


With IE8, we delivered a highly-interoperable implementation of CSS 2.1


Some standards tests - like Acid3 - have become widely used as shorthand for standards compliance, even with some shortcomings. As we improve support in IE for technologies that site developers use, the score will continue to go up.




A more meaningful (from the point of view of web developers) example of standards support involves rounded corners.






Another example of standards support that matters to web developers is CSS3 selectors.




Ultimately, we want to work with the community and W3C and other members of the working groups to define true validation test suites, like the one that we're all working on together for CSS 2.1, for the standards that matter to developers.



more...

Google AJAX libraries CDN

Thursday, 19 November 2009

View Comments

Are you using the Google AJAX libraries CDN for your applications? Google offers the simplest and fastest way to deliver the latest version of your favourite AJAX libraries over the net.

With a few lines of code you can grab your preferred libraries without having to download or host them yourself. You can even specify a specific version.

Libraries include:

I now regularly use this method to include jQuery and the jQuery UI and the speed is definitely comparable to hosting it yourself.

Here's an example of including jQuery 1.3.2:
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
 google.load("jquery", "1.3.2");
</script>

It also parses as valid HTML.

No need to worry about downloading another copy of jQuery just include the lines above to get the version you need.

John Resig also mentioned in the first jQuery podcast that eventually core plug-ins and add-ons will also be available across the CDN so look forward to seeing that in the near future also.

Enjoy!
more...

jQuery Twitter streaming widget in 30 lines

Wednesday, 18 November 2009

View Comments


Today we are going to build a widget for streaming Twitter search results using jQuery. This widget only requires 30 lines of javascript and the rest is CSS and a small bit of HTML.

The widget takes in a search term, in the example we are searching for the term "jQuery". It then updates every 20 seconds (you can change the frequency). Each time it updates it places the latest results at the top and if the number of results exceeds 20 it removes the excess results from the bottom.

Search jQuery


You can download the code for the above widget from here.

Let's begin by looking at the HTML:
<div id='twitter_stream'>
     <img src='./images/twitter.png' style='position:absolute;right:5px;top:-2px;' />
     <p id="stream_term">Search jQuery</p>
     <div id='twitter_results'></div>
</div>

This is very simple and consists of the widget div, the Twitter bird icon, the heading and then the results pane.

The javascript is essentially one main function, load_tweets():
function load_tweets() {
     var last_ID = $(".tweet_result:first").attr("ID");
     
     if (last_ID) {
          last_ID = last_ID.replace("tweet", "");
          var url = "http://search.twitter.com/search.json?q=jQuery&since_id=" + last_ID + "&callback=?";
     } else {
          var url = "http://search.twitter.com/search.json?q=jQuery&rpp=5&callback=?"; 
     }
     
     $.getJSON(url, function(json) { 
          var results = '';
          $(json.results).each(function() {
               if (this.id == undefined) return;
               results += "<p class='tweet_result' id='tweet" + this.id + "'><a href='http://twitter.com/" + this.from_user + "' class='tweet_user'><img width='16' height='16' alt='" + this.from_user + " on Twitter' src='" + this.profile_image_url + "' /></a>" + linkify(this.text) + "</p>";
          });
          if (last_ID) {
               $("#twitter_results").prepend(results);
          } else {
               $("#twitter_results").append(results);
          }
     });

     $(".tweet_result:gt(20)").remove();
}

load_tweets() checks to see if there are already any tweets loaded by retrieving the ID of the first tweet (this will be the ID of the most recent tweet form the results).

If there were already tweets then it sets the URL to retrieve all the results since that ID by passing in the &since_id= variable.

If there wasn't any tweets loaded then it just grabs the 5 most recent results.

The results are then loaded in via JSON using the jQuery $.getJSON call. It loops over the results and adds them to the results string and then prepends the results string to the #twitter_results div.

The next bit of magic is the linkify function. This function builds us some nice links depending on the type of link provided:

function linkify(text) {
    // modified from TwitterGitter by David Walsh (davidwalsh.name)
    // courtesy of Jeremy Parrish (rrish.org)
    return text.replace(/(https?://[w-:;?&=+.%#/]+)/gi, '<a href="$1">$1</a>')
               .replace(/(^|W)@(w+)/g, '$1<a href="http://twitter.com/$2">@$2</a>')
               .replace(/(^|W)#(w+)/g, '$1#<a href="http://search.twitter.com/search?q=%23$2">$2</a>');
}

The rest is just a little bit of CSS styling and the final touch is in the document ready function to get our results to stream we use the setInterval function:
$("document").ready(function() {
     load_tweets();
     setInterval(load_tweets, 20000);
});

Hope you like it!
more...

jQuery multi-tiered drop-up menu

Tuesday, 17 November 2009

View Comments

This post provides a working example of the drop-up version of the jQuery Superfish drop-down menu plugin. A cross browser multi-tiered menu plugin that has many great features; it looks great and works great and was designed by fellow Aussie Joel Birch.

Drop-up functionality works similar to drop-down functionality except that it works from the bottom up and menu options are placed at the bottom of the screen or element rather than at the top of the page or element.



You can view an example of the drop-up functionality here.

You can download the working code here.

You can visit the original Superfish site. Where you can view working examples and documentation of the original and also download the original code.

Superfish easily takes an multi-tiered unordered list and using CSS and jQuery generates a nice looking menu that can easily be styled to your liking.

The solution was discovered when I was looking for similar functionality for myself. I haven't found a similar solution yet that looks as good and works across all the major browsers.

Enjoy!
more...

jQuery CSS stylesheet switcher

Monday, 16 November 2009

View Comments

This tutorial shows you how to create a CSS style sheet switcher using jQuery for your site. This style sheet switcher has conventient forward and back links for you to cycle through available sheets and incorporates the cookie plugin so that user preferences can be retained for later visits.

The switcher widget is built by looking at each stylesheet that has the .switch class and uses the link tag's CSS color value to decide the color of the swatch in the widget. Clicking on a particular swatch will disable other stylesheets with the .switch class and activate the selected style sheet allowing a user to choose their style preference. Users can also use the left and right arrows which will either cycle forwards or backwards through available stylesheets.

To view a demonstration of this in action go here.

To download the files for this tutorial click here.



To begin with we have our html file, it is made up of 3 main parts:
  • Our stylesheets each with a color style, and with the class switch (all other stylesheets will be ignored)
  • Our sheetswitch div that will hold the color swatches for users to select their stylesheet
  • The rest of the html body to be styled

To begin with our stylesheets for us to switch between will look like the following:
<link rel="stylesheet" class="switch" style="color:#23B2D2;" type="text/css" href="./css/styles_blue.css" />
<link rel="stylesheet" class="switch" style="color:#8BE487;" type="text/css" href="./css/styles_green.css" />
<link rel="stylesheet" class="switch" style="color:#F24633;" type="text/css" href="./css/styles_red.css" />

Notice they all have the .switch class and a color style attribute. Our jQuery code will loop over these stylesheets when the page loads. It will get the color style from each link element and use it to build the color swatches that appear in the sheetswitch div.

The sheetswitch div is made up of color swatches; colored squares that users can click on to change the stylesheet. It also has a left and right arrow for users to loop forwards or backwards through available stylesheets. The style for the sheetswich div sits in its own stylesheet styles.css, because this stylesheet does not have the .switch class it will be ignored by the javascript during switching.

Let's look at the javascript:

To begin with we loop over the .switch stylesheets and build up the colors array as follows:
var colors = new Array;

// Disable all .switch stylesheets and build array of colours
$(".switch[rel='stylesheet']").each(function() {
    $(this).attr("disabled", "true");
    colors.push($(this).css("color"));
});

We then loop over the colors array to build the swatches and append them to the #sheetswitch. This is followed by the right looping arrow:

$(colors).each(function(index, el) {
     $("#sheetswitch").append("");
});

$("#sheetswitch").append("");
Next comes the click function for each of the swatches. The swatches are added in the same order as the sheets themselves so we can use the index of the selected element to set the active stylesheet. Once we get the index we disable all stylesheets, enable the selected index and then we store the value in our cookie:
$(".swatch").click(function() {
   $(".swatch").removeClass("swatch_hi");
   $(this).addClass("swatch_hi");
   var index = $(".swatch").index(this);
   $(".switch[rel='stylesheet']").attr("disabled", "true");
   $(".switch[rel='stylesheet']").eq(index).attr("disabled", "");
   $.cookie('mysite_sheetswitch_idx', index, {expires: 7});
});

Our next and previous links that correspond to the forward and back arrows are a little more complex. They find the index of the currently selected sheet. They also get the number of sheets using the size() function. Then either add or deduct 1 fom the index to select either the previous or next stylesheet. The length of sheets is used to determine whether we need to loop (either to the last or first element depending on which way we are going):
var selected = $(".switch[rel='stylesheet']").filter(function () { return 
$(this).attr("disabled") == false; });
var current_idx = $(".switch[rel='stylesheet']").index($(selected));
var length = $(".switch[rel='stylesheet']").size();

if (current_idx >= 0) {
 var next = current_idx + 1;
 if (next > (length - 1)) next = 0;
 
 $(".switch[rel='stylesheet']").attr("disabled", "true");
 $(".switch[rel='stylesheet']").eq(next).attr("disabled", "");
 
 $(".swatch").removeClass("swatch_hi");
 $(".swatch").eq(next).addClass("swatch_hi");
 
 $.cookie('mysite_sheetswitch_idx', next, {expires: 7});
}

The last step is for when the page loads, we check to see if a cookie is already set. If so we take the index value and set the stylesheet accordingly.

if ($.cookie('mysite_sheetswitch_idx')) {
   var idx = $.cookie('mysite_sheetswitch_idx');
   $(".switch[rel='stylesheet']").eq(idx).attr("disabled", "");
   $(".swatch").eq(idx).addClass("swatch_hi");
}

Currently no stylesheet is set by default, this could be implemented easily by yourself. It would also be quite easy to add a random, or disable all links.
more...

Would enterprise adopt Google Docs?

Saturday, 14 November 2009

View Comments

Google have announced in a recent interview with ZDNet Asia that within a years time that Google Docs should have enough functionality that most enterprises could "get rid of [Microsoft] Office if they chose to".

As I am currently working for a large company this is a bold statement to be making. From this side of the fence there are few things that would need to be considered before a switch like that could be made:
  • Is the feature set comparable?
  • Is the security comparable?
  • Is the application compatible with my equipment?

Is the feature set comparable? In the article it goes on to mention that MS Office is "an overkill tool for most people" and that "Microsoft's offering will become a specialized offering for office workers who need its additional functions". I would argue from this respect, if Google are still offering a cut-down version of Word that Word will still continue to stay on top in the enterprise. I also don't see the difference between what Google are referring to as "most enterprises" and "office workers"?

Is the security comparable? Most large enterprises have highly secured documents that need to be edited on a daily basis; why would an enterprise support two applications internally? Big business requires that highly sensitive files are encrypted before being sent over the internet. I'm not sure what Google offers on this front, but from business perspective the trust is not there.

Is the application compatible with my equipment? For a business to adopt Google Docs they would need to be sure that Docs was compatible with their other applications (printers, faxing software, cut and paste to other applications) etc. This may be the smallest hurdle but is something to be considered.

Looking at it form here, for a multinational to start using Google Docs they would decide this on a company wide basis. That means the decision to use Google Docs is a decision to support Google Docs worldwide on every machine in every country. I would suggest that for any large company the first three points would need to be satisfied before the cost/benefit could be looked at.

Besides enterprise edition, for people like my parents and for my own personal use, I look forward to a new improved Google Docs with excitement. My parents collect a lot of garbage on their machine and it would be great to use a cut-down operating system in combination with Google Docs. As it stands, it's hard to argue for them to move away from Office as the things that my Mum gets emailed are usually buried in powerpoint slides, excel sheets and word docs. You don't know how much fun it is when your mother calls every time there is a file with an extension that she can't open.

Tech Crunch today also speculated through a "reliable source" that the Google Chrome OS will be launched within a week. Not sure what to expect from this one as it has been previously suggested that the initial support will be targeted towards netbooks.

Disclaimer: Views expressed here about Google Docs are my personal opinion and have nothing to do with my employer.
more...

New jQuery podcasts

Friday, 13 November 2009

View Comments

It's always nice to get surprises and yesterday I got three in the form of three jQuery podcasts. I wasn't even aware that they were coming and yesterday by sheer luck I came across one after the other. The three podcasts I'm referring to are yayQuery, the official jQuery podcast and Remy Sharp's jQuery (click on the iTunes podcast link) podcasts.


I haven't had a chance to listen to Remy Sharp's podcasts yet, but the two that I did hear were a special treat. Remy's has several podcasts already up his sleeve, but the other two only just started this week and are well worth the listen.

yayQuery is an informal discussion with four top class jQuery developers who chat about some of their own ideas and what they see going on around using jQuery. These guys spend a lot of time in the IRC channel and are quite in touch with those people who use jQuery the most. The end of the podcast has a nice surprise, well worth watching.

Picture of the yayQuery podcast


The official jQuery podcast will have a regular guest each week, this week was John Resig who talked further about jQuery in mobile phones, the jQuery project and more about the upcoming 1.4 release. At the end of the show the guys went over some of their favourite jQuery plug-ins and add-ons that were released in the past couple of weeks, so work hard and maybe you can get a mention.

Next week (18th of Nov) they will have Richard D Worth from the jQuery UI development team. If you have a chance you can catch the live stream from here. Alternatively you will need to wait till U.S. Friday morning when they put the edited cast on line. You can subscrive via feedburner here or search on iTunes to subscribe via iTunes.

If you can get the chance it may be preferable to listen live to the jQuery podcast as you can also participate in the online chat. USTREAM allows you to chat and use twitter while listening to the cast, its a good chance to ask a question. At the end of this weeks show John went over some of the questions asked in the chatroom at the end of the podcast so it's worthwhile sticking around.

The best part of both podcasts was getting an insight into some of the parts of jQuery that I haven't used in detail and hearing from the pros some best practices. yayQuery and the official podcast are different enough to be worthwhile to have and listen to both, so take time to check them out and you might prefer one over the other or both.

Enjoy!

more...

jQuery - Submitting a form with an AJAX call to determine return value

A quick tip for submitting a form that has an AJAX call and submitting the form from inside the AJAX success function.

The issue happens when a function is bound to the submit event on a form and you want to validate details of the form via an AJAX call. This will look something like the following:

$("#my_form").submit(function() {

 var some_data = ....whatever...

 var url = 'call_to_some_script_for_validation';
 
 $.get(url, {data: somedata}, function(response) {
  if (response == 'success') {
   return true;
  } else {
   // Display error msg, whatever...
   return false;
  }
 });

 return ???
});

The issue here is in the line return ???. What should you place here? If we place return true here 99% of the time it is not going to wait for your ajax call to complete so it will always return true. If we place false here how are we going to get the AJAX function to return true.

To start with we want our AJAX function to determine whether or not the submit was a success. Because the stuff outside the AJAX call is going to be processed faster you should include anything that could cause the function to return false outside of the AJAX call to appear first and have the AJAX function have the final say. To do this you should have our function return false and let the AJAX function decide whether or not to reverse this, see below:

$("#my_form").submit(function() {

 var some_data = ....whatever...
 
 // Any other processing that may cause us to return false early

 var url = 'call_to_some_script_for_validation';
 
 $.get(url, {data: somedata}, function(response) {
  if (response == 'success') {
   return true;
  } else {
   // Display error msg, whatever...
   return false;
  }
 });

 return false;
});

Now this is not the finished product. The issue with the return true inside the AJAX function is that it only returns true to the AJAX function itself and will not trigger the submit of the form. Your first thought may be to call $("#my_form").submit(); but this will only trigger the submit function action and end in an infinite loop. So how to fix this?

The easiest way to get around this issue once we have success is to unbind the submit function from your form before submitting, as follows:
$("#my_form").unbind("submit");
 $("#my_form").submit();

So in the end you should have something that looks like this:
$("#my_form").submit(function() {

 var some_data = ....whatever...
 
 // Any other processing that may cause us to return false early

 var url = 'call_to_some_script_for_validation';
 
 $.get(url, {data: somedata}, function(response) {
  if (response == 'success') {
   $("#my_form").unbind("submit");
   $("#my_form").submit();  
   return true;
  } else {
   // Display error msg, whatever...
   return false;
  }
 });

 return false;
});
more...

Examples of beautiful information

Thursday, 12 November 2009

View Comments

This post looks at several creative ways of presenting information. Each example, while presenting a large amount of information, combines together to make a work of art. Although it may not seem practical to combine these forms of presentation in your own pages and applications it is a good idea to think of information as more than just a collection of data.


As a software developer you may not spend much time thinking about the presentation of information. Much of the focus of making a site look beautiful is spent on the overall look and feel of a site, whereas a large portion of the information presented on sites and in applications is stuck in tables. Again, if you are a software developer, you may see a large number of forms and database result sets every day and we present most of this information in a flat grid.

The main point that can be taken from these examples is that data can be presented in a way that provides further meaning. The white space, the grouping of related information, the relative size and coloring all take on different meanings and enhance the raw data. By sticking data into a grid you remove a large portion of its context aside from the table headings and we are stuck of coming up with ways of how to highlight a particular row, minimising the context and the meaning down to its smallest possible level.

The speed at which a user can interpret information on a page is important, especially for applications used in daily work life. The longer your staff are spending unnecessary time on page the more money it is costing your business. Tools are a simple primitive thing and if your work application is overly complicated it is not functioning as a tool.

If you develop a site, think about the role the data plays in usability. Or, if you are selling something on a site, if you place your pricing in a grid what does this say about the different values on offer? Are all values the same even though one costs $20 per month and the other $240?

Take some time to look at the examples of well presented information and think about why they work and how they provide extra meaning to the data they are representing. Rather than taking away an idea of how you could present your data in a practical way, think about what techniques have been used to enhance the information provided; white space, the grouping of related information, the relative size and coloring etc.

The first example Gapminder presents comparative statistical information for countries around the world. You can view oil production vs. consumption or life expectancy vs. income per person. These statistics can be compared per person and per country.



The next example is a collection of similar sites. visual complexity provides a range of sites displaying complex visual information. It provides a gallery of information artworks. One of my favourites is the results of the 2009 eurovision song contest.



The last site is the Processing exhibition site. Processing is a programming language designed specifically for visual display. John Resig of jQuery fame also wrote a port of Processing to javascript. The exhibition page showcases many fine examples of displaying visual information using Processing. My favourite being the Oasis project, a display of spatially aware living creatures that swim around under a display of black sand waiting to be discovered in this art piece.

OASIS from yunsil heo on Vimeo.


Consider next time before placing your information in a box, can you enhance the information with a variety of methods. Should your users search the grid for their one row or be guided by a display of well presented enhanced information.
more...

jQuery 2009 Conference Slides - jQuery 1.4 release

Wednesday, 11 November 2009

View Comments

This post covers two presentations that John Resig made at the 2009 jQuery Conference that was held in September. These presentations provide a great insight into the current state of jQuery and what we can expect in the upcoming 1.3.3/1.4 release.

Below I have embedded the two slideshows from the talks John gave. I'm still not certain if there is going to be a 1.3.3 and a 1.4 release. Ealier this year it looked like we were going to get a 1.3.3 release, but it has been mentioned that 1.3.3 is likely to be called 1.4 and there will be no 1.3.3 release. Quote: "We're much closer to shipping 1.3.3 now (which is likely to become 1.4, with all the new features that're being added)".

There are rumours that 1.4 will ship in January, but we'll have to wait and see. I recommend you look at my other post which goes in detail over some of the features that we are likely to see in 1.4. Of note is the dramatic increase in speed with the new Sizzle selector engine and an expected three time faster increase in speed. Also moving towards support for jQuery in mobile applications.

The first talk covers recent changes to jQuery and the upcoming release.



The second talk covers the state of jQuery and more of what is happening around the software (Google groups alternative, growth, moving to Git) etc. It is well worth the read to find out about the effort behind jQuery and the direction of the jQuery project.


For those of you who can't wait, you can get a copy of jQuery 1.4pre from the jQuery nightly build. As this hasn't been officially released/documented yet, I'm not sure what is already implenting, working or stable so you will need to have a look around for yourself.
more...

Selectable font image replacement

Tuesday, 10 November 2009

View Comments

This tutorial provides a technique on how to implement cross-browser selectable font image replacement.

You can download the files for this example here. Note: the files do not contain the fonts. You can follow the links at the bottom of this post to obtain free fonts. You can view a demo of the functionality here.

After so many years of being stuck with a small number of web fonts it's a shame that there isn't a standards compliant solution to the problem. What do we want? An accessible (as in users don't need to have anything installed and turn everything off and the page is still readable), fast, usable (text should be selectable, single, double and triple click should work as expected as well as copy/paste) and minimal (easy set up, limited number of DOM elements) solution to the font issue.

Designers want to use any font available, developers want to be able to implement it without doing extensive hacks and tricks and users want to enjoy the beauty while having the styled text function as standard text.


Currently there are several ways to go about trying to achieve the above, but I would argue that none of them meets the criteria above. They either rely on flash, don't work across all browsers, the text isn't selectable in most solutions or they require a hell of a load of mark up. The main issue is the lack of a standard that is already implemented across all of the major browsers. We want a solution that will work in 90 plus percent of all of the browsers currently in use.

This solution requires a limited amount of mark up, works in all of the current major browsers (IE6/7/8, Chrome, Firefox, Safari 4 Mac and Safari 4 Windows), works in text browsers (tested in WebbIE3) and can be used with any TTF and OTF fonts. This works in an unobtrusive way to allow users to select and copy text and single, double and triple click functionality works in all browsers.

My solution is really a collection of parts of other peoples solutions glued together with jQuery. I would like to take credit for it and give it a fancy name, but I think the solution has been around for a while in parts, but not put together in a clever way. At the end of this tutorial I will give links to some of the sources who deserve credit for their work.

The answer is in three parts: php and GD is used to render the custom font, a little bit of CSS hackiness is used to provide the text selection and jQuery is the glue which holds it all together by determining the text and font to be displayed and doing a little bit of browser checking to see if opacity is supported to get IE to work.

So how is it done? We start with an element, say a h2 and add a pair of span tags inside with some text as follows. Each of these pairs of elements will have a special class for later styling; I have called mine font_substitue and inner.

<h2 class='font_substitute'>
<span class='inner'>This is my fancy text</span></h2>

Furthermore you can provide a second class to the h2 to determine which font to display, e.g. Anidka

<h2 class='andika font_substitute'>
<span class='inner'>This is my fancy text</span></h2>

Using jQuery we loop over each of the $("h2 .andika") classes and then retrieve the text from the span. Using the combination of the selector and the text we use jQuery to insert a background image on the h2 element by setting the src of the image to point to our php script. The php script serves a png image with our rendered font. CSS is used to hide our text and provide a solution to the selection problem. This is done using filters in IE and ::selection rules for Opera, Firefox and Chrome. The last bit and the most tricky is getting the line-height, font size, wrapping and word-spacing to line up. This can take some playing around but a seasoned CSS expert shouldn't have too much difficulty in getting it done.

I'm not going to go in to great deal on how the php font rendering is done, this solution has been around for several years now and the full article on how to do this can be read here. My php script also allows custom tracking (like kerning), word wrapping and custom line height. Also be warned that GD appears to render the images slightly differently (spacing, line height etc.) depending on which version you are using so you will need to do a bit of tweeking. I also took TJ Atkins great color matching function.

This solution provides us with an accessible, fast, usable and minimal solution that works in all current browsers without the need for the user to install anything extra, designers can have their fonts and developers only need to add a minimal amount of code to get it working.

Sources:
Stewart Rosenbergers - Dynamic Text Replacement
TJ Atkins - php Image Replacement
ultraniblet at gmail dot com - tracking
One of many sources for free (as in please read the license first) fonts - sources for fonts
jQuery - jQuery

Fonts for example:
Andika
Marketing Script
Sax Mono
more...

Using jQuery with forms - part 3 (Selectors)

Sunday, 1 November 2009

View Comments

This is the third in a series of tutorials on using jQuery to manipulate forms. In this series I will be looking at parts of the jQuery API reference in detail. In this tutorial I will be looking at forms and jQuery Selectors.



Basics


element

Matches all elements with the given tag name


// Can be used to select the following elements
 
$("select")
$("label")
$("input")
$("form")
$("textarea")
$("fieldset")
$("legend")
$("select")
$("optgroup")
$("option")
$("button")



Basic Filters


:first / :last

Matches the first or last of group of elements


// Selects the first button element in my form
$("#my_form :button:first"); 

// Selects the last password element in my form
$("#my_form :password:last"); 

:not(selector)

Filters out elements from a previous group that match the given selector


// Selects all of the input fields 
// that are not hidden
$(":input:not([type='hidden'])")

:even / :odd

Matches all elements with an even or odd index


// Hide all the buttons with an even index
$("#my_form :button:even").hide(); 

// Apply the odd class to all text input elements 
// with an odd index
$("#my_form :text:odd").addClass("odd"); 

:eq(index)

Select a single element by its index


// Selects the third radio element with the name browser in 
// the form (count starts from 0) and set it to checked
$("#my_form :radio[name='rad']:eq(2)").attr('checked', 'checked');

:lt(index) / :gt(index)

Selects all elements with an index that is either less than or greater than the one specified


// Remove all radio elements with name browser 
// that have an index less than 2
$("#my_form :radio[name='browser']:lt(2)").remove();


Content Filters


:contains(text)

Selects all elements that contain the specified text


// Select all labels that have the word detail
$("#my_form label:contains('detail')");

:has(selector)

Selects parents that contain the select area


// Matches all fieldsets that contain 
// at least one radio element
$("fieldset:has(':radio')")


Forms


The following special selectors are available for forms


// Matches all input, textarea, select and button elements.
$(":input");

// Matches all input elements of type text.
$(":text");

// Matches all input elements of type password.
$(":password");

// Matches all input elements of type radio.
$(":radio");

// Matches all input elements of type checkbox.
$(":checkbox");

// Matches all input elements of type submit.
$(":submit");

// Matches all input elements of type image.
$(":image");

// Matches all input elements of type reset.
$(":reset");

// Matches all button elements and input elements of type button.
$(":button");

// Matches all input elements of type file.
$(":file");


Form Filters


:enabled / :disabled

Selects elements that either have or do not have the disabled attribute set


// Select all the textareas that are disabled and enable them
$("textarea:disabled").enable();


:checked

Matches elements that are checked (typically checkboxes, radio)


// Select the value of the checked radio element with name browser
$(":radio[name='browser']:checked").val();


:selected

Matches option that is selected (typically select element options)


// Get the text value of the select element named cheese
$("select[name='cheese'] :selected").text();


If you found this post useful, here are part 1, part 2, and part 4 of this series.
more...

Using jQuery with forms - part 2 (Core)

This is the second in a series of tutorials on using jQuery to manipulate forms. In this series I will be looking at parts of the jQuery API reference in detail. In this tutorial I will be looking at forms and jQuery Core functionality.


jQuery Object Accessors


each(callback)

Applying a function to each of the elements in the form


// Loop over each of the text elements in the form #my_form
 and set their background color to red if they are empty
 
$("#my_form :text").each(function() {
 if (!$(this).val()) {
  $(this).css('background-color', 'red');
 }
});

size()

Returns the number of matching elements


// Alerts the number of password elements in the form
 
alert($("#my_form :password").size());

eq(position)

Returns the element at the specified position


// Returns the third radio element with the name browser in 
the form (count starts from 0)
 
$("#my_form :radio[name='browser']").eq(2);

index(subject)

Returns the index of a matched element if found (starting from 0) otherwise -1


// Alerts the index of each radio element amongst its siblings

$("#my_form :radio").click(function() {
 alert($(this).parent().children(":radio").index(this));
});


If you found this post useful, here are part 1, part 3, and part 4 of this series.
more...