Getting the page height with jQuery

I was recently adding an overlay (or modal) feature to one of my sites. This overlay feature required a blackout mask (when your webpage gets covered with an opaque black layer) to be shown over the content. Writing the CSS for this was a breeze, and writing the jQuery (or Javascript) for this was even easier. However, I did run into one little snag.

In current browsers I could do a position fixed on the blackout and define width and height at 100%. While in outdated browsers (Internet Explorer), position fixed does not work properly, so I had to resort to Javascript and position absolute. This was easy as I would grab the height of the page and apply it to the blackout CSS. During my testing I noticed that the blackout would only fill the viewport of the page, and the second you started scrolling you would see where the blackout cut off.

After some browsing of the jQuery API I discovered my problem. It seems that the window and document objects return different heights in jQuery. Window returns the height of the viewport (everything within view) while document returns the height of the HTML page (the whole page, including hidden content that requires scrolling). Furthermore, window and document can not use outerHeight() nor innerHeight(), they must simply use height().

$(window).height(); // returns height of browser viewport
$(document).height(); // returns height of HTML document

Here is how I remember which is which: document returns the height of the DOM (document object model) and window returns the height of the viewport (viewing out a window). I know many of you may of already known this, but it slipped past me and was quite a genius implementation of behalf of the jQuery team.

Book Review: jQuery Enlightenment

Before I dive on topic, I want to give you all a heads up that I will be posting a review of the book: "AJAX and PHP: Building Modern Web Applications 2nd Edition", by Packt Publishing. So far the book is great and has some nice chapters about jQuery, JSON, PHP and even XAMPP.

Now jQuery Enlightenment (jQE) isn't much of a book as it is a helpful PDF. It's not as long as your typical book, but it gets the job done in explaining jQuery and its core components. The topics that are covered in jQE range from Selectors, Traversal, DOM and Manipulation, Forms, Events, Plugins, AJAX, the Core and much more. The current edition of the book covers jQuery 1.3.2, and since 1.4 recently came out, I'm going to note any differences or problems that can be solved using 1.4 (if there are any).

The document is only a mere 123 pages (PDF pages to be precise), which may sound like a lot, but in actuality it isn't. Each chapter of jQE comes with code snippets, which have links to JsBin.com that allow you to run and test the snippet yourself. The snippets are also color coded to represent which areas of the code you should be focusing on and understanding. These features were probably the greatest thing about jQE and what sets it apart from regular books. It allows you to interact with the code first hand and makes it easier to understand once you see it in action (and you don't have to manually write it all too!). One caveat though is that the snippets use alert() for testing purposes, as they didn't want to rely on you having Firebug installed.

The book begins by talking about the jQuery core, of course. It states that jQuery should only be used in HTML standards mode, and not in quirks mode, as it has problems with rendering. One great aspect that I liked is its explanations of do's and don'ts and how to properly use jQuery to its full advantage. An example of this is to use the built in short hand code, and when to properly bind and invoke certain event handlers.

// You can get the current jQuery version by using the jQuery property
var version = $().jquery; // or $.fn.jquery

A feature that I rarely use (but probably should) is deep chaining. What I mean about deep chaining is using the find() function, paired with end(). I personally prefer just calling the functions separately on the specific jQuery objects. Regardless, jQE also made use of detailing out when and how this should be used and in what context (especially helpful for callbacks and special functions). While re-reading over the selectors chapters, I can't help but think to my self "A lot of this would be easier in 1.4 with the new functions, has() and not().", but you can still create your own selection filters (which is awesome).

// Define custom filter by extending $.expr[':']
$.expr[':'].positionAbsolute = function(element) {
	return $(element).css('position') === 'absolute';
};
var divs = $('div:positionAbsolute');

I always forget that you can create your own selection filters (just a reminder to myself!). Back on topic, jQE gets more in depth and accompanies it with more examples, so a must read if you are interested in such techniques. I bet you didn't know that you can nest filters as well:

var divs = $('div:not(:has(.jQuery))');
var ps = $('p').filter(':not(:first):not(:last)');

Moving on, the chapters on traversal are really well done, as it talks about rarely used functions like andSelf() and contents() (to me at least). However, I really feel like the forms chapter could of been a bit more in depth. It described how to properly select radios, checkboxes, selects, etc, but I felt as if something was missing. I noticed that when talking about getting/setting values for selects, that jQE might be wrong or out of date, as I am able to set the value of a select by passing the "value" tag of the option, and not the text. Maybe this has been updated in newer versions of jQuery, or I'm just blind. Besides that little mishap, it provides some great examples on how to select certain options based on the selection filters (pretty nifty!).

