Monday, April 13, 2009

Rnadom Flex Note - Event handlers Nightmares

It is onerous to say the least to determine which events to handle for many objects.

 

For instance today, I was trying to get an <mx:HSLIDER> control to change the size of something in real time (as the thumb drags around)..

 

Let me explain how I figured out which one to use.

 

Here is how I started out:

 

<mx:HSlider id="mySlider" />

<mx:Text id="foo" text="{mySlider.value}"/>           

 

With this setup, there is some magic that occurs so that when you stop dragging the little thumb, the binding takes into effect and the value in the text box updates.  Easy enough, right?

 

So now, what if I want the value in “foo” to update AS I DRAG THE THUMB.  Should be possible (it is).

 

I counted, and there are no less than 63 events that an HSLIDER control can fire.  Which one to trap?

 

Well, not finding an obvious answer on the internet, I began attaching handlers to events that seemed to make sense:

 

So here is my first iteration:

 

<mx:HSlider id="mySlider" change="sliderChange(event)"/>

 

Notice that with Flex, you always have to pass in the (event) object. 

 

Now in your handler, you have to “handle” the specific type of event that has fired.

 

So let’s say I wrote my handler as follows:

 

    internal function sliderChange(event:StateChangeEvent) : void {

      var theSlider:Slider=Slider(event.currentTarget);

      foo.text = String(theSlider.value);

    }

 

THIS WILL NOT WORK AS I HAVE NOT USED THE RIGHT EVENT TYPE.

 

There are two ways to determine what type of event to put in your handler.

 

1)       put *something* and let the IDE tell you:

     “Error implicit coercion of a value of type mx.Events.SliderEvent to an unrelated type mx.Events.StateChangeEvent”

So, in this case, it’s telling you to use “SliderEvent”, not “StateChangeEvent”

2)       The second (preferred) way is to simply hover over the “event” part in your line:

<mx:HSlider id="mySlider" change="sliderChange(event)"/>

      *sometimes* a tooltip will appear telling you what event handler type to use

   3) A third way is to launch in debug mode, get an error and then you can hover over stuff and figure out what type of event handler to use:  See Screenshot:

 

 

One would think that this tells me the event type is SliderChange.

 

The actual event handler type is “SliderEvent”

 

The reason I am telling you all of this is that it is extremely frustrating to do some simply shit in the Flex IDE at the moment. Surely there must be a better way.

 

Anyway, moving on.

 

So now we have:

 

<mx:HSlider id="mySlider" change="sliderChange(event)"/>

 

And an event handler

    internal function sliderChange(event:SliderEvent) : void {

      trace(“sliderChange eventg fired”);

      var theSlider:Slider=Slider(event.currentTarget);

      foo.text = String(theSlider.value);

    }

By adding “trace” calls, when you run in debug mode, the text dumps to the console.

 

So, in this case, guess what?  This event does not fire until the user has released the thumbdrag handle!

 

So I try another event han dler

 

<mx:HSlider id="mySlider" currentStateChange="sliderStateChange(event)"/>

 

