A deep(ish) dive in to the structure of jQuery

I’ve been working with Murach Publishing to revise their JavaScript book, and it’s almost ready to go to press (yay!). While I was finishing up the final Intro to jQuery chapter, though, I found myself puzzled by something. But first, let me give you some JavaScript background to explain my puzzlement.

In JavaScript, you can use constructor functions to make multiple instances of an object. When you do, you should put the object’s methods on the object prototype. This saves memory and makes the methods available to all instances of the object.

By contrast, when you’re working with a singleton, like an object literal or an object created with the module pattern, there’s only one instance of the object. In this case, you should put the methods directly on the object.

The jQuery library uses the module pattern to create a global singleton object named jQuery. That means there’s only one instance of jQuery. But when you create a jQuery plugin, you’re supposed to add your method to the jQuery.fn (or $.fn) object, which is an alias for the jQuery object’s prototype. But why? If the jQuery object is a singleton, why are we adding methods to it’s prototype?

Further puzzling me, the jQuery plugin tutorial says “Whenever you use the $ function to select elements, it returns a jQuery object”. Hmm. If jQuery is a singleton created by the module pattern, how is the $ function returning jQuery objects?

Now, you should know that you don’t need to have the answers to these questions to use jQuery effectively and write your own plugins. You don’t even need to have them to write an effective Intro to jQuery chapter in a JavaScript technical book. And so I set this all aside and went on with my work.

Yesterday, though, I couldn’t take not knowing the answers for one more second. So I read some articles online and studied the uncompressed jQuery code (luckily the library isn’t too massive), and I think I’ve figured it out. Bear with me, though – things get a little circular.

The jQuery object is indeed a singleton created with the module pattern, and the methods that jQuery uses are indeed on this singleton object’s prototype. But, crucially, one of the objects on that prototype is a constructor function named init. And, also crucially, the prototype of the init constructor function points to the prototype of the singleton jQuery object. Finally, most crucially, an instance of this init object is what is returned when you call the $ function.

Another way to look at this is that the singleton object’s $ function is a factory that creates and returns instances of the jQuery.fn.init constructor function. And both the singleton and the instances of the init object are referred to as jQuery objects. And they have the same prototype, so anything I add to the singleton’s prototype is also available to the prototype of all the instances of the init object.

Phew! No wonder I was puzzled. I’m not enough of a jQuery maven to know why this structure was chosen. It might be that there’s some really advanced JavaScript benefit to coding it this way. Or it might be that it’s a holdover from earlier versions that they wish they could change but it would cause too many problems. But at least I think I’ve got a grip on why I’d want to add my plugin code to the singleton jQuery object’s prototype.

Here are some code examples that illustrate:

example

Advertisements

Add maxlength to textarea with jQuery

An html input field has a maxlength attribute to limit the number of characters that can be entered in the field, but an html textarea does not.  However, maxlength functionality can be easily added to textarea controls with a little bit of jQuery.

This snippet looks for textareas that have a maxlength attribute, and limits the number of characters to the number in the attribute.  This could use a bit of validation code, perhaps – making sure that the maxlength value is numeric, for example.  But you get the idea…

$(document).ready(function(){

    $(‘textarea[maxlength]’).keyup(function() {

        //get textarea text and maxlength attribute value
        var t = $(this);
        var text = t.val();
        var limit = t.attr(‘maxlength’);

        //if textarea text is greater than maxlength limit, truncate and re-set text
        if (text.length > limit) {
            text = text.substring(0, limit);
            t.val(text);
        }
    });

});

Formatting percentages with Javascript

My current project has several pages where I need to grab values from textboxes, calculate a percentage, and then display the percentage in a label.  So I needed to write a Javascript function to do that.

As always, I relied on Google, as I’m not a Javascript expert, and find it easier to start with other people’s code than figure out all the details myself.  So I found a nifty little bit of code that formats a decimal as a percentage using Math.round.  All well and good, except my client wants some percentages to always have two decimals, even when the calculated value is a whole number.  And, of course, Math.round rounds to the nearest integer – no remainders.