$('#select option:not(':selected')');

When talking about events and binds, it was pretty straight forward. Events in itself aren't that complicated to understand, especially in jQuery. However, there was one minor feature I didn't know about, and that was passing an empty bind function will trigger the previous bind.

// Alert will automatically trigger since we are passing an empty click()
$('a').click(function(){ alert('Its black magic!') }).click();

Actually, I just got a bit ahead of myself. There are some pretty exciting things you can do with events, like event specific namespacing, and using the event object functions (preventDefault(), stopPropagation(), etc). I won't get into this subject, as you should purchase jQE and find it out for yourself! Oh, did you also know that you can disable the right click menu in your browser (not in Opera, sorry guys!), by simply using jQuery? Me neither!

$(document).bind('contextmenu', function() { 
	return false;
});

Now for the bigger topics, Plugins, Effects amd AJAX. jQE covers the basics on how to properly write and create a basic skeleton for your plugins. It goes over the correct usage and context for this, how to apply the plugin to all selected elements with the each command, how to return and chain properly and how to easily extend and creating custom settings. Now that's basically all it talks about, which I think was too short. I've seen so many different variations and usages for jQuery plugins, and so many of them wrote it differently. For example, Ive seen people define functions within the each() loop, as well as outside the loop scope. I'm assuming this sets the visibility to private so that the functions can not be called, but what if I do want to call a plugin function?

$.fn.myPlugin.someFunction = function() { 
	// Do something 
}

Is it possible for me to call this function outside of the plugin, or does a plugin only package everything and do it automatically. There was too many "What if", "How does" type questions I was thinking to myself, and jQE was unable to answer. But these type of questions are probably best asked in larger more in depth books.

I don't want to keep boring you with minor details, and I would like to leave the chapters on AJAX and Effects alone (aka the exciting ones). By doing so, I hope you go purchase this document and read it for yourself. I promise you won't be disappointed.

Besides a few minor gripes and unanswered questions, jQE delivered everything I needed to know on how to properly use jQuery. It did its best to go into detail, and to also describe how the internals are working. I personally liked the fact that the document was littered with multiple examples on how to optimize and write fast code. A quick example for your enjoyment (and you may have not known that you can pass objects as an argument!).

$('a').css({'display': 'block', 'color': 'red'}).attr({'title': 'Title Txt', 'href': 'http://www.jquery.com'});

All in all, this was a great book to start learning jQuery. However, there was one thing I completely disliked, and it was when the book prefixed variables with a $ ($var), ugh! Sorry, this is Javascript, not PHP!

In closing, be prepared for my next review on AJAX and PHP: Building Modern Web Applications 2nd Edition, as I know some of my readers will enjoy it (yes you!).

AJAX calls in CakePHP: The Front-end

One of topics I always see pop up in the CakePHP Google Groups is how to correctly do AJAX calls in CakePHP. I will be splitting this up into 3 different entries, the first will be pertaining to the view/Javascript (front-end) and how to fire off the AJAX call, the second will deal with the controller action and how to process the request, and the final entry will be dealing with the response (in JSON format).

Additionally I will be using jQuery for all my Javascript interactions. Some developers ask me if I have ever used CakePHP's built in Javascript helpers, and I always so no. I don't find the benefit in using the helpers when it causes extra parsing in the front-end by running it through PHP objects, when you can simply write out the function name manually within the HTML template, which takes no processing!

Now lets begin by creating your forms within the templates. I will be doing a user login system to demonstrate the AJAX calls. Take note that I am applying a "return false" on the form's onsubmit attribute. This will stop the form from posting when a user hits enter, so that they must use the AJAX trigger.

<?php echo $form->create('User', array('onsubmit' => 'return false;'));
echo $form->input('username');
echo $form->input('password');
echo $form->end(); ?>
<button type="button" onclick="login();">Login</button>

The form is pretty straight forward, so lets start out by creating our login() function within the Javascript. The login() function will be firing an AJAX call to AjaxController::login(). Furthermore, I will NOT be doing any form input validation in the AJAX, feel free to figure it out yourself!

function login() {
	var data = $("#UserAddForm").serialize();
	/* Or no serialization (Read #2 below)
	var username = $("#UserUsername").val();
	var password = $("#UserPassword").val();
	var data = "username="+ username +"&password="+ password;
	*/
	$.ajax({
		type: "post",		// Request method: post, get
		url: "/ajax/login/",	// URL to request
		data: data,		// Form variables
		dataType: "json",	// Expected response type
		success: function(response, status) {
			// Will continue in part 3
		},
		error: function(response, status) {
			alert('An unexpected error has occurred!');
		}
	});
	return false;
}

