Yelotofu

Yelotofu ~ “In building standards compliant sites we are creating a better Web for the future.”

jQuery: Textarea maxlength

Being able to restrict the maximum length of user input from the interface directly is very convenient and practical in use. We do this a lot with input elements. Unfortunately textarea elements do not natively support the maxlength attribute. This attribute was finally added in HTML5 but at the time of writing Chrome is the only browser supporting it.

Naturally many have used JavaScript to rectify this problem and I'd be doing the same here. The difference is we won't be putting anything special in the markup but will simply use the maxlength attribute within our textarea as if it's native — like so:

HTML

 
<textarea cols="30" rows="5" maxlength="10"></textarea>
 

This is looking good for HTML5 compatibility. And here's the magic:

jQuery

jQuery(function($) {
 
  // ignore these keys
  var ignore = [8,9,13,33,34,35,36,37,38,39,40,46];
 
  // use keypress instead of keydown as that's the only
  // place keystrokes could be canceled in Opera
  var eventName = 'keypress';
 
  // handle textareas with maxlength attribute
  $('textarea[maxlength]')
 
    // this is where the magic happens
    .live(eventName, function(event) {
      var self = $(this),
          maxlength = self.attr('maxlength'),
          code = $.data(this, 'keycode');
 
      // check if maxlength has a value.
      // The value must be greater than 0
      if (maxlength && maxlength > 0) {
 
        // continue with this keystroke if maxlength
        // not reached or one of the ignored keys were pressed.
        return ( self.val().length < maxlength
                 || $.inArray(code, ignore) !== -1 );
 
      }
    })
 
    // store keyCode from keydown event for later use
    .live('keydown', function(event) {
      $.data(this, 'keycode', event.keyCode || event.which);
    });
 
});

See live example.

This code could probably be enhanced further by triggering custom events when the maximum length is reached or include a character counter of some sort within the keypress handler. Your imagination is the limit!

Tip: getting values from an options list

So, if you've ever converted an options list into a single array of values you might have intuitively done this:

var select = $('#mySelectElement')[0];
var values = new Array();
for (var i=0; i < select.length; i++) {
  values.push(select.options[i].value);
}

Pretty boring aye?

There is a snazzier (is that a word?) alternative, which is to use jQuery.map:

var select = $('#mySelectElement')[0];
var values = $.map(select.options, function(n) {
    return n.value;
});

OK, only one line saving but doesn't that feel good?!?

Labs: UI inlineEdit

After my last tutorial on building a simple inlineEdit plugin I converted the concepts into a UI widget. It's now sitting in labs for you to use, pick apart and devour:

http://jquery-ui.googlecode.com/svn/branches/labs/inlineedit/demo.html

If you inspect the source code you will notice the structure is quite different. It's actually pretty much the same functionality wise but I have separated each piece into smaller chunks. This makes it easier to extend in future (I plan to write more on ways to extend UI widgets in another post).

Here's a quick peek at the features:

  • Basic inline editing - click, edit, save
  • Ability to save on pressing ENTER key
  • Cancel by un-focussing the input or clicking cancel
  • ThemeRoller ready
  • Customisable empty placeholder
  • Save and Cancel callbacks
  • Cancelable Save callback (so you could script a cancel action)

As usual please feedback and help improve this widget either by commenting here or on the InlineEdit design & planning page

Enjoy!

jQuery Inline Edit tutorial

A friend recently asked me to review his edit-in-place code which turned out to be a modification of the one found at http://docs.jquery.com/Tutorials:Edit_in_Place_with_Ajax. Reading the tutorial on that page I asked myself how I would do this differently? Defining a global setClickable() function and then calling $('#editInPlace").click() is totally uncool, essentially limiting yourself to one edit-in-place area per page.

Since the concept of edit-in-place is so simple and the implementation should be likewise I want to try and tackle this as a tutorial by going through concepts and teaching to build from scratch.

The concept

There's a piece of text on a page, e.g. a heading or paragraph, looking plain and un-interesting. When you hover over it however a visual highlight, usually pale yellow, indicates that it's something special - an editable region. You then click on the text and it magically transforms into an editable box with save and cancel buttons tagged at the end. On clicking either save or cancel the editable box transforms back into its original form with text updated if saved.

The build

What we want to do is put these concepts into code whilst building something that's re-usable. A plugin will do the trick! Let's call our plugin inlineEdit.

We are only 3 steps away from achieving our goal! Follow along...

