Welcome to the training grounds of the JavaScript Jedi

Regular Expression Discovered in Internet Explorer 7 JavaScript Engine
2008-05-06

I discovered a bug in the IE7 JavaScript Regular Expression engine when trying a validation pattern out in several browsers. See the blog entry I did on That One Developer Blog for more details about it.

Go to the Regular Expression Tests for Your Browser page and try it out in several browsers. The other major Windows platform browsers at the time of this writing work fine with the expression; just not IE7. (I tested on Firefox, Safari and Opera.)

A Tale of Two Properties (using prototype)
2008-02-29

I was teaching my friend Joe about how prototype works in JavaScript; and how you can use it, essentially, to create static properties across multiple instances of classes.

For those that haven't used prototype in JavaScript, the concept is fairly straight-forward. You create a class by defining a function and afterward set new properties and methods that will be "shared" by multiple instances using {className}.prototype.{propertyOrFunctionName} and assigning them properly:

function MyClass( someString )
{
  // Every object instance created from MyClass will have its own InstanceProperty
  // and it will be assigned whatever the "creator" passes into the constructor as its value.
  this.InstanceProperty = someString;
}

MyClass.prototype.StaticProperty = "Hello World";

//=================================

var instance1 = new MyClass("Instance 1");
var instance2 = new MyClass("Instance 2");
					

Now we have:
instance1.InstanceProperty == "Instance 1";
instance2.InstanceProperty == "Instance 2";
instance1.StaticProperty == "Hello World";
instance2.StaticProperty == "Hello World";

Let's take instance2 for example. When we access the StaticProperty, JavaScript looks at the instance and asks, "Do you have an 'instance property' called "StaticProperty?"

Instance2 responds with a "no", so JavaScript decides to examine the MyClass.prototype and see if it's defined there. It sees that it is and returns the string "Hello World".

Those that haven't used this before are saying to themselves, "... And I care because ...?!?!?!"

Think about this set of code:

