Archive for the 'web design' Category

3 Value Checkbox with JQuery

Monday, March 24th, 2008

Few days ago I did that whole 3-value checkbox thing basing it on some script I found online. I went back and I re-implemented it using some JQuery magic. I’m not going to reiterate the whole setup here. I recommend that you check the linked post for details. You can find the JQuery-fied code below:

$(document).ready(function() 
{
   checked = '/path/to/checked.jpg';
   unchecked = '/path/to/unchecked.jpg';
   na = '/path/to/na.jpg';
   i = 0;
 
   // replace the checkboxes with images    
   $("form.funky_form > fieldset.funky_set :checkbox.funky_box").hide().each(function() {
 
      img = document.createElement('img');
      img.className = "funky_image";
 
      switch($(this).val())
      {
         case "0":
            img.src = unchecked;
            $(this).attr("checked", "false");
            break;
         case "1":
            img.src = checked;
            $(this).attr("checked", "true");
            break;
         case "2":
            img.src = na;
            $(this).attr("checked", "true");
      }
 
      // these will let us identify which image was clicked
      // and which checkbox does it belong to
      $(this).attr("id", "input" + i + "image" + i);
      $(img).attr("id", "" + i + "image" + i);
 
      i++;
 
      $(this).before(img);
   });
 
   // add onClick functionality to the new images
   $(".funky_image").click(function() {
 
      // select the checkbox corresponding to the clicked img
      t = $("#input" + $(this).attr("id"));
 
      switch(t.val())
      {
         case "0":
            $(this).attr("src", na);
            t.val(2).attr("checked", "true");
            break;
         case "1":
            $(this).attr("src", unchecked);
            t.val(0).removeAttr("checked");
            break;
         case "2":
            $(this).attr("src", checked);
            t.val(1).removeAttr("chekced");
      }             
   });
});

The major difference from the other version is that I’m much more discerning in which checkboxes get converted. The code will select only a box that is of the class funky_box and is inside a fieldset with a class of funky_set and inside a form with a class of funky_form. I’m mostly doing that to show power of JQuery - this is all specified in that very first select statement. I was trying to do something similar in the old code, but I was getting hung up on the silly DOM gotchas. JQuery makes this easy.

Also note the chaining functions. The first line after I declere i both hides all the selected check-boxes and begins the each block. Similarly, check out the second switch statement. In several places I set the value of t and change the checked attribute on the same time. It’s very expressive, and lends itself toward very compact and concise code.

Here is the HTML for the form to go with the code above (note the inclusion of the fieldset tag):

<form name="funky_form" method="POST">
      <fieldset class='funky_set'>
         <label for="c1">
            <input type="checkbox" name="c1" value="1" class="funky_box" checked>
            Some Important Task
         </label><br>
 
         <label for="c2">
            <input type="checkbox" name="c2" value="1" class="funky_box" checked>
            Some Important Task #2
         </label>
      </fieldset>
 
      <label for="c3">
         <input type="checkbox" name="c3" value="1" class="funky_box" checked>
         This is a checkbox with the funky_box class outside the funky_set
      </label><br><br>
 
   <input type="submit" value="Submit">
</form>

How readable is this version? To me it is actually better because it’s smaller, and more compact. To someone who never worked with JQuery it might be a bit confusing at first but I think you get used to the weirdness pretty quickly. I find the selection statements much more elegant than nested loops for example, even if they might The more code I can see on my screen the better. I’m not golfing though - all the methods and most variables have meaningful names (well, except stuff like t and i but you know how it goes).

Which version do you like better?

JQuery Quirks

Friday, March 21st, 2008

Here are two interesting quirks in the way JQuery works. Let’s say you want to check all the check-boxes on the page - how do you do it? It’s trivial:

$(":checkbox").attr("checked", "true");

Now quickly, how do you un-check all the check boxes on the page? If you said:

$(":checkbox").attr("checked", "false");

You are wrong. Despite the fact that if you use a more standard notation and say for example:

document.myform.mycheckbox.checked = false;

it will give you the desired result. The JQuery call however does not work. In fact, it does the exact opposite - it will set all the check-boxes on the page to their checked state. Apparently for the few funky attributes like “checked” and “selected” the second argument of the attr() method call can be any non-empty string. It makes sense because you actually don’t have to give these attributes an explicit value in your HTML code for them to work. So how do you un-check a box in JQuery? There are two ways. First one, and the more sure-fire one is to remove the checked attribute altogether:

$(":checkbox").removeAttr("checked");

Second one which has worked for me for some reason was to set the attribute to the empty string:

$(":checkbox").attr("checked", "");

Same goes for select attributes in combo boxes and list boxes. You need to remove them or set them to an empty string, or they won’t go away.

Quirk number two: axjax weirdness. Look at the code below:

$("a").hover(function() {// do something});
$("#sometrigger").click(function() { 
   $("#container").load("/some/page.html #links"); });

What am I doing here? I’m adding some hover functionality to all the links on the page. Then I add an on-click even to #sometrigger element. When it is clicked I want to load the contents of #links from /some/page.html into #container asynchronously. Where is the quirk? The hover functionality will not apply to the dynamically loaded links. It’s weird, but that’s just how it works. So if you want this functionality on all your links you will need to reapply them:

$("a").hover(function() {// do something});
$("#sometrigger").click(function() { 
   $("#container").load("/some/page.html #links", 
      $("#container > a").hover(function() {// do something});
         });
   );

It’s a little bit silly, but it kinda makes sense. You may not always want to apply the same events and formating to newly loaded html. But if you don’t expect it, it may throw you off. One you know about it however you learn to compensate for it. I’m being told that folks coming from the Prototype community are hitting this little quirk like a brick wall because their framework behaves in the opposite way (ie. the effects and behaviors are applied to the ajax loaded html).

JQuery Fun

Wednesday, March 19th, 2008

I’m doing more and more work with JQuery lately, and as I mentioned several times before I’m totally loving Javascript again. It is really a very concise and terse framework so it doesn’t really take long until you start feeling like you know it inside out. But it is very powerful and learning to use it properly, and efficiently will probably take me a while. Last time when I was raving about it I did it without any neat code samples to show you how cool it is, so this post will have a few.

One nice thing about this framework is that it is all about closures. Most of the JQuery functions take a pointer to a callback function as an argument, and the selectors automatically loop through DOM instances they match forcing you to write very different code. For example, if I wanted to add an onClick event to all links on my page with regular javascript I would probably do something like this:

function addOnlclickToLinks()
{
    myLinks = document.links;
    for(var i=0;i < myLinks.length;i++) 
    {
       myLinks[i].onclick = return onClickFunction();
    }
}
 
function onClickFunction()
{
    // the on click code goes here
}
 
 window.onload = addOnClickToLinks

It’s readable, and understandable code, but it looks messy. JQuery saves you tons of typing and really makes the code more ellegant:

$(document).ready(function() {
    $("a").click(function() {
        // on click code goes here
    });
});

This is a totally new way to code, and it forces you to think in a much different way. After doing this for a while, you come to rely on this new way of coding to the point where it trips you up. For example, the other day I wanted to do something like this:

$("input.someclass").after(function() {
    if($(this).val() == foo)
    {
        // do somethign complex
    }
    else
    {
        // do something else
    }
 
    return something;
}());

What I want to do here is to generate element called something based on the value of the selected input - and the process may actually be quite complex, requiring several lines, or multiple function calls. The sample above doesn’t work because I’m using the $(this) selector in the wrong context. To the best of my knowledge, the way to achieve this sort of thing would be by doing:

$("input.someclass").each(function() {
    if($(this).val() == foo)
    {
        // do something complex
    }
    else
    {
        // do something else
    }
 
    $(this).after(something);
});

The each function actually iterates through all the selected elements, and runs the passed function in the context of each one. At least I think this is how you would do it.

Either way, it is really a much nicer way to program and the way you select DOM elements and interact with them is consistent across platforms. If you still haven’t tried JQuery, I highly recommend checking it out. Javascript is really a great language - it has a somewhat traditional syntax (braces and semicolons, ftw) but all the awesome features of a modern highly dynamic scripting language like Python or Ruby. The only thing that is holding it down these days is the inconsistency in which it is interpreted across browsers. JQuery fixes that issue, and draws on some of the cool dynamic features of the language to really make your life easier.

3 Value Checkbox

Wednesday, March 5th, 2008

Recent conversation with the PHB regarding internal web application project:

PHB: This is all good, but these 4 items here need to have a “not available” option
Me: Ok, no problem. I’ll change it into a yes, no, n/a radio buttons instead of check boxes
PHB: Oh, no - I like the check boxes. It’s just that sometimes I want to be able to say N/A there
Me: So… You want like two check boxes?
PHB: No, just one. But with N/A option.
Me: But… Check boxes just don’t work like that - they are on/off kind of thing
PHB: Sure they do. I saw it on several websites.
Me: Oh, ok… Can you send me a link? I’d like to see how they did it.
PHB: Oh, hmm… It was a while ago. If I remember where I saw it, I’ll shoot you an email.

So… Any of you know how a 3-state checkbox looks like? Cause it beats me but powers that be seem to be dead set on having one. I could try to argue in favor of radio boxes or something more sane, but it seemed like a waste of time. So instead I decided to make it work. Just how?

I figured it would be like a toggle switch that oscillates between 3 states. Regular HTML check boxes can only be checked or un-checked so I gave up on them initially. I figured I could use a button as a toggle. It would have an attached onClick script which would change the button’s appearance and then modify a value of some hidden input field. Then I realized it was stupid. Not only was it too complex, and hackish but also completely failed if Javascript was disabled.

I really needed the form to at least half-work without Javascript - for example just default to standard on/off check-box forgetting about n/a option. And for that I needed to stick with the HTML check boxes. I would have to style them or change them up somehow. So I searched for some code that would let me do it with minimum effort and minimum dependencies.

BrainError’s script seemed like a good fit. He wrote some code that allowed me to replace check boxes with images on the fly which is really what I wanted here. Naturally I had to modify it to fit my needs. You can see the changes below:

var inputs;
var checked = 'checked.png';
var unchecked = 'unchecked.png';
var na = 'n-a.png';
 
function replaceChecks()
{
   //get all the input fields on the funky_set inside of the funky_form
   inputs = document.funky_form.getElementsByTagName('input');
 
   //cycle trough the input fields
   for(var i=0; i < inputs.length; i++)
   {
      //check if the input is a funky_box
      if(inputs[i].className == 'funky_box')
      {
         //create a new image
         var img = document.createElement('img');
 
         //check if the checkbox is checked
         if(inputs[i].value == 0 )
         {
            img.src = unchecked;
            inputs[i].checked = false;
         }
         else if(inputs[i].value = 1 )
         {
            img.src = checked;
            inputs[i].checked = true;
         }
         else if(inputs[i].value = 2 )
         {
            img.src = na;
            inputs[i].checked = true;
         }
 
         //set image ID and onclick action
         img.id = 'checkImage'+i;
 
         //set image
         img.onclick = new Function('checkChange('+i+')');
         //place image in front of the checkbox
         inputs[i].parentNode.insertBefore(img, inputs[i]);
 
         //hide the checkbox
         inputs[i].style.display='none';
      }
}
}
 
//change the checkbox status and the replacement image
function checkChange(i) 
{
   if(inputs[i].value==0)
   {
      inputs[i].checked = true;
      inputs[i].value = 2;
      document.getElementById('checkImage'+i).src=na;
   }
   else if(inputs[i].value==1)
   {
      inputs[i].checked = false;
      inputs[i].value = 0;
      document.getElementById('checkImage'+i).src=unchecked;
   }
   else if(inputs[i].value==2)
   {
      inputs[i].checked = true;
      inputs[i].value = 1;
      document.getElementById('checkImage'+i).src=checked;
   }
}
 
window.onload = replaceChecks;

This is the script. To get it working all I need is to create a form with a name “funky_form”. The original script looped through all the input fields on the page which seemed like an overkill in my case. I’m also applying this formating to the check-boxes with the “funky_box” style. My form may have 60-80 input fields, many of which are check-boxes, but only 4 or 5 of them in a very specific section need to have this behavior. I was fine with using regular check-boxes for the rest of them.

It’s also important to add that the getAttribute method does not work in IE when you try to grab the class of your element. BrainError was using it to select on type, which was working well. I’m much more selective, and it took me a while before I realized that IE borked this method. Fortunately you can use the className property instead - and it works equally well in FF as it does in IE.

The form would look something like this:

<form name="funky_form" method="POST">
      <label for="c1">
         <input type="checkbox" name="c1" value="1" class="funky_box" checked>
         Some Important Task
      </label><br>
 
      <label for="c2">
         <input type="checkbox" name="c2" value="1" class="funky_box" checked>
         Some Important Task #2
      </label><br>
 
      <label for="c3">
         <input type="checkbox" name="c3" value="1" class="funky_box" checked>
         This is a checkbox with the funky_box class outside the funky_set
      </label><br><br>
 
   <input type="submit" value="Submit">
</form>

If you want to see it in action, check out the working demo.

All my boxes all have distinct names which intended. This way I can individually access via the $_POST array from PHP when the form submits and extract their value as needed. You may also have noticed they all start checked. This is actually a requirement in the spec, but also a fall-back measure.

If you disable Javascript you will see regular check-boxes. They start with value 1, and if they are not checked (and thus not present in $_POST) they will default to 0. This way you can submit the form normally - you just won’t be able to set them to n/a.

There is a small downside - when the page loads, you can see the regular check boxes for a brief second or two. But that’s just the way this script works. Suggestions and corrections are welcome. I tested this in Firefox 2.x and IE 6.x+. Let me know if it fails miserably in your browser of choice. )

Client Side Table Sorting with JQuery

Monday, February 18th, 2008

For a while now, I have been searching for a good Javascript framework to mess around with. I think I finally found one that I really like. JQuery is a 15 Kb bundle of pure fucking magic. It is tiny compared to some of the other frameworks out there, and at the same time very powerful. What can it do? Tons of stuff. First thing you notice is of course eye candy things like sliding and hiding divs, easily manipulating tons of data on the page and etc.

The best thing about JQuery however are the plugins created by the very active community. For example, there is a small script out there that lets you sort HTML tables on the client side. Normally when I dumped data out of the database on the screen I would implement sorting by simply making another database query with a modified WHERE clause and then re-draw the whole table with the new dataset. But why should I use such a database intensive method when JQuery makes it possible to quickly sort a large table without even reloading the page using client side resources?

Go check out the demo on the tablesorter plugin website. It is really a great piece of work. Christian Bach deserves major props for putting this together. To tell you the truth, I never really considered sorting of tables in javascript mainly because the amount of effort it would take on my part. Implementing the sorting algorithm and the transformations would just be so much more time consuming than simply writing a quick SQL query. It somehow never seemed worth the effort to me, until now.

The way I see it, this method of sorting has two major benefits over the old fashioned server side method. First, as I mentioned earlier it takes some load of the database. Second, the sorting will seem much faster to the user because we are not reloading the page. It should even be faster than an asynchronous call as these things tend to have a visible delay. Tablesorter manipulates DOM objects which are already loaded in clients memory - there is no loading or waiting required.

It even comes with a neat pager functionality which could come in handy. Then again, I’m not absolutely convinced that paging should be done on client side for very large tables but it is a nice feature to have just in case you need it.

Just to illustrate how easy it is to apply it to an existing table here is what you need to do. First download both JQuery and Tablesorter and dump them somewhere. Then simply add following calls in the header:

<script type="text/javascript" src="/path/to/jquery.js"></script> 
<script type="text/javascript" src="/path/to/tablesorter/jquery.tablesorter.js"></script>
<script type="text/javascript">	$(document).ready(function() { $("#myTable").tablesorter(); } );</script>
<link rel="stylesheet" href="/path/to/tablesorter/themes/blue/style.css" type="text/css" media="screen, print" >

Then simply make your table id to be “myTable” and it’s class to be “tablesorter”. The first one is mandatory for the sorting logic to work. The “tablesorter” class is defined in the css stylesheet you link to and it will create nice headers and table layout for you:

<table id="myTable" class="tablesorter">
	<thead>
		<tr>
			<th>Column 1</th>
			<th>Column 2</th>
			<th>Column 3</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>1</td>
			<td>a</td>
			<td>foo</td>
		</tr>
		<tr>
			<td>2</td>
			<td>b</td>
			<td>bar</td>
		</tr>
 
	</tbody>
</table>

That’s it. You are done. Your table is completely sortable now, by every column, and is even nicely formated. It couldn’t be easier than this. ) And yes, you do have to use the thead and tbody tags.

There is one caveat though. When you download current release, be aware that the jquery.tablesorter.pack.js file is broken for some reason. It will work just fine in Firefox, and Opera but in IE6 it will throw some sort of cryptic bug. It might be some artifact introduced in the packing process or something like that. If you use jquery.tablesorter.min.js which is only marginally larger in size, this issue goes away. Go figure. It’s still a great tool despite this little glitch.

I think I’m sold on JQuery. It is tiny, and yet extremely powerful toolkit, and I highly recommend checking it out.