So I found another nifty little bit of code that allows you to round to however many decimal places you want, rather than just to the nearest integer.   Better, but this code will return a whole number with no decimals when the calculated value is an integer, and I need two decimal places no matter what.

And so I found another neat bit of code, which uses the modulus operator to determine whether the value is an integer – if it is, then I’ll add the “.00” manually.

Just a couple more wrinkles – in this application, when the values in the texboxes are negative numbers, they will be formatted as (100), rather than -100.  And, sometimes the client wants two decimals, and sometimes he wants no decimals.

So I put it all together as follows (a previous function – not shown – does the calculation, and sends the calculated value to the function below):

function formatPercent(value, noDecimal)
{
//set default flag value
var isNegNum = false;

//set default multiplier value (will produce result with 2 decimal places)
var mult = 100;

//if neg number (begins with paren)…
if (value.substring(0,1) == “(“)
{
//set flag to true
isNegNum = true;

//remove parens from value
value = value.replace(/(/,”);
value = value.replace(/)/,”);
}

//if  no decimals wanted (‘noDecimal’ param has a value), set multiplier to 1
//(will produce result with no decimals)
if (noDecimal != null) {mult = 1;}

//format percent (‘mult’ variable determines whether has 2 or 0 decimals)
var result = Number(value)*100;
result = Math.round(result*mult)/mult;

//if decimals wanted (‘noDecimal’ param doesn’t have a value) and result
//is a whole number, manually add decimals
if (noDecimal === undefined && isInt(result) == true)
{
result = result + “.00”
}

//add minus sign to neg number
if (isNegNum == true)
{
result = “-” + result;
}

//add percent sign
result = result + “%”

//return formatted percent
return result;
}

function isInt(num)
{
// get the modulus
var mod = num % 1;

//if it’s 0, then it’s an integer
if (mod == 0) {
return true;
} else {
return false;
}
}

To use this function, send in the value that you want to be formatted as the first argument.  If you want the formatting to contain two decimal places, then don’t send anything in for the second (‘noDecimal’) argument.  If you want no decimals, then send in something for this second argument (doesn’t really matter what, as the function only checks to see if the parameter is null or not – I’ve been sending the string “true”).

Correct for 2-digit date entry in Javascript

In working with the function described in the last post, I realized that I needed some way to correct the year when a user entered a 2-digit year as opposed to a 4-digit year. This is because Javascript interprets ‘3/1/09’ as ‘March 1, 1909’ — throwing off my calculations quite alarmingly. There are a variety of fixes for this posted online, but most of them are focused on 19xx and 20xx. And even though it seems unlikely that anyone will be using anything I’ve written in 21xx, I still wanted to plan for it – wanted to learn from my y2k forebears.

So I worked out a script that subtracts the first two numbers of the entered year from the first two numbers of the current one, multiplies the result by 100 and then adds it to the entered year. Thus, if the current year is 2008 and the entered year is 1909, the script does this:

  • 20 – 19 = 1
  • 1 * 100 = 100
  • 1909 + 100 = 2009

If the current year is 2008 and the entered year is 2009, the script does this:

  • 20 – 20 = 0
  • 0 * 100 = 0
  • 2009 + 0 = 2009

And, if someone is still using my script in 2108, and javascript still interprets a 2-digit year of ’09 as 1909, the script would work like this:

  • 21 – 19 = 2
  • 2 * 100 = 200
  • 1909 + 200 = 2109

Here’s the script:

function correctYear(dt)
{
//get current date
var currDate = new Date();

//get full year for current date and arg date and convert to string
var dtYear = dt.getFullYear() + “”;
var currYear = currDate.getFullYear() + “”;

//subtract first two numbers of arg year from first two numbers of current year
var diff = (Number(currYear.substring(0,2)) – Number(dtYear.substring(0,2)));

//multiply difference by 100 and add to arg year
dtYear = Number(dtYear) + (diff * 100);

//set corrected year as arg date year, and return corrected date object
dt.setFullYear(dtYear);
return dt;
}

It’s not perfect – if the someone in 2108 really meant 2009, not 2109, then they’ll have to type that out. Same for someone in 2098 referring to 2102 – if they type in ’02, my script will interpret that as 2002 (of course, once the century turns, it’s all good again). But I took my shot. You can see the function in action in my last post.

Get the difference between two dates with Javascript

This is nothing earthshaking; in fact, it’s almost a complete rip off of another blog post, where I initially found what I needed. But I thought I’d put it here, so it would be easier for me to find it again when I need it again:-)

