Well, kidzies, them eggheadcafe.com
folks done relented and let me back again, to share some cool stuff with
y'all! Now the first think I want to share is a cool trick you can use
with the Internet Explorer userdata persistence default
(that means, "built-in") behavior. Now different browsers "behave"
in different ways, and the last time I checked the stats, the overall
usage for Internet Explorer out there in the bandwidth world is running
over 96.6 percent! Damn! Yessir, dis means y'all can seriously consider
writin' code fer IE only, you can tell them "other" guys ta
"take a hike", and basically yer finally gonna be on safe ground
with all this "Browser compatibility B-S!! (That is, unless the user
happens to work fer SUN, Oricle, or one a doze weirdo companies that tell
all their people if they ever catchum usin' anything but Nutscrape they
will get their "nuts scraped").
OK, now I'm going to get serious:
UserData is a kind of CSS style behavior. There are
four or five of them "built in" to IE, but really, this one
is the most interesting. Now a guy on this site wrote a couple of articles
about this stuff some time ago, and I don't want to beat a dead horse,
but I think he may have missed part of the point; let me give you a very
big tip:
If you are writing apps that don't use HTTP (in other words, they are
designed to come up from the local filesytem in Internet Explorer) then
if you plan on using HTTP cookies YOU ARE GONNA BE PLUM OUTTA LUCK!. Cookies
don't work off of "FILE:///", period! Oy, vay!
So now that we've recovered from a bad case of Debuggus Interuptus
, what we gonna do, call CookieBusters? Nope! We
gonna use "UserData", which is way cool better than cookies
ANYWAY! It doesn't care whether its HTTP, FILE, GET, PUT or BLOG! It "just
works"! Damn! Slicker'n snot on a DOORKNOB!
Now lots of people use this as a single element behavior, and they end
up with a whole buncha files gumming up the user's machine in the C:\Documents
and Settings\<loggedOnUserName>\Application Data\Microsoft\Internet
Explorer\UserData folder, and they make a real mess of
the whole thing. Typical novice programmer behavior! However, if you do
a little study and remember to remove your foot from your mouth before
you go to show off everything you know, you will find that the userData
element that you assign this behavior to exposes an xmlDocument
property. This document has the root tag "rootstub"
already built it, and every time you assign a new attribute to it, it
just gets added as a name / value attribute pair. You can't add elements
to it, only attributes. Microsoft sez you can stick up to 64K
worth of your USELESS JUNK in there PER PAGE, and up to 640 K
worth of your useless junk per domain! Man! I smell a cookie factory on
steroids here! Take a bath, at least, after you leave!
Now anybody that's had the chance to meet or talk with Dr. Dotnetsky
in between hops on railroad cars drinking Absolut Martinis will know that
he prefers to illustrate rather than pontificate.
So the best way to see how this stuff all works is: 1) look at some sample
code, just below and, 2) have a working set of test pages (just below
that!). I swear I didn't inhale!
UserData Persistence Behavior Redux [How it works - all on one
single HTML page!]:
<HTML>
<HEAD>
<STYLE TYPE="text/css"> .userData {behavior:url(#
default#userdata);}</STYLE>
<script>
window.onload = loadUserData;
window.onunload = saveUserData;
function showUserData()
{
alert(spnUserData.xmlDocument.xml);
}
function loadUserData()
{
spnUserData.load("userData");
}
function saveUserData()
{
spnUserData.save("userData");
}
function addUserDataItem(sName, sValue, iExpiryMinutes)
{
try{
var oTimeNow = new Date();
oTimeNow.setMinutes(oTimeNow.getMinutes() + iExpiryMinutes);
var sExpirationDate = oTimeNow.toUTCString();
spnUserData.expires = sExpirationDate;
spnUserData.setAttribute( sName,sValue);
spnUserData.save("userData");
}
catch(e)
{
return e;
}
}
function getUserDataItem(sName)
{
if(spnUserData.getAttribute(sName)==null || spnUserData.getAttribute(sName)=="") spnUserData.load("userData");
try{
return spnUserData.getAttribute(sName);
}
catch(e)
{
return e;
}
}
function removeUserDataItem(sName)
{
spnUserData.removeAttribute(sName);
}
</script>
</HEAD>
<BODY>
<SPAN CLASS="userData" ID="spnUserData" style="display:none;" ></SPAN>
<h3>UserData Tester Page One</h3>
<input type="text" id="DataName" >Item Name<BR>
<input type="text" id="DataValue" >Item Value<BR>
<input type="text" id="exp" value="1000000">Expiration(Minutes)<BR>
<input type="button" value="add item" onclick="addUserDataItem(DataName.value, DataValue.value, exp.value);">
<input type="button" value="remove item" onclick="removeUserDataItem(DataName.value);">
<input type="button" value="Show Data" onclick="showUserData();">
<a href="userdatatester2.htm">Page Two</a>
</BODY>
</HTML> |
UserData Persistence Behavior Redux [Try it out, man! You got
this far, didn't ya?]:
Page
One (it has a link to Page Two, which is essentially identical). View
Source to see ALL THE CODE. (OK Posters and Emailers, let me make it a
little clearer: RIGHT CLICK Anywhere on the Page and choose the "View
Source" context menu option [they complain why didn't we provide
the source code for a 100% client side script page - DUH!]).
So now you have a way to store relatively large amounts of data on the
client, it doesn't matter whether you brought up the HTML page by double
clicking it on the user's file system or as a shortcut because that's
where your WISE, Installshield or other installer program deposited your
junk, or whether they clicked a link that made an HTTP call. It's gonna
work EXACTLY THE SAME! Yay!
Debugging Functions
OK, now lets' switch gears a little and talk about debugging JAVASCRIPT
(JSCRIPT, SCRIPT, Javascript1.2, you name it).
I'm not going to explain a lot about this because I took some time to
put together a page with a JS include file that really illustrates all
the concepts. You have the window.onerror built-in method illustrated
(try changing the debugMode variable from true to false, etc to see the
action). This is extremely useful for zeroing in on the exact line where
an error has occurred, and as you can see it's easy to turn on and off.
Don't forget you can also go into IE in the Tools/Internet Options/Advanced
and clear the "Disable Script Debugging" checkbox too.
You have the "showSource" method I've put together which iterates
the DOM and displays it in a window, which can be invaluable for dynamic
HTML assembly (I use it to display the results of XSLT transforms that
are dynamically assigned to the <BODY> element or even where you
have multiple IFRAMEs and you assign the ENTIRE HTML document to the source
of one of the IFRAMEs as a result of a transform). Next, the showSource
method automatically exposes a clipboard copy method so you can paste
your stuff into EDIT PLUS or your favorite text editor from the clipboard.
And finally, the "debugger" Javascript keyword shows how to
bring up the JIT Debugger at a specific point in your code. Now I don't
know which debugger you use, but Dr. Dotnetsky likes Visual Studio.NET
2003 because it allows you to step through your code with F11, examine
the values of locals and watch variables, print (? XXX) values and expressions
to the Command Window, and everything else - even in a crummy client-side
Javascript page! The only part that doesn't seem to work well is the Intellisense
when you mouse hover over a variable and get to see its value.
There aren't too many other tricks for debugging client - side script
than these. If you have one, please DON"T HESITATE to post your comments
or suggestions on our eggheadcafe.com
forums!
<HTML>
<head>
<script language="JScript" src="Debug.js"></script>
<script>
var debugMode=true;
var debugMe=true;
window.onerror=showError;
function showError(Moe,Larry,Curly)
{
if (debugMode) alert(Moe +"\n in: " + Larry + "\n at line: " + Curly);
return true;
}
var test="YO!";
function showMe()
{
show.innerHTML=test ;
}
function doDis()
{
if(debugMe)debugger;
show.innerHTML=test ;
}
</script>
</head>
<BODY onload="showMe();">
<table Cellspacing =2 cellpadding="2">
<TR><TD>HELLO</TD><TD>HOWDY</TD></TR>
</TABLE>
<input type="button" value="do it with error" onclick="showSource(ko);">
<input type="button" value="do it right" onclick="showSource();">
<input type="button" value="Debugger" onclick="doDis();">
<div id='show' />
</BODY>
</HTML> |
//save as debug.js//
function showSource(){
var doc_stuff="";
for(i=0; i<document.all.length; i++)
{ // NOTE: Next line prevents recursion...
if (document.all[i].tagName.toUpperCase() !="BODY" &&
document.all[i].tagName.toUpperCase() !="HEAD")
{
doc_stuff+= document.all(i).outerHTML;
}
}
openWindowAndCopy(doc_stuff);
}
function openWindowAndCopy(strText) {
winBS='toolbar=no,location=no,directories=no,menubar=no,';
winBS+='scrollbars=yes,width=650,height=500';
winBS+=',left=10,top=25';
holyCow=window.open("","",winBS);
holyCow.document.open();
holyCow.document.write("<scr"+"ipt>function copy(){stuff.focus();stuff.select(); window.clipboardData.setData('Text', stuff.value);}</scr"+"ipt>");
holyCow.document.write("<input type=button value='Copy To Clipboard' onclick='copy();'> <input type=button value='Close' onclick='javascript:self.close();'><BR>");
holyCow.document.writeln ("<textarea id='stuff' rows='60' cols='75'>");
holyCow.document.write (strText);
holyCow.document.writeln ("</textarea>");
holyCow.document.close();
holyCow.focus() ;
}" |
Dr. Dexter Dotnetsky is the alter-ego of the Eggheadcafe.com forums, where he often pitches in to help answer particularly difficult questions and make snide comments. Dr. Dotnetsky holds no certifications, and does not have a resume. Always the consummate gentleman, Dr. Dotnetsky can be reached at youbetcha@mindless.com. Dr. Dotnetsky's motto: "If we were all meant to get along, there would be no people who wait for all the groceries to be rung up before starting to look for their damn checkbook."
|