Monday, November 26, 2007

JavaScript String :: Primitive or Object?

A colleague and I were chatting about JavaScript strings and he noted that they're not objects. He's half right.

JavaScript strings are primitives much like numbers and booleans, but they can also be objects ( like Numbers and Booleans ).

You can create a String object ( I've capitalized String ) by calling it's constructor --

var aStr = new String("I'm a String object");

You can also create a string like this without calling the constructor ( that is without the "new" operator ) --

var aStr = String("I'm a string primitive");

This is the same as calling --

var aStr = "I'm a string primitive";

which results in a string primitive or as I typically call it, a string constant or a string literal.

Typically, you don't have to worry about whether you're dealing with a string primitive or a String object because JavaScript automatically converts the primitive to a String object.

So, you can do things like getting the length of a string primitive --

var myStr = "I'm a string primitive";
alert(myStr.length);

or calling methods --

alert("string primitive".substring(0,7));

All this means is that you can access any property found in the String object from a primitive.

The difference between a primitive and a String object is when you're adding functionality to a String. You can't do it to primitives.

So, if you wanted to add a function to reverse the characters in a string, you'd do something like this by adding it to the String object's prototype property --

String.prototype.reverseString = function() {
var len = this.length;
var result = "";
var strArray = [];
var ct = 0;
for (var i=len-1;i>=0;i--) {
strArray[ct++] = this.charAt(i);
}
return strArray.join("");
}

If you called it,

"cola".reverseString();

it's the same as

var cola = new String("cola");
cola.reverseString();

Though you've added the method to the String object, you can use it with a primitive!

Note that JavaScript strings are immutable ( you can't change them ). This is why the method reverseString returns a new string from the array.

All of this is pretty obvious, but I thought I'd share my thoughts.
Enjoy!

Sunday, November 18, 2007

iGoogle and Newsvine Dashboards

I've always been partial to dashboards and often write about it. I've done a number of prototypes just to experiment with visualizations.

Dashboards allow you to organize and see the information you want without adding too much clutter.

Here's my implementation. It works in IE, Firefox, Safari and Opera.

I particular like the visualizations found in the iGoogle and Newsvine dashboards. They provide nice feedback on the container that you're moving and where you'll drop it. So, I've copied that.

When you move a container, everything is **real** size -- not a sliver for where you'll drop the container. For me, the argument that you cause too drastic of a visual change to the layout doesn't hold water.

You're only moving one container at a time and it's not a common operation. You basically move a container, get it into the fold and view it.

You're also only dealing with a **few** containers. This is a dashboard.

The key to the implementation is controlling the container's drag over behavior. Each container needs to react to the container that's being dragged over it. So, pick a library that the supports such behavior ( you can pick Bindows, but keep in mind that if you do, you won't be able to see the dashboard if you turn JavaScript off; of course, with any library, turning it off means that there's no behavior at all! ).

My implementation uses YUI 2.3 which easily handles drag over with the drag and drop library.

In this case, a drag over, inserts a temporary marker above the container.

When you drag out, the marker is removed and the container list collapses ( each container is statically positioned ).

All of the JavaScript is really short and pretty much self explanatory. Run the example and view the source. Use and improve it.

Have fun!

Sunday, November 11, 2007

Select and Scroll

A colleague asked how I would implement this --
  1. Click on an item in an list
  2. Vertical scroll to it
I can up with this --



I think the only way that this can be done is if the item ( in this case a div ) is absolutely positioned. For each item, you can get the value for CSS style "top" from the item's parent ( container ).

If the item uses default positioning ( i.e. static ), "top" is auto which is pretty much useless for moving the vertical scroll bar. If position is relative that's also not useful because "top" is relative to the previous element.

The idea is that we'll increment the parent's "scrollTop" property until that's equal to the top position of the item. This causes vertical animated scrolling ( if you don't want animation, you could just set "scrollTop" to "top"; however, keep in mind that this "jumps" rather than transitions to the item ).

We end up with a some simple JavaScript --

var doScroll = function(e){
var target = YAHOO.util.Event.getTarget(e);
var container = document.getElementById("container");
var targetTop = parseInt(YAHOO.util.Dom.getStyle(target, "top"));

var animateScroll = function() {
var maxScrollTop;
if (container.scrollTop !=
maxScrollTop = container.scrollTop; container.scrollTop+=2;
container.scrollTop == maxScrollTop ? clearInterval(intervalID) : "";
} else {
clearInterval(intervalID);
}
var info = "container.scrollTop = " + container.scrollTop;
document.getElementById("info").innerHTML = info;
}
var intervalID = setInterval(animateScroll, 1);
}
YAHOO.util.Event.on("container", "click", doScroll);

Note that we're using YUI, but that's not critical. YUI is used to handle events in a cross-browser manner as well as getting the CSS style ( you can do this with target.style.top, but I chose to use YAHOO.util.Dom.getStyle() ).

maxScrollTop tells us whether the scroll button is at it's maximum value. We know this when we increment the container's scrollTop and it doesn't change. When that happens, we stop the animation by clearing the timer.

You can see the full example
here.

If you can do this with static or relative positioning, I'd love to see your solution!

Have fun!