Sag Rising

Notes to Myself

Using jQuery To Style Labels

Posted by Richard Cockrum on August 8, 2008

One of the plugins for Habari that I use on a regular basis is the markup plugin, which allows you to add Html tags to your content as your write through the use of a toolbar rather than having to add the markup by hand.

The markup plugin has always had one minor irritant, though. Habari's publish page is well labeled. The title area has a label. The content area has a label. The tags area has a label. For each of these, when the field doesn't have the focus the label is in the field. Move the focus to the field, or write something in the field, and the label moves above the field. It's a nice indicator of where the focus is, and where the content is.

Unfortunately, the markup plugin doesn't take this label into account. When you go to your blank publish page to write a post, the content label appears under the toolbar - not an especially helpful place for it to be.

I'm not a javascript programmer. I barely know PHP. :) But I wanted to fix this minor irritant, so I got to start learning. Habari uses jQuery as it's javascript workhorse. One of the tasks for which it is used is moving the labels around on the publish page.

A unique thing about jQuery is that almost everything begins in it's $(document).ready() function.
I dug through Habari's javascript code and found this snippet that controlled the movement of the labels:

// Move labels into elements. Use with usability-driven care.
$('label.incontent').each(function(){

	var ctl = '#' + $(this).attr('for');
	if($(ctl).val() == '') {
		$(ctl).addClass('islabeled');
		$(this).addClass('overcontent');
	} else {
		$(ctl).addClass('islabeled'); 
		$(this).addClass('abovecontent'); 
	}
	
	$(this).click(function() {
		$(ctl).focus();
	})
});

$('.islabeled').focus(function(){
	$('label[for='+$(this).attr('id')+']')
	 	 .removeClass('overcontent')
	 	 .addClass('abovecontent').show(); 
}).blur(function(){
	if ($(this).val() == '') {
		$('label[for='+$(this).attr('id')+']')
	 	 .addClass('overcontent')
	 	 .removeClass('abovecontent'); 
	} else {
		$('label[for='+$(this).attr('id')+']')
	 	 .removeClass('overcontent')
	 	 .addClass('abovecontent');
	}
});

This code totally bewildered me, so I went off to jQuery's docs page, where I found two things that clarified it.

  • $() creates a jQuery object. Almost anything can become a jQuery object. A css class. An html item with an id. An html item with an attribute.A set of html items. These are called selectors and jQuery has a series of functions to manipulate and choose between them.
  • A large number of jQuery's functions return a jQuery object, so you can chain together several functions.

So what this code is doing is

  1. Looping through all the labels with a class of incontent. For each one, find the text field it labels. Add the islabeled class to the control. If the control contains text, add the abovecontent class to the label. if not, add the overcontent class to the label. Looking in the css file shows that the overcontent class moves the label into the text field, while the abovecontent class moves the label above. the text field.
  2. When the focus changes on the page, toggle the label classes between overcontent and abovecontent as necessary, depending on whether the text field is receiving or losing the focus and whether it contains text.

Since it uses jQuery, the markup plugin already has a $(document).ready() function. My first thought was to use the jQuery css() function to change the margins in the content fields label, so it was actually in the content field and not under the toolbar.

$('label[for=content].overcontent').css('margin-top', '30);
$('label[for=content].overcontent').css( 'margin-left', '5px;');

This failed miserably. The label appeared where it was supposed to when the text box was empty, but the css change also affected the label when it had the abovecontent class and when text was in the content field, so the label rarely appeared where it belonged.

So I asked myself what my actual goal was. Answer: to move the label. Now, it is accepted practice to use css to position elements on a web page. Normally, this css is in a separate style sheet. However, html elements can have a style attribute, which will override whatever is in the page's style sheets. Using the style attribute tends to be frowned upon, but it is possible, syntactically correct, and jQuery has a function perfect for this purpose - the attr() function, which you can use to add any attribute to an element. What I ended up with was this:

$('label[for=content].overcontent')
 	 .attr('style', 'margin-top:30px;margin-left:5px;');
$('#content').focus(function(){
	$('label[for=content]').removeAttr('style'); 
}).blur(function(){
	if ($('#content').val() == '') {
		$('label[for=content]')
 	 	 	 .attr('style', 
	 	 	     'margin-top:30px;margin-left:5px;'); 
	} else {
		$('label[for=content]')
 	 	 	 .removeAttr('style');
	}
});

When the page loads, the content field's label has a style attribute added to it that moves the label into the proper area of the content field. Whenever the content field gets the focus, the style attribute is removed from the label, so it can move where it is supposed to go. When the content field loses the focus, if it doesn't contain text, the style attribute is added back to the label so it can move back down into the field. If it doesn't contain text, the style is removed from the label, in case it was on it.

This isn't a perfect piece of code. It still doesn't feel right using the style attribute. But it serves the purpose of removing a minor irritant from the use of the plugin. More important, it shows that even a tyro can learn to use jQuery for practical purposes.

This entry is filed under and . You can follow any responses to this entry through the feed . New comments are currently closed.

What do you think?
Comments for this post are disabled.