Step 1: Getting the basics right

The basic functionality consists of a static text element (span, div etc...) that can transforms into an input element on click. And on hover the text element will highlight to indicate special interactions.

Here's our first pass: Basic Interaction

Code explanation: Our plugin accepts a CSS class name defined in a stylesheet which is used to toggle the text element's classname - a class defining a background-color typically #ffC is sufficient for this. On clicking we replace the entire contents of the original text element with an input element and pass the original content into the input as its value. For now changes are saved when the input loses focus.

Step 2: Save, Cancel & Callbacks

As you may notice above we saved on blur and there is no way of canceling changes. Let's improve the interaction by adding a save button. This is important as you'd want to do something with the changes or allow a user to manually cancel. Every application does something different at the saving stage so we'll simply define a save callback and let the implementer decide what to do on save, be it Ajax post or what not. The save action is also cancelable by returning false inside the callback.

Here is our second pass: Save, cancel and callbacks

Step 3: Finishing Off

Our plugin is nearly there. Just a few nice finishing touches remaining. We want to give the user ability to change the button label and pass an initial value. Also to make the plugin more re-usable we want to move the core plugin code out to a separate file and reference it instead. To set the stage for further improvements in the future we also split the code into two distinct clauses.

Final result: Live Demo and source code is here

Usage

Now to use this brand spanking new plugin we simply apply it to any DOM element like so:

HTML

<span class="editable">Hello World!</span>

JavaScript - Basic

$(function() {
  $('.editable').inlineEdit();
});

JavaScript - Customised button label with save callback

$(function() {
  $('.editable').inlineEdit({
    buttonText: 'Add',
    save: function(e, data) {
      return confirm('Change name to '+ data.value +'?');
    }
  });
});

Feedback welcome - if you happen to improve upon this code base please share-a-like!

Enjoy!

Update 26-Aug-09: Added placeholder for cases when text is empty; changed label option to buttonText to minimise confusion.

Update 18-Oct-09 Fixed bug in setting initial value via script.

Update 17-Jan-10 Moved code to http://github.com/caphun/jquery.inlineedit. I will continue to improve this plugin from there. Feel free to fork it!

jQuery UI Spinner update

Sorry for no news these past few months. It's been very hectic since the new family addition back in Dec which I managed to keep low key. ;) I feel so guilty now for neglecting this blog and my readers (don't think I have any regulars anyway so I guess nothing missed!), but hey priorities are priorities!

Right, the reason for this post is that there has been some recent exciting updates to the spinner which I would love to get feedback on from the public. Thanks to Brant Burnett, the Spinner has been re-invigorated and looks set to become a kick ass component in the making!

We've added a bunch of new features and updates including:

  • support for jquery 1.3.2
  • support for ThemeRoller
  • spinning hexidecimals and other numbering systems
  • ability to auto-hide buttons and show on hover with customizable speeds
  • better keyboard interaction - you could now use PgUp/PgDn to setup up/down in a large increment set by options

We also have a lot more in the works, such as:

  • ability to spin time and date
  • segment spinning based on formatter
  • HTML5 support
  • and more...

If interested, check out the latest demo

We're still in the planning / development stage so any features you see now or currently being planned may not necessarily land in the final release. It all depends on what you want - collectively that is! So if there is something you really think should be added please shout in the dev mailing list or on the UI planning wiki.

Looking forward to some feedback.

Thanks!

jQuery UI 1.7 released

Today marks the release of jQuery UI 1.6 1.7. Kudos to UI team members who've done a fantastic job!

I'm pretty excited not only because I'm now part of this fantastic team but also with where UI is headed and how it has progressed thus far. UI is still pretty young compared to other UI libraries so there is much to learn from the likes of Dojo, ExtJS and YUI - all great frameworks in their own right.

1.7 see's a total refactor of the UI CSS Framework and ThemeRoller v2. This must be by far the greatest visible improvement in this version of UI. Though it is but one part of the hard work put into this release.

For full details on this release see the official release announcement.

jQuery: how to tell if you’re scroll to bottom?

This is a question a jQuery user asked recently. It's a simple question without a simple answer. There is no inverse equivalent to scrollTop=0 (i.e. scrollBottom=0) so the solution requires some thought.

The scenario is this — we have a fixed height div with overflow:auto and we want to use JavaScript to programmatically scroll to bottom and also to execute some code if we're at the bottom. Our div looks like this (using inline styles for the sake of this example):

 
<div id="box" style="height:300px; overflow:auto;">
  <!-- Your content goes here -->
