Welcome to the training grounds of the JavaScript Jedi

document.queryString
2010-05-16

Over the years, there has never been a REALLY good way to easily access query string parameters in JavaScript.

Tonight I had a brain fart, and decided to come up with a function that creates a document.queryString object you can use to access them in object-oriented fashion.

For instance, if I had a URL like this ... http://javascriptjedi.com/querystring/?key1=value1&key2=value2

... I could access with:
document.queryString.key1 and document.queryString.key2

The cool thing about my implementation is that it handles keys with periods in them (common in ASP.NET MVC for complex models)... For instance, if I had a URL like this ... http://javascriptjedi.com/querystring/?Person.FirstName=JavaScript&Person.LastName=Jedi&Person.Address.Zipcode=11111

... I could access with
document.queryString.Person.FirstName and document.queryString.Person.Address.Zipcode

Now, there is a high probability that jQuery or Prototype have something similar; but I wanted to see how easy/hard it would be to write. I had this quick version done in about 30 minutes, and I'm pretty happy with it. I created it for a quick reference access, not to be smart so when you change a property, it'll automagically reload the page with a new revised query string (like setting window.location.search to something different than its existing value.)

If there isn't something like this out there, I hope you find it useful for a quick way to get to your query string params in JavaScript.

Download the querystring.js file (right-click and Save As...).

To use, call the SetDocumentQueryStringObject during your window.onload handler (or if you're a jQuery fan, you may call
$(document).ready(SetDocumentQueryStringObject);

Happy Coding!

Image Thumbnail Selector Experiment
2010-03-11

My friend Walt inspired me today.

He and I had been discussing an image thumbnail selector tool he was going to build into the ASP.NET site he's working on. We discussed some ideas and he came up with a really great one on how to bulid one by having a div with an image in it, and another div on top of it that used transparent borders to simulate the "greyed-out/non-selected" parts of the image and leaving the content area of the div to let the full-color image bleed through, thus representing the "selected" part of the image for the thumbnail. As the user clicked and dragged the thumbnail area, the transparent borders would be adjusted according to the movements of the mouse.

Of course my gears started to spin on how one would implement that in JavaScript and I was giddy. I told Walt he had to show it to me as soon as he was done.

Well... I couldn't wait for him. I came home tonight and had to implement it myself, because it sounded like such a fun project to tinker with. I was engrossed and ended up skipping working on side-job work I needed to get done tonight. heh

I'm still excited to see how Walt-O-Meal implements his; but while we wait for his unveiling, go check out my alpha version and see what you think.

It works fairly well in Firefox 3, Google Chrome 4 and Safari/Win. It's somewhat glitchy in Internet Explorer 8. IE won't let you click the thumbnail itself (content area of the overlay div) to drag the selector around... you have to click outside the thumbnail on the "grey" area to get it to move. Then it is still somewhat funky after you start moving it around. I think if you drag the mouse outside the image container element and let go of the mouse button, IE wants to remain in "drag" mode. I guess it makes a little sense, since the onmouseup event happens on targets that aren't the overlay div. Anyway, Opera doesn't work either... it's the most jittery of the bunch.

I hope you enjoy it. I sure enjoyed getting it to this point, and it only took me a couple of hours, which was fun too. I left all the CSS and JavaScript in the .htm file, so you can just View Source and see it all there.

Kudos again for the idea, Walt... it works! And have I mentioned lately how much fun JavaScript is? :)

Towers of Hanoi - JavaScript style!
2010-03-11

I actually wrote this a long time ago, and I'm not sure why it took me so long to link to it here... It's not much, but it was a fun little project writing the classic recursion problem in JavaScript. If you have some time to kill, give it a go.

The discs are dynamically generated by the JavaScript, which sets CSS rules for each disc's width so we could create the pyramid. If you want to see the dynamically generated source, you'll have to use Firebug in Firefox, IE's Developer Tools or something else of similar nature.

Enjoy...

Magic Box Again Works in More Browsers Than Just Internet Explorer
2009-01-25

When I first created the Magic Box page, I had it working in IE, Firefox and Opera.

The only reason it didn't work in Safari at the time, was because Safari refused to make radio button functionality work properly with multiple radio buttons that were named the same; unless said radio button tags were nested in a <form> tag. Since the W3C never mentioned a requirement for that, but on the contrary mentioned that DHTML authors would potentially wish to use those elements outside of form tags for purpose of a better user-experience on their page... I wasn't about to wrap them in a form tag when I didn't have a form to submit, just to get it to work in Safari. Safari would have to suffer until it was ready to conform with the way things are supposed to be.

However, when I finally got my script uploaded to the site... I'd somehow got an older script that only worked with IE because I was only checking the window.event when analyzing mouse events, which only exists in IE and browsers that mimic it. The other browsers send an event object to the event handler function that gets fired; and thus I can talk to it to get the event details.

I finally decided today to fix the functionality so it could work in more browsers. Suprisingly it only took me 5 minutes to fix as I remembered what I needed to do to access the sent-in event object instead. Why didn't I take that 5 minutes a LONG time ago and have it working all this time?! *sigh* That's what having limited free-time after-work-hours for software development fun does to you.

Anyway, I'm happy to say that it's finally working in multiple browsers, the way it was initially programmed; and even better, Safari has caught up and doesn't require the radio buttons to be in a form element anymore... so it works in Safari too.

After uploading my changes, I tested in Win/Internet Explorer 7, Win/Firefox 3. Win/Opera 9.27, Win/Safari 3.2.1 ... I haven't tested in the Mac browsers yet.

** I know Scriptaculous and other cool JS libraries already provide this kind of functionality for you, but it was a fun experiment back then (and even now) to understand a little more about mouse/key events and how to utilize them when their events fire in a page. Enjoy!

JavaScript Won't Run on Internet Explorer as a Local File When Copied From Another Computer
2008-08-14

My friend Walt and I discovered an interesting phenomenon in Internet Explorer that keeps JavaScript from executing (no information bar prompt for you to allow it) because it comes up in the "Restricted Sites" zone when loaded.

It happens when you copy a file created on one machine to yours and try to run it on yours.

Find out why JavaScript Won't Run on Internet Explorer as a Local File When Copied From Another Computer on my "Software Development" blog... and find out how to fix the problem when you need the code to run.

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.