Notice I have to use a different handler because (you guessed it), the event fired is different.  So, going through the same agony as before, I determine that the event type being fired is, in fact, called “StateChangeEvent”. (Bascically by trying to run the app, letting it error out and reading the error message.

 

    internal function sliderStageChange(event:StateChangeEvent) : void {

      trace("sliderStageChange");

    }

 

Near as I can tell, this event does not fire.

 

So, thinking that it is not really the “slider” that is changing but the thumbdrag handle, I thought to add an event handler to that one as well:

 

Now, iteration #4:

 

        <mx:HSlider id="mySlider" thumbDrag="thumbDrag(event)" />

        <mx:Text id="foo" text="{mySlider.value}"/>           

 

And writing another event handler:

 

    internal function thumbDrag(event:SliderEvent) : void {

       trace("thumbDrag");

    }

 

 

I can see in my debug window that this is, in fact, the event that fires as you move the thumbDrag thingie around.

 

Therefore, I fix the event handler to read As follows:

    internal function thumbDrag(event:SliderEvent) : void {

       trace("thumbDrag");

      var theSlider:Slider=Slider(event.currentTarget);

      foo.text = String(theSlider.value);

    }

 

 

GUESS WHAT?  IT STILL DOESN’T WORK!

 

Turns out all you have to do is add “livedragging=”true” to the HSlider control and you do not have to write any event handlers.

 

 

So,

        <mx:HSlider id="mySlider" liveDragging="true"/>

        <mx:Text id="foo" text="{mySlider.value}"/>           

 

Does the trick..

 

 

Why do I tell you all about this?  So hopefully you can share in my pain and perhaps to learn something from my stupidity.

Flex is cool, but there is a pretty steep learning curve.

 

-shannon norrell

 

 

Wednesday, February 25, 2009

Fast JavaScript Max/Min on an Array Taking Arbitrary Number of Arguments

What's the fastest way to find the largest, or smallest, number in an array?

Array.max = function( array ){
return Math.max.apply( Math, array );
};
Array.min = function( array ){
return Math.min.apply( Math, array );
};

By using JavaScript's .apply() method on a built-in function you can pass in an array of unlimited arguments.

Shannon Norrell

Friday, September 5, 2008

Line 0 Object Required Internet Explorer Javascript error - aka the dangers of using CSS Expressions

One of my favorite mechanisms to do IE-specific CSS is to use the so-called dynamic properties available to IE5+ browsers. Firefox and Opera ignore these tags, so if, for instance you need a div of id "foo" to be 110 pixels in IE and 100 pixels in all other browsers, you would just do this:

#foo {
width: 100px;
width: expression('110px');
}

I spent the better part of the day trying to track down a Line 0. Object Required Javascript bug that, when hit, required me to close IE using Task Manager. I could not figure this one out and finally resorted to reconstructing the entire page, line-by-line until I found the problem.

I finally determined it occurred only when I added a <textarea> tag to the page. My current project is a Ruby on Rails app, which necessarily indludes the Prototype Library, so I initally wrote off this weirdity to the vagaries of Prototype and its many tens of thousands of lines of code.

In this context, an input type=text tag would do fine, so I switched to that and moved on.

Quite by accident, I stumbled across this section in my CSS file:

.WordBubble textarea {
overflow:hidden;
border:solid 1px lightBlue;
height:30px;
width:expression(document.getElementById('WordBubble').offsetWidth-66);
}

And WHALA, the answer!

Thinking to reuse a WordBalloon construct I had developed, I had modified this CSS to play off of .WordBubble the className vs the original #WordBubble the ID. Therefore, when I used the original:

<div id="WordBubble" class="WordBubble">
<textarea>
</div>

things worked as expected.

However, when using:

<div id="AnotherWordBubble" class="WordBubble">
<textarea>
</div>

The CSS, when evaluating the dynamic property, could not find "WordBubble" as it did not exist.

Although I realize this posting is somewhat confusing (I barely understand it myself), what I am trying to say is "When you get a Line 0, Object Expected" Javascript error and can't figure out where it comes from, check your CSS for Expression use.

Shannon

Tuesday, March 18, 2008

parseInt Bug in Firefox, IE6 and IE7

Quick Entry:

There is a bug in shipping versions of Internet Explorer and Firefox wherein if you attempt to use parseInt on a number with leading zeros, the value returned will be incorrect for "08" and for "09" due to the Javascript interpreter thinking these are, in fact, Octal numbers (and there being no "Octal" value greater than 7, the value returned is "0").

Try it.  Copy/Paste the following Javascript into your browser's url:

javascript:alert(parseInt("08"))

Anyway, a somewhat esoteric bug perhaps but it did cause an infinite loop today when I was attempting to scan over a date range where the dates were stored with leading zeroes.

The fix?

USE parseInt( var, 10) instead.

The second parameter ensures that the parseInt operation will be done using base10

Tuesday, March 11, 2008

fetchNVPValue - routine to extract a value from a querystring

There are probably more efficient - and more bullet-proof - ways to do this, but figured I had better capture this before I forgot that I wrote it.

Shannon Norrell