</div>
 

To scroll to bottom programmatically we do:

 
$('#box').animate({scrollTop: $('#box')[0].scrollHeight});
 

And to check whether we're scrolled to bottom:

var elem = $('#box');
if (elem[0].scrollHeight - elem.scrollTop() == elem.outerHeight()) {
  // We're at the bottom.
}

That's pretty easy, right? As noticed scrollHeight is the key to this solution.

A bit of info about scrollHeight: First introduced by MSIE and supported since version 4, [1] scrollHeight gets the height of an element's content including padding but not margins. Though scrollHeight is currently supported by all major browsers it is not part of a W3C recommendation so still considered proprietary. [2]

Now, some of you reading this might not like scrollHeight because it's not a W3C recommendation. So for the interest of this group let's explore an alternative solution.

Your first stab at the alternative to check whether we're scrolled to bottom might look something like this:

 
if ($('#box').scrollTop() == $('#box').outerHeight()) {
  // We're at the bottom.
}
 

Novel, but that doesn't work! scrollTop is quite an arbitrary value. In our case depending on which browser you choose the answer could be anything from 244px (Safari) to 289px (IE7). Clearly this is not a viable solution.

To determine whether we're truely at rock bottom we have to get the height of the element's content and do a calculation based on that. Now we're immediately faced with another problem — there is no sane way of determining the actual height of an element's content because .outerHeight() always gives 300px plus padding as the answer. We could use an inner div to get over this problem:

 
<div id="box" style="height:300px; overflow:auto;">
<div class="inner">
    <!-- Your content goes here -->
  </div>
</div>
 

So now we have a way of determining content height we can scroll to bottom:

$('#box').animate({scrollTop: $('#box > .inner').outerHeight()});

And to check whether we're scrolled to bottom we use the offset method:

var elem = $('#box');
var inner = $('#box > .inner');
if ( Math.abs(inner.offset().top) + elem.height() + elem.offset().top >= inner.outerHeight() ) {
  // We're at the bottom!
}

Not as striaght forward as scrollHeight aye?

Here's a test page encompassing the stuff talked about in this article.

References:
[1] scrollHeight Property
[2] element.scrollHeight

Microsoft to adopt jQuery

Some exciting news just came out — Microsoft plans to adopt jQuery making it part of their official development platform. So it will be shipped with Visual Studio and have all that intellisense goodness. I'm pretty excited about this news. Though I no longer use Microsoft development products it is a testiment to the world of jQuery's continual growth and rise as one of the best JavaScript libraries in existance.

Read the official annoucements:

jQuery.com website redesign

The long overdue redesign of jQuery.com has finally landed! I like the new design as it's more intuitive and inline with jQuery UI's design. This redesign will certainly help pitch jQuery to new comers; for many the decision to go with another framework has purely been based on the ugliness of the old jQuery website, that should hopefully change now.

Old:
old jQuery.com design

New:
new jQuery.com design

However, I still see much room for improvement. The website clearly lacks that magical touch you could easily achieve with a sprinkle of FX and UI. The fade-in boxes on the homepage don't work for me and display erratically when you mouse over them quickly. Hopefully the jQuery team will change this soon.

That said, hats off to the team as they have done an excellent job, as always, in giving us this redesign and of course — jQuery. Thanks!

jQuery: Shuffle Plugin

I recently came across a really compact array shuffling function on JSFromHell.com, which I thought would make a nifty jQuery plugin.

This jQuery shuffle plugin could be applied to any HTML element or Array object.

 
(function($){
  $.fn.shuffle = function() {
    return this.each(function(){
      var items = $(this).children();
      return (items.length)
        ? $(this).html($.shuffle(items))
        : this;
    });
  }
 
  $.shuffle = function(arr) {
    for(
      var j, x, i = arr.length; i;
      j = parseInt(Math.random() * i),
      x = arr[--i], arr[i] = arr[j], arr[j] = x
    );
    return arr;
  }
})(jQuery);
 

Due to line wrapping issues in this post I inserted a few hard carriage returns in the above snippet (sorry).

Example 1: shuffle an unordered list

 
$('ul').shuffle();
 

Example 2: shuffle an array

 
var arr = [1,2,3,4,5,6];
arr = $.shuffle(arr);
 

There is a demo here. And for those interested I encourage you to download the plugin for closer inspection.

« Previous Entries