function daysDiff(dayStart, daySubtract)
{

//if either date is empty, return empty string
if ((dayStart == ”||dayStart == null)||(daySubtract == ”||daySubtract == null)){
return ”;
}
else {
//convert values to dates
var dateStart = new Date(dayStart);
var dateSubtract = new Date(daySubtract);

//correct for 2-digit year entry
var dateStart = correctYear(dateStart);
var dateSubtract = correctYear(dateSubtract);

//one day in milliseconds
var oneDay=1000*60*60*24;

//use the getTime() method of the javascript date object, which converts
//date to milliseconds. Subtract dates, then divide by oneDay variable
//to convert milliseconds to days. Round using the Math.round()* method.
var days = Math.round((dateStart.getTime()-dateSubtract.getTime())/(oneDay));

//return variable
return days;
}
}

*Originally, I used Math.ceil() here (because the blog I ripped off used Math.ceil).  However, I found in practice that Math.ceil did not give me the results I wanted.  Math.ceil always rounds upwards to the nearest integer, whereas Math.round will round up or down to the nearest integer.

Dynamically change Master Page body tag

Today I spent several hours butting up against this, and finally, after much gnashing of teeth, found this great blog entry explaining what I needed to know.

Basically, the problem is that I needed to call a javascript function onload in the body tag of the page, but of course in an ASP.NET Content page there is no body tag – it only occurs once in the Master Page. But I didn’t want to put my javascript call in the Master page body tag, because then it would be called on every page.

I tried putting it in window.onload in a script tag at the bottom of the page, but this then screwed up some ASP.NET-generated javascript from a validation control, and also some of my other javascript.

The solution put forth in the blog entry is to dynamically put the onload function call in the Master Pages body tag from code in the Content page. Don’t have time to show the code myself (it’s quittin’ time), but please feel free to follow the above link.

By the way, this still didn’t solve my problem in Mozilla, but it works in IE, and as this particular project is for a company intranet that only uses IE, I’m happy (at least for now).

Fire javascript onchange event programmatically

Recently I was working on a project where I needed to tally a bunch of textbox values using javascript. The textboxes were in a table with 3 columns and about 14 rows. The far column and the bottom row were readonly textboxes used to total the various rows and columns. I wrote some javascript that fired on each textbox’s onblur event so that whenever a textbox value was changed, both the column and row totals would update.

My problem was totalling the far column. Because the textboxes in the far column were readonly, the text was being changed programmatically rather than by a user. Thus, the onchange event was not firing, and the javascript written to total the column was not running.

I turned to Google, and found a little blog entry that showed me how to fix my problem. It was refreshingly simple – test to see if an onchange event exists for the readonly textbox, and then fire the event. So I could total my row, and then fire the total textbox’s onchange event to total the far column. See below for an example.

//sum row values
function addRow(sField, iField, totField)
{

//get row values from textboxes on page
var s = document.getElementById(sField).value;
var i =
document.getElementById(iField).value;

//convert values to numbers
s = Number(s);
i = Number(i);

//add values together
var total = s + i;

//load total textbox in variable
var TotField = document.getElementById(totField);

//set total textbox value to sum of values (format as currency)
TotField.value = '$' + addCommas(total);

//fire onchange event on total textbox (make sure it exists first)
if (TotField.onchange) {TotField.onchange()};

}

Notice the addCommas function call above. This is a nifty little function to format numbers with commas in all the right places — lifted wholesale from another blog entry I found. Thank God for bloggers!!