////////////////////////////////////////////////////////////////////////////////
//
// fetchNVPValue( string aNVPName, string aURL )
//           Extracts the value of a Named Value Pair (NVP) from a URL
//       eg: fetchNVPValue( "name", "http://mypage.com?id=123&name=Shannon&foo=bar" ) returns "Shannon"
//
////////////////////////////////////////////////////////////////////////////////
function fetchNVPValue( aNVPName, aURL ) {
  var retVal = "";
  var bItsInThereSomeWhere = (aURL.toLowerCase().indexOf(aNVPName) > -1);
  // If we have a URL, a name to look for and it's in there somewhere ...
  if ( aNVPName && aURL && bItsInThereSomeWhere ) {
    var theURL = aURL.toLowerCase().replace(/\+/g, ' ');    // Turn all '+' signs back into spaces, if applicable
    var args = theURL.split("&");
    for (var i=0;i<args.length;i++) {
      var nvp = args[i].split("="); // Break out each argument out into a NVP
      var name  = unescape(nvp[0]);
      if (name==aNVPName) {
        retVal = nvp[1];
        break;
      }
    }
  }
  // If we still haven't extracted it and it's in there somewhere, the Named value follows a "?" in the URL, rather than a &
  if (( retVal == "") && bItsInThereSomeWhere) {
    if (aURL.toLowerCase().indexOf("?" + aNVPName) > -1) {
      retVal = aURL.split("?")[1].split("=")[1];
    }
  }
  return retVal;
}

Sunday, December 9, 2007

Data transmission and rendering techniques implemented over a client-server system

I was writing some Javascript the other day that accepted a JSON structure assembled on the server and generated HTML client-side.

The whole process seemed like deja vu and I remembered that I had come up with such a technique back in 1999 while working for Webvan.  I called it "client-side ASP" and what it did, exactly, was to accept just the data formatted for use by Javascript as:

var data=[
  {"id":1234567,"d":"Purple Ketchup","p":2.89, "q":4,"i":"1234567sm.jpg"},
  {"id":1234527,"d":"Pink Ketchup","p":2.29, "q":2,"i":"1234527sm.jpg"}
  ];