function MyClass()
{
	this.InstanceProperty = "Hello World";
	this.ShowInstanceProperty = function(){ alert(this.InstanceProperty(); }
	
	this.ReallyLongFunction()
	{
		var result = "";
		for( var x = 0; x < 1000; x++ )
		{
			result += x + "<br />\n";
			
		}
		
		//... does a lot more ...
	}
	
	this.SomeOtherFunction()
	{
		// ... does even more ...
	}
}

var instance1 = new MyClass();
var instance2 = new MyClass();
var instance3 = new MyClass();
var instance4 = new MyClass();
var instance5 = new MyClass();
					

Each time the MyClass constructor above is called (in this case 5 times...) we end up creating new copies of all instance members in new memory space. That means the ShowInstanceProperty, ReallyLongFunction, SomeOtherFunction functions get 5 copies made; making 15 functions in memory space that do the same things. What a waste of memory!

Using prototype instead causes the same 3 functions to only be created once in memory space; only one of each. Look at the "prototyped" code:

function MyClass()
{
	this.InstanceProperty = "Hello World";
}

MyClass.prototype.ShowInstanceProperty = function(){ alert(this.InstanceProperty(); }

MyClass.prototype.ReallyLongFunction = function(){
	var result = "";
	for( var x = 0; x < 1000; x++ )
	{
		result += x + "<br />\n";
		
	}
	
	//... does a lot more ...
}

MyClass.prototype.SomeOtherFunction = function()
{
	// ... does even more ...
}


var instance1 = new MyClass();
var instance2 = new MyClass();
var instance3 = new MyClass();
var instance4 = new MyClass();
var instance5 = new MyClass();
					

It looks a little odd at first; but once you get used to it, it's "No Big." And trust me when I say, it's the better way to go. Think about if MyClass had 30 functions defined for it that all had 10 lines of code each. Now pretend we went with the "instance functions" example and through our script ended up creating 1000 instances of MyClass. That amounts to 30 functions times 10 lines of code times 1000 instances.

30 x 10 x 1000 = 300,000 lines of code eating up memory space now.

If we use prototype: 30 x 10 x 1 = 300 lines of code taking up memory space.

Nevertheless, I digress. The story for today wasn't supposed to focus on that. That really needs to go into its own document on this site; which I hope to get to at a later time (like the rest of the projects I've uploaded to the site but haven't had time to document, so they're not listed... one day!)

Back to the story for today...

So I was showing Joe some code for demonstrating how prototype seemed to make the concept of Static Properties availble across multiple instances of objects. Then it dawned on me that I'd never tried assigning the "Static Property" after creating instances of a class that has been given prototype properties.

Can you guess what happened? Here's some sample code:

function MyClass( someString )
{
	this.InstanceString = someString;
}

MyClass.prototype.StaticString = "Hello World";

//=====================

var instance1 = new MyClass("Instance 1");
var instance2 = new MyClass("Instance 2");
alert(instance1.StaticString);	// output: "Hello World";
alert(instance2.StaticString);	// output: "Hello World";

// Attempt to assign the static property, with the intent of updating what both instances see.
instance2.StaticString = "Some New String";
alert(instance1.StaticString);	// output: "Hello World";
alert(instance2.StaticString);	// output: "Some New String";
					

What the...?! After thinking about it for a second, it made total sense to me; and this is something important we should all remember when programming in JavaScript...

Writing a Property
When you access a property to set its value and that property doesn't exist on the object yet, JavaScript will create the instance property without looking at the prototype and make the assignment.

Reading a Property
When you access a property to get its value, it will look at the instance first and see if it's got an instance property with that name. If it doesn't, it will check the prototype and return its value (if it exists there.) If it doesn't exist on the prototype, JavaScript will throw the infamous "Object doesn't support this property or method." error.

So, it was the best of times, it was the worst of times... what was two instances talking to one "static property", we now had one talking to a "static" and one talking to an "instance".

This got me thinking even harder about it and wondering if there was some way to still use it as a static property.

Sure... since JavaScript doesn't put constraints on when you can/can't set the value of a property, you can reset the value of the prototype property by setting it the same as when you initially created it... Here's our example again showing this concept:

function MyClass( someString )
{
	this.InstanceString = someString;
}

MyClass.prototype.StaticString = "Hello World";

//=====================

var instance1 = new MyClass("Instance 1");
var instance2 = new MyClass("Instance 2");
alert(instance1.StaticString);	// output: "Hello World";
alert(instance2.StaticString);	// output: "Hello World";

// Re-assign the static property, with the intent of updating what both instances see.
MyClass.prototype.StaticString = "Some New String";
alert(instance1.StaticString);	// output: "Some New String";
alert(instance2.StaticString);	// output: "Some New String";
					

This is really great for scenarios where someone using your class might be creating multiple instances of your class; but you want to ensure that the functionality from each instance always talks to a "central" property for data manipulation. Later on if/when I get more time I'll update the site witha fun proof-of-concept I worked on tonight for handling multiple window.onload events to ensure when multiple scripts are included in a page and could potentially all be assigning window.onload.

For now, my sleep-deprived body won't let me do anymore. Until next time... I hope this will help in our quest to become JavaScriptorians - Head of the "Class". :)

Happy Scripting...

JavaScript Countdown Timer and Date Manipulation
2008-01-30

Thanks goes out to my friend Jarin that found a bug in the Countdown Timer. It's an interesting bug because it only manifested itself on the last dates of a month that contains more days than the month of the Countdown Date. (Example: Today = 30 January, Countdown Date = 14 February)

The bug occurs because of the way JavaScript handles dates when the setMonth, setDate, setYear functions are called. When any of these functions are called, the given value is set, then the date is re-evaluated to see if any of the other values have been invalidated by the change. If so, those values are modified to make the date correct again.

Let's take the example above. The timer creates a new Date object (which represents the current date/time; so in this case, 30 January 2008.) We'll call this instance "theDate". The timer then takes the string date passed into it and breaks it up into its various date parts (in our case: 2, 14, 2008 respectively.)

The timer then starts setting the input date values into theDate. It starts by setting the month.

// We have to subtract one because JavaScript months are zero-based.
theDate.setMonth(2 - 1);
					

The result of this call is that theDate now represents 2-30-2008 (30 February 2008) which we all know doesn't exist. JavaScript knows this too... so it says, "What is the equivalent date to this?" It decides by taking the number of days in February 2008 (29) and subtracts that from the current value (30) which gives it: 30 - 29 = 1; and then adds that many days (starting with the first day of the following month; in this case March.)

This leaves JavaScript thinking theDate really represents 01 March 2008, which wasn't our intended result, since we were about to set the Date next.

// With our current example, this now makes us represent 14 March 2008!!!  ACK!
theDate.setDate(14);
					

So the timer incorrectly reported 44 days instead of 15 days. To solve this... I ensure that theDate gets the following call before the real month, date or year are set, to keep us on even ground no matter the month.

// Set to the first date of the month
theDate.setDate(1);
					

I'll be re-evaluating the way the date handler functionality is written; I don't like it much right now and would like to find a simpler way.

// 31 January 2008
var d = new Date(2008, 0, 31);
document.write((d.getMonth() + 1) + "-" + d.getDate() + "-" + d.getFullYear() + "<br />");
// 31 February 2008 -> 02 March 2008
d.setMonth(1);
document.write((d.getMonth() + 1) + "-" + d.getDate() + "-" + d.getFullYear() + "<br />");

output:
1-31-2008
3-2-2008
					

Moral of the story? Be VERY careful when manipulating a Date object by using the set[Month|Date|Year] functions. The same re-evaluation of the correctness of the date happens on each set call... for instance:

// 01 March 2008
var d = new Date(2008, 2, 1);
// 42 is 11 days past 31 
// (42 is also the answer to life, the universe and everything...)
d.setDate(42);
document.write((d.getMonth() + 1) + "-" + d.getDate() + "-" + d.getFullYear());

output:
4-11-2008

// 29 February 2008 (2008 is a Leap Year)
var d = new Date(2008, 1, 29);
// 29 is 1 more than 28 (2007 is NOT a Leap Year)
d.setYear(2007);
document.write((d.getMonth() + 1) + "-" + d.getDate() + "-" + d.getFullYear());

output:
3-1-2008
					

I'm grateful JavaScript handles dates this way, as it is super useful for performing operations such as adding or subtracting a number of days from a given date without having to worry about the month's boundaries; the JavaScript will handle it for us.

// The IRS told me I could get my refund within 100 days of filing... 
// Let's find out the farthest day out from Tax Day so I can know the latest I'll get it.
// 15 April 2008 (Tax Day - blech)
var d = new Date(2008, 3, 15);
d.setDate(d.getDate() + 100);
document.write((d.getMonth() + 1) + "-" + d.getDate() + "-" + d.getFullYear());

output:
7-24-2008
					

Shame on me for not catching this before... I knew better, but for some reason didn't dawn on me when I was writing it. Oh well, it's fixed now. Enjoy!

What JavaScript DOM modules does your browser support?
2006-08-03

I was reading in my O'Reilly book "JavaScript: The Definitive Guide 4th Edition" about events and event handling in JavaScript, trying to grasp a better understanding to help me through some problems with event firing that I'm having at work.

Long story short, I decided to make a page that will tell you information about your browser by querying the navigator object and document.implementation.hasFeature() method (if your browser supports it.)

So if you're curious, go find out which DOM Features your browser reports support for. There is no documentation for my code as of yet, but feel free to take a look at it.

Oh! Did I mention...?! O'Reilly is getting ready to release their "JavaScript: The Definitive Guide 5th Edition" this month! I've already got mine on order and can't wait to have it in my hands.

I also found out today that the latest Firefox 2 Beta offers support for JavaScript 1.7! What is JavaScript 1.7, you ask? Well, from what little reading up on it I've been able to do so far, it appears to be based on the newest EMCAScript standard "ECMAScript for XML (E4X)".

document.getElementById() IE Glitch Article Added
2005-06-24
I rediscovered an IE glitch in the getElementById() function. I decided to do some research and experimenting on it... I hope you find it useful.
Welcome to JavaScriptJedi.com

Welcome to the first hints of what you'll find at JavaScript Jedi site. My name is Jeff Litster, and I am putting up this site as a repository for my JavaScript experiments.

I love the JavaScript language and all the cool things it can do for client and server-side scripting. Many nights I find myself thinking of a cool thing to try in JavaScript that helps me understand it better.

My goal with the site is to post my projects here in the case it might help someone else accomplish their goals with their web sites that either don't have the time to do it themselves, or need some extra help on making it happen. I hope this site will be educational and fun for all who visit.

Please pardon the lack of luster on the site while we get a blog set up. For now, enjoy the projects I've already posted. Forgive the lack of documentation in the project pages, I plan to revamp those pages for the "official" site launch as well.

So until then... you'll have to live with what I've got. hehe! The newer projects have been tested with most fairly new (if not newest) releases of the following browsers: (Windows/IE, Windows/Firefox, Windows/Opera, Mac/Safari, Mac/Firefox, Mac/IE.)

That said, you may need to tweak code a little to work in those not-listed above (or in the case of my older projects, until I can get them tested... you may need to tweak for some of those too.) Feel free to use any of the code here for your own use. I'm not a stingy freak, nor do I claim to be the best coder in the world... but I have fun at what I do, and I hope you'll have fun in sharing. (And hopefully you'll be kind enough to say you got it from the JavaScript Jedi if you do use something in your site. Not necessary, but it's nice to know when your stuff gets used.)

Anyway, enough of my rambling, enjoy your stay. Hopefully we'll have this site whipped into some decent shape in the near future. For now, just play with the pages and read the JavaScript code for yourself where documentation is lacking.