There are a few key points I would like to discuss first about the code above, before continuing with the tutorial.

1) AJAX functions

In my example I am using jQuery's built in ajax() function. However, you can use the alternative post() function, but as a personal preference I like to use ajax(). (I will also post an article about the difference between get/post and when to use each one).

2) Form input names

By default if you use jQuery's serialize() function, it will convert all inputs into name/value pairs and wrap them into a query string. This means it also includes the "data[Model][field]" setup when posting to your AjaxController, which in turn means you can access the data at $this->data['Model']['field'].

If you want to do it the old fashion way (or when its not possible to serialize()), you can create the query string yourself by grabbing the value of each input and building the name/value pairs (You can see this in the example below). However, if you do not include the "data[Model][]" around each of the named variables, you cannot access the data in the controller with $this->data, you would have to use the $this->params['form']['field']. Below is a quick example of the possible jQuery data values and how to access them in CakePHP.

var data = "data[Model][field]=foobar";
$field = $this->data['Model']['field']; // Equals foobar
var data = "data[field]=foobar";
$field = $this->data['field']; // Equals foobar
var data = "field=foobar";
$field = $this->params['form']['field']; // Equals foobar

If you go the path of not wrapping your fields in data[] and are using my AjaxHandlerComponent, the component will automatically process and parse the jQuery posted data into the controllers $this->data.

3) Response type (dataType)

One major feature of jQuery is the "dataType" variable within its AJAX calls. If you are responding with a JSON object with the HTTP header of application/json, be sure to set the dataType to json or your response will fail -- this goes the same for XML. If your responding with plain text or HTML you can omit the dataType variable.

Pro Tip: If you are responding with a JSON content type, jQuery will automatically decode the JSON object for you (and apply it to the response argument for success, error, etc), so that you do not have to use a 3rd party library.

Now that we have the form and Javascript written, lets test our AJAX call. If you have Firebug installed (which you should), open it beforehand to check the HTTP request and response. If everything worked correctly so far, you should see the following HTTP request and response headers (Do note, your headers will be similar, and not exactly the same!).

// Request Headers
POST /ajax/login/ HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)
Accept: application/json, text/javascript, */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 73
// Response Headers
HTTP/1.x 200 OK
Server: nginx/0.8.15
Date: Thu, 01 Oct 2009 23:22:13 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/5.2.10
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
// Posted Variables
data[User][password] password
data[User][username] milesj

Basically what these headers are telling us is that our request was posted to /ajax/login/ and the server responded with an OK using the Content-Type of application/json. If you are unfamiliar with HTTP headers, I highly suggest you read the book "HTTP Developers Handbook, by Chris Shiflett.

That is all for now in this entry, stay tuned shortly for part 2 where I discuss the controller and action setup and how to properly respond to an AJAX call. Hope this helps you confused developers so far!

Toggling all checkboxes with JavaScript

Update You can view the comments for alternative and quicker ways to do this, enjoy!

Say you have a list of messages, and on the left of each message is a checkbox. This checkbox is used for a mass deletion or mass insert function here. Now to make it easier on the user, its nice to add a checkbox outside of the list that either checks them all, or dechecks them all. The following function can achieve that effect, using jQuery or regular JavaScript of course.

// Javascript
function toggleCheckboxes(current, form, field) {
	var val = current.checked;
	var cbs = document.getElementById(form).getElementsByTagName('input');
	var length = cbs.length;
	for (var i=0; i < length; i++) {
		if (cbs[i].name == field +'[]' && cbs[i].type == 'checkbox') {
			cbs[i].checked = val;
		}
	}
}
// jQuery
function toggleCheckboxes(current, form, field) {
	$("#"+ form +" :checkbox[name='"+ field +"[]']").attr("checked", current.checked);
}

Lets set up a quick example of how our form should be and how this function should be used properly. We will begin by adding the form tag with an id (required) and the message checkboxes.

<form action="" method="post" id="messageForm">
<input type="checkbox" name="messages[]" /> Message 1
<input type="checkbox" name="messages[]" /> Message 2
</form>

Let me explain the functions arguments before we finish up. The first argument should always be "this" so it can reference itself, the second argument would be the id of the containing form and the final argument would be the name of the checkboxes input name attribute (without the []). Now with that, we can finish this up by placing the following code anywhere on your page. Enjoy!

<label for="messages"><input type="checkbox" onclick="toggleCheckboxes(this, 'messageForm', 'messages');" /> Toggle all checkboxes</label>