etc.  That format is now commonly called JSON, I think.  I am not sure what the exact timeline is/was when that term was coined but that is the exact format I used back in 1999 to transmit compressed data over the internet to client-side Javascript that then parsed (or rather eval'ed) the data and then used the data to generate the repetitive HTML required to display it. 

Recall that Webvan was like Amazon or even eBay in that it had many products that would be displayed under a particular category of food, so the HTML (in those days TABLEs) was of a known structure.  Using this technique (of just transmitted the data rather than the HTML and then generating the HTML on the client) reduced the average data required to display a page from, say 50KB to .5KB - or a hundredfold compression.  That's pretty significant, in my book.

If you are going to implement this technique, obviously, do not use multiple document.write's as that is generally considered "bad manners."  Rather, generate one bigString containing all of the HTML you like and then do one foo.innerHTML=bigString at the end.

This is basic stuff, IMHO, but I rarely, if ever see this technique used on the internet.

Here is a link to my patent:

Data-transmission-and-rendering-techniques-implemented-over-a-client-server-system


It's funny how patents go.  None of those other guys listed had anything at all to do with this technique.  they just happened to be in the same building, I guess.

Shannon Norrell


Never miss a thing. Make Yahoo your homepage.

Tuesday, October 23, 2007

Answers to Meebo Puzzlers

A company named Meebo was looking for a "Javascript Ninja".

Being the Javascript Ninja Master that I am (and a humble one , too), I was sought out by a headhunter to interview for the role. I checked them out and thought they were doing some cool DHTML things, so I agreed to interview with them.

Meebo gives a set of "puzzler" questions to candidates to test their Javascript acumen before speaking with them.

A couple of the questions were pretty good ones that made me go "hmmmmmm...?".

I believe I answered them as close to correctly as possible. The interview went well but it became apparent that Meebo was really interested in a Full-Time Employee (FTE) rather than a contractor, which is what I was looking for at the time (due to the whole circumnavigation thing).

Anyway, the puzzler questions, and my answers are presented here.

My apologies to Meebo if they have to come up with fresh ones due to this posting!

Shannon Norrell

Puzzlers


(1.) When does div.setAttribute(”###”) not equal div.###?

Having never found much use for setAttribute() - I always use direct assignment (div.foo=###) - I put together a "test harness" of sorts to test the behavior of setAttribute() so I could answer this question intelligently. Here is the code:

//////////////////////////////////////////////////////////////////////////////////
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<style type="text/css">
#sampleDiv, #sampleDiv2 { background-color: #FF0000; height: 40px; }
button { background-color: #CCCCCC;}
.comparison { float:left; border:solid 1px green; width:48%; top:0px; margin:5px;}
</style>
<script type="text/javascript">
function doSetup() {
var div = document.getElementById("sampleDiv");
var aStr = "sampleDiv<br/>DIV.Property= example DIV <br/>";
div.innerHTML = aStr;
div.arbitraryProperty="Hello";
div.arbitraryFunction= function() { alert('This is a test'); }
div.onclick = function() { alert('sampleDiv onClick'); }
div.style.backgroundColor='#00FF00';

var div2 = document.getElementById("sampleDiv2");
var aStr = "sampleDiv2<br/>DIV.SetAttribute() example DIV <br/>";
div2.innerHTML = aStr;

if ( navigator.userAgent.toLowerCase().indexOf("firefox") != -1 ) {
div2.createAttribute('arbitraryProperty');
div2.createAttribute('arbitraryFunction');
div2.createAttribute('onclick');
div2.style.createAttribute('backgroundColor');
}
div2.setAttribute('arbitraryProperty',"Hello");
div2.setAttribute('arbitraryFunction', function() { alert('This is a test'); });
div2.setAttribute('onclick', function() { alert('sampleDiv2 onClick'); });
div2.style.setAttribute('backgroundColor','#00FF00');
}

function doEnumeration( anHTMLElement, anOutputContainer ) {
var div = document.getElementById( anHTMLElement );
var aStr = "Enumerated Properties of " + anHTMLElement + "<br/>";
for (property in div) {
aStr += anHTMLElement + '.' + property + ' = '+ div[property] + '<br/>';
}
document.getElementById( anOutputContainer ).innerHTML = aStr;

}

function showDiv2Stuff() {
var div = document.getElementById('sampleDiv2');
alert(div.arbitraryProperty);
}
</script>
</head>
<body onload="doSetup()">
<div class="comparison">
<button onclick="doEnumeration('sampleDiv', 'valuesProperty')">Click to Enumerate</button>
<div id="sampleDiv"></div>
<div id="valuesProperty">Property Values</div>
</div>
<div class="comparison">
<button onclick="doEnumeration('sampleDiv2', 'valuesSetAttribute')">Click to Enumerate</button>
<div id="sampleDiv2"></div>
<div id="valuesSetAttribute">Property Values</div>
</div>
<button onclick="showDiv2Stuff()">
show sampleDiv2 stuff</div>
</body>
///////////////////////////////////////////////////////////////////////////////////////////////

I tested in IE7 and in Firefox 2 and my findings were that using setAttribute() does not work at all as expected in Firefox (even after adding conditional code to use createAttribute() before calling setAttribute() for FF).

So the short answer for this question would be:
div.setAttribute(”###”) does not equal div.### when running under Firefox
It is also interesting to note that arbitrary property assignments appear at the top of the DOM stack for Firefox and at the bottom in IE. Not sure what the implication of this is, as the assignment values resolve identically for either browser.


(2.) What’s the difference between these two statements:

a. var x = 3;
b. x = 3;

a. has local scope, b. has global scope


(3.) What’s the difference between:

a. !!(obj1 && obj2)
b. (obj1 && obj2)


a. will always evaluate to true (why you would have this line of code I can't imagine)


(4.) Write a one-line piece of JavaScript code that concatenates all strings passed into a function:
function concatenate(/*any number of strings*/) {



var string = ""; for (var i=0; i < arguments.length; i++) { string+=arguments[i]; };return string;}

Note - if there were a way to cast the Arguments object into a String object, one could then use .join("")



(5.) What do these two examples have in common?
Example 1:
var obj = document.getElementById(’adiv’);
document.getElementById(’adiv’).ptr = obj;

Example 2:

function assignClick() {
var el = document.createElement(’div’);

function handleClick() {
el.innerHTML = ‘clicked!’;
}

el.attachEvent(”onclick”, handleClick);
}


They both append values to the DOM
They both have potential for leaking memory
They both create closures
(actually Example 2 is a true closure - if it were this.innerHTML = 'clicked' it would be better)
(Example 1 might be called a quasi closure, although I have never seen it named as such)


Shannon Norrell 08/17/2007