No Mod Required

Archive for the 'tips-and-tricks' Category

Why Did I Never Try this Before? CSS Font-Size : 0 Hides Input Button Text

Support: This technique is supported by IE 5.+, Firefox (and the rest of the Mozilla family) and Safari 3.0*. It is not supported by Opera** (any version), Netscape 6.2 and earlier or Camino.

Warning: I haven't thought this all the way through, so if you see any problems with this technique other than the above support caveats, then please let me know :) I'm really just writing this up because I just thought of doing it, I tried it and it worked…

The issue and solution: Occasionally I need to use an image, with text, for the background of an input button. It's rare, but occasionally it needs to happen. Thing about that is, I still want to have a regular value in there so that screen readers and people surfing without CSS at all can still figure out what the input says. I've usually encountered this issue on on fast-paced, get-em-out-the-door projects, so I've never had time to actually sit down and think about a solution. I've never actually come up with a solution that I liked for this. Negative text indent doesn't work, so I've always done random hacks to get it to work correctly. Recently I achieved it by shrinking it way down and coloring it the same size as the background.

Painful.

Today, for whatever reason, I took it one step further and set font-size:0;, hit F12 and saw, *GASP*, it worked. Browsercam verifies that it works for the above named browsers.

Here's a sample:

And here's the code for the above #go button.

#go {
	background: url(images/go.jpg);
	height: 25px;
	width: 41px;
	font-size: 0px;
        border:0;
}

This is one of those things that seems so obvious in hindsight. I just never thought to do it before today. hrm.

*Safari 1.2 and 2.0 ignore both the font-size AND the background image.
**Opera displays something like 6pt type.

JSON Feeds For Fun and Profit Part 3- wherein Eval() kind of bums me out

(and several months later I finish my little JSON series…)

So far my exploration of JSON has been a fun-filled walk in the park. Moonbeams and rainbows. All that.

This last post on the subject is slightly less cool as I get into one of the least attractive components of the whole JSON thing- the use of eval() to transform a text response into a proper JavaScript object. The use of eval() is one of the reasons I originally was a little shy about using JSON. Why? eval() is slow and I try to stay away from slow if at all possible. That and the idea of eval()-ing code from some third party makes me wary.

Obviously, if you're using a callback, or are just using a built in object reference which inserts itself into memory as soon as the script is attached a script to a page, eval() won't come up, but sometimes there's no other option but to ingest the JSON feed as plain text and eval() it into Object-hood. The feed I was helping out a co-worker with last week (AKA back in August when I originally wrote this) which spurred on this very series of posts was one such feed. It looked something like this example cribbed from JSON.org:

{
    "glossary": {
        "title": "example glossary",
		"GlossDiv": {
            "title": "S",
			"GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
					"SortAs": "SGML",
					"GlossTerm": "Standard Generalized Markup Language",
					"Acronym": "SGML",
					"Abbrev": "ISO 8879:1986",
					"GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
						"GlossSeeAlso": ["GML", "XML"]
                    },
					"GlossSee": "markup"
                }
            }
        }
    }
}

As you can see, if you attach that script to the document using a script tag, there's absolutely no way to get anything out of it. No amount of poking at it will communicate the contents of that file to the rest of the scripts on the page. There's no reference in memory and there's no way to create one. It has to be ingested using an XMLHttpRequest and eval() has to bring it into life. Since it uses XHR and not a dynamic script tag, that means a local pass-though script on the server side is needed to get around cross site scripting constraints. In other words, we're looking at a big pile of "why bother?" For my money, if you have to go through these steps, then plain old XML makes more sense.

Anyway, if you have to use JSON and the only option is a response structured like the above, then the following is one way to handle it.

First, an example "tag cloud" hacked together with a raw JSON tag feed from del.icio.us:

Now some code:

<script type="text/javascript">
function ajaj(json_doc,callback) {
  //ajaJ? get it? so clever.
  //create a cross browser XMLHttpRequest object
  //let's do this
  	http= new function() {
  	var xmlhttp;
  	try {
  	//start with the standard
 	 xmlhttp = new XMLHttpRequest();
 	}
 	catch(e) 
  	{
 	 //do the IE thing if the above fails
	  //IE deserves some props since they actually created 
 	 //the whole XMLHttp thing
  	xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  } 
  return xmlhttp;
  };
  //is it okay to try this?
  if((http.readyState==0)||(http.readyState==4))
  {
  //I do this in a try/catch since  there's 
  //always a chance of some 
  //sort of data error and I want the 
  //possibility of using a fallback function
  //if there is some sort of internet hiccup. 
  //I also hate showing errors to the user
  //In this case I could just drop it since
  //I'm not doing anything special here, but 
  //I'm not about to reinvent the wheel
  //since this is more about JSON 
  //than the way I use XMLHttpRequest 
  try 
  {
  //open the connection
  http.open("GET", json_doc, true);
  //set up a function to handle the response
  http.onreadystatechange = function handle_http_response() 
  {
  //I take no chances.
  //you can have have readyState of 4, a status 
  //of 200 and still run into trouble
  //I want to know all my ducks are in a row :) 
//I'm all belt and suspenders 
  if ((http.readyState == 4)&&(http.status==200)&&(http.responseText!=null)) 
  { 
//First I create an empty object, the same structure as the regular Delicious JSON feed
//I like the structure of their feeds so I'm replicating them here.
  Delicious = {}
  //Use eval to bring the response into the Object
//Again, I'm using Delicious' own Delicious.tags structure
  Delicious.tags=eval('('+http.responseText+')');
  //we've got this handy object now that we've done our eval
  //we pass it as an argument to the callback function
  callback(Delicious);
  }
  }
  }
  catch(e) 
  {
  //we could do some stuff here to handle errors
  }
  //let's put an end to this transaction
  http.send(null);
  }
  }
  //this is the callback function
  function delicious_me(Delicious) {
  //this is an object functioning as 
  //an associative array
  //not a proper indexed array typical of most JavaScript
  //so we use a for... in loop
  //which loops through all the members of the array by named reference
  for (var i in Delicious.tags) {
  //inside the loop i will refer to the property name
  //tags[i] will return the value
  //BTW, the tag cloud is a total hack. 
  //I should do some better math here, 
  //but like the XHR stuff above 
   //this isn't really about a tag cloud...
  document.getElementById("delicious").innerHTML+="<a href='http://del.icio.us/rob_react/"+i+"' style='font-size:"+Delicious.tags[i]/2+"px;line-height:"+Delicious.tags[i]/2+"px;'>"+i+"</a>\n";
  } 
  }

The interesting bit, at least in the scope of this article, is this where we use the eval method to pull the responseText into the Delicious and pass it to the callback function.

Delicious.tags=eval('('+http.responseText+')');
callback(Delicious);

Nothing groundbreaking there and, honestly, not much for me to get excited about. I just don't like the use of eval and would go to great lengths to avoid using it. That said, if you're in a situation where a raw JSON feed is all you've got to work with, this is a way out of the jam.

For my next, and final entry in this I'll use this handy JSON parser for the safe alternative to eval(). That should make me a little happier than this example.

Belt and Suspenders- Flash Embed With SWFObject and Conditional Comments

If you're using Flash and you want the best possible coverage (meaning it works with users who don't have JS turned on) while still using something like SWFObject where possible to get around the "click here to activate and use this control" ActiveX message in Internet Explorer, then take a look at the ridiculous pattern below.

Warning- not for the squeamish…

Here's the HTML:

<!--[if IE]>
   <noscript> 
   <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0" width="1004" height="281">
      <param name="movie" value="_assets/flash/homepage.swf" />
      <param name="quality" value="high" />
      <embed src="_assets/flash/homepage.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="1004" height="281"></embed>
</object>
</noscript>

<![endif]-->
<!--[if !IE]> <-->
   <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0" width="1004" height="281">
      <param name="movie" value="_assets/flash/homepage.swf" />
      <param name="quality" value="high" />
      <embed src="_assets/flash/homepage.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="1004" height="281"></embed>
   </object>
<!--> <![endif]-->

Here's the SWFObject script served to Internet Explorer and ONLY Internet Explorer

<!--[if IE]>
<script src="_assets/js/swfobject.js" type="text/javascript"></script>
<![endif]-->

And here's the SWFObject call:

        //use conditional compilation to hide this call from non-IE browsers
	/*@cc_on @*/
        /*@if (@_win32)
	if ($("home")){
		var so = new SWFObject("_assets/flash/homepage.swf", "mymovie", "1004", "281", "8", "#336699");
	   	so.addParam("wmode", "transparent");
	   	so.write("messaging");
	}
	/*@end @*/

Before I go further, let me just say I'm counting the days until I can just dump a SWF into the page and be done with it. I hate all of these script based machinations to get a SWF out to the browser. This is code overhead that really bugs me…

Anyway, here's the HTML logic.

  • We use Microsoft's conditional comments to show/hide content
  • In the "This is IE" block we use a noscript tag to present a traditional embed to IE users who might have JS turned off.
  • Every other IE user with JS turned on will see the SWF embedded by SWFObject so no "click to activate and use this control" will be visible to the user. Since the majority of people on the web use IE and the majority of them surf with JS turned on, most users will fall into this category.
  • Then, in the "This is not IE" block, we just embed the Flash the old school way. Since the Eolas suit doesn't come into play with other browsers, embedding the SWF the old way is fine (at least in terms of the "click here…" nonsense.) As an additional benefit there's no concern over whether or not the user has JavaScript turned on. JavaScript or no, the only issue is whether or not they have the plugin. Whereas with SWFObject there's a possibility (however slim) that a user could have the Flash plugin installed but be surfing with JavaScript disabled. As I mentioned, this is for the best possible coverage… Also, if they don't have the plugin they get the "download this plugin" notification. I head reports that that wasn't happening with SWFObject running* so getting that to work properly is definitely a bonus. It was actually that bug report that got me into this whole mess :)

As for how it works technically, the following

<!--[if IE]>

Is read by IE as a test (if the browser is Internet Explorer.) Every other browser reads it as an open comment. Since it's an open comment everything up until the close comment (<![endif]–>) will be ignored by non-IE browsers. IE, on the other hand will go ahead and process that markup normally since it recognizes that pattern as a conditional and its test will evaluate to true.

As a note, I don't like using this stuff all over the place**, but in a situation like this conditional comments are a great option to have.

On the flip side All other browsers read this pattern:

<!–[if !IE]> <–>

As a completed comment, opened and closed on the same line. Therefore everything that follows is rendered normally by Firefox, safari and all the rest. IE, on the other hand, reads it as the beginning of a "If the browser is NOT IE" conditional. Since that resolves to false, everything down to the end of the conditional endif (<!–> <![endif]–>) is ignored by Internet Explorer. That allows us to go back to the future and embed flash in the old school way for everything but IE.

Oh the pain.

Still, if you absolutely need the best possible coverage and want to use SWFObject, this is a way to go. Is it the best? Probably not, since it's horrifyingly hack-y and won't validate. But it might be useful for someone out there…

*and I could figure out how to make it happen…
**I mostly use it to attach IE specific style sheets. For those of you keeping track, that looks like this:

<!--[if gte IE 5.5]>
<![if lt IE 7]>
<link rel="STYLESHEET" type="text/css" href="_assets/styles/ie6.css" />
<![endif]>
<![if gte IE 7]>
<link rel="STYLESHEET" type="text/css" href="_assets/styles/ie7.css" />
<![endif]>
<![endif]-->

Automatically Track Outgoing Links in Google Analytics with Javascript

I'm currently reading the O'Reilly Google Analytics book, so of course I'm going to sandbox some stuff (albeit not here at this site.) One of the first things I seized on was the ability to track outgoing links by calling the urchinTracker() function onclick. Taking a few minutes out of my morning I put this together:

//window.onload we run this
var anchors=document.getElementsByTagName("a");
for (i=0;i<anchors.length;i++) {
    anchors[i].onclick=trackOutBoundLinks;
}

the trackOutBoundLinks function looks like this:

function trackOutBoundLinks(){
var thelink=this.href;
if (thelink.indexOf(location.hostname) ==-1 ){
   urchinTracker('outgoing:'+thelink+'');
   document.location.href=''+thelink+'';
   return false;;
   }
}

And with that you get fancy entries in your content list like this:

"/outgoing:http://www.dccomics.com/graphic_novels/?gn=7679"

Of course, the above will skew your page views, etc. so you'll need to apply some filters and/or create a new profile, if you want to track page views in the default way in addition to tracking outgoing links, etc. but all that's a Google Analytics matter*. The biggest JavaScript caveat is that if you've got any other onclick events on your anchor tags the above will muck with them. But if not, it's that simple to start tracking outgoing links. Pretty cool, I think.

Here's a zip file with a small script file that will insert the above into any page it's attached to. I might write a more generic version of this that will work in any environment if I get the time, but for now if people are interested here it is to play around with. Simply upload the analytics.js to your server and attach it to the page in question and it should work:

<script src="/your-path-to/analytics.js" type="text/javascript"></script>

*albeit a pretty big one. I can't stress enough that this code will completely skew your content reports. You need to have two profiles- one which filters against outgoing: and one which doesn't. The one which does will track page views in the normal way, the one that doesn't will have all the fancy outgoing links interspersed with the content reports. I am not savvy enough with analytics to solidly answer questions about that side of this business, so if you're unsure look to some Google Analytics expert for sage advice on the ins and outs of profiles and filters. Maybe after reading the rest of this book and sandboxing these techniques for a month or two I'll be more certain about some of these things, but for now I'm a javascript nerd who's just exploring the GA landscape.

JSON Feeds For Fun and Profit Part 2 - Callbacks with Twitter

In my first exploration of the JSON data interchange format, I used it in its most basic way possible. I attached a script (from delicious) to a page and simply used the built-in object created by their implementation to generate an unordered list of my recent del.icio.us posts. While it showed how easy it is to use the O, a handy, structured, Javascript Object, provided by JSON, it didn't really illustrate how to manipulate the page with new data after page load. That is, after all, a very now thing to do, so it's kind of important. This article will examine how to dynamically load a JSON feed and how to get data out of Twitter and into a usable form using a callback.

While one can load JSON using XMLHttpRequests, the real benefit of JSON is to be able to load data from a third party without worrying about the browser security model and without having to rely on a local pass through file. So, we're going to load the data without XHR in this example. Instead we're going to load it using dynamic script tags.

Here's the function I'm using in my example:

function twitter_me() {
   var twitter_JSON = document.createElement("script");
   twitter_JSON.type="text/javascript"
   twitter_JSON.src="http://twitter.com/statuses/user_timeline/rob_react.json?callback=twitter_callback&count=10"
   document.getElementsByTagName("head")[0].appendChild(twitter_JSON);
}

First I create a new script element in the document, then I set the type and source. The source is the interesting part. As you can see, after the URL for the JSON document representing my Twitter updates, I append a query string. The first argument for that query string (callback=twitter_callback) is where the magic happens. There I'm defining the function that will be called when the script is attached to the document. To see what happens compare the first few characters of the JSON script called with and without the callback.

Here it is, as referenced in the above code

twitter_callback([{"source":"web","text":"426 queries in 107.66 milliseconds. drupal is teh awesome",

and called without the callback (but with the count, so the data structure returned is the same)

[{"source":"web","text":"426 queries in 107.66 milliseconds. drupal is teh awesome",

Notice the function referenced in the first example. Right there, as soon as the JSON document is loaded, JavaScript executing the callback function with the JSON object as an argument. I'm a JavaScript geek and all, so I'm biased... but that's just plain cool.

So what happens next, you ask? Well, since I defined a function called twitter_callback, that function is run and I'm able to do all sorts of cool things with the provided object. Here's the function in all its glory:


function twitter_callback(twit) {

 //this is the div I'm writing the content to
 var t_div = document.getElementById("twitter");

//these are some other variables, mostly
 //placeholders so that the code is a little clearer
 var who,what,when,icon, bgcolor

//start the ul
 t_div.innerHTML = "<ul>"

   //loop through the twit object
   for (i=0;i<twit.length;i++) {

   //Look at me use the JavaScript modulus operator to do even/odd rows.
   if(i % 2) {
      bgcolor="#efefef"
   } else {
      bgcolor="#ddd"
   }

   //Right here, I've broken out the separate bits of the string into placeholder variables
   //so you can more easily see the dot notation and array indices in place
   //once you figure out the structure it's dead easy to reference data in an Object
   icon=twit[i].user.profile_image_url;
   who=twit[i].user.name;
   what=twit[i].text;
   when=twit[i].created_at.substr(0,19);

   //and here I mash it all up into a fancy li
   t_div.innerHTML +="<li style='background:"+bgcolor+" url("+icon+") no-repeat'><strong>"+who+"</strong>: "+what+" ("+when+" GMT) </li>"
   }
  //and close the UL
  t_div.innerHTML += "</ul>"
}

Here it is in action (be patient, Twitter can be slow):

Call me crazy, but that's something like 75% away from being a widget and there are no cross domain concerns to deal with so it could easily be spread far and wide. There's a lot to like right there :)

That's it for this time. Feel free to ping me with questions or comments.

Next up, as I mentioned, one can load JSON via XMLHttpRequest, so I'll be building an example that does just that. There's a wrinkle tossed in with that technique that will make things, if not tougher, a little less cool (eval(), I'm looking in your direction.) More on that when I put it all together.

JSON Feeds For Fun and Profit, Part One- Del.icio.us makes it easy

Confession time: I've never actually worked with a JSON feed.

No, really.

I know what you're thinking… Yes, you're right. I've built a bunch of Ajax-y components and one full blown application over the past two years.

No, in light of that it doesn't make any sense that I haven't used JSON before.

Anyway, I've been helping out one of our developers at work with My Big Javascript Brain™ and I've had to mess about with JSON a little bit. She's ostensibly a Java developer, but she's shown a willingness to help out wherever she can (thereby learning a bunch of other web technologies,) so this week she's been tasked with creating a JSON fed Google widget. This has been interesting for me, as I've had to guess a lot and leap at solutions at every turn to try to help her (since I have no practical experience with the acronym myself.) The good thing is I've actually been helpful. Apparently I've soaked up enough about JSON through osmosis or something, because when that feeble amount was blended with my regular JS knowledge I was actually able to piece together some of what was going on. Go me.

Anyway, to actually know what I'm doing the next time, I've decided to play around with some feeds right here and write up what I discover to help solidify my own knowledge and to help out anyone else who might be wondering about the magical world of JSON feeds.

Did I mention that they were magical? They are. They're made of pixie dust and rainbows.

Yes, I've gone slightly off the rails… ignore that last bit.

To start with, I decided to use the handy feed from one of my favorite web twenty web sites, del.icio.us. Not surprisingly, since del.icio.us kicks all kinds of ass, they make it dead easy to use JSON feeds. All it takes is attaching the script generated at http://del.icio.us/feeds/json/user_name to a document and a Delicious JavaScript object is automatically created- full of all sorts of social bookmarking goodness. Notably a posts array which contains all of the selected user's posts.

You can read their concise documentation here. They have a nifty sample as well.

Here's the example I whipped up. This grabs the last 15 bookmarks from my account, takes the link, description and tags and drops them all into a regular old unordered list*.

Here's the feed itself. As you can see it contains a simple test to see if the Object already exists, an object definition and then an array populated with all of the post data. Pretty straightforward, really.

The following code sample shows how I did it. My comments are inline and in blue.


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>JSON part 1</title>

<!--Seriously. This is all it takes to get the object into your document. 
That's really cool, right? I think so-->
<script type="text/javascript" src="http://del.icio.us/feeds/json/rob_react"></script>
<script type="text/javascript">
window.onload = function() {

//this is the built in delicious object. 
//the array posts has all the stuff we're looking for. 
//let's make a quick reference to it
var del = Delicious.posts;
var d_div = document.getElementById("delicious");

// create a placeholder for the link
var the_link=""

//****As a note, I've found writing out innerHTML 
//****to be faster, so I use it. 
//****I actually prefer the elegance of JavaScript's 
//****DOM based node creation and manipulation methods.
//**** but speed roolz, so I play with strings
//**** sometimes this stuff can get really confusing, 
//**** though, so I'll fall back and use
//**** the DOM stuff anyway. 
//**** Anyway, back to the script...

//write the beginning of the <UL>
d_div.innerHTML = "<ul>"

//loop through every element in the Delicious.posts object and do stuff
for (i=0;i<del.length;i++) {

//look at me, I'm doing stuff!
//clear the placeholder for the link
the_link="";

//  add in the opening of the LI and the opening of the anchor
// add a reference to the u (url) of the i member of the del array
// and the d (description) of the same memberr
the_link ="<li><a href='"+del[i].u+"'>"+del[i].d+"</a>";

//are there tags? is there a tags array of the i member?
if (del[i].t) {

//if so lets write them out
the_link +=" <em>tags:</em> "

//loop through all the mambers of the t (tag) array
for (j=0;j<del[i].t.length;j++) {

//and build another link
//here I'm using the simple, but powerful URL scheme
//that delicious uses to nice effect
//by dropping the the tag onto the end of my delicious url
//I'm easily building out links to my tags
//they use a logical, transparent url scheme which makes 
//it crazy easy to navigate both as a user and as a programmer
//go delicious go.
the_link +="[ <a href='http://del.icio.us/rob_react/"+del[i].t[j]+"'>"+del[i].t[j] +"</a> ]" 
}
}

//close out the li
the_link += "</li>"

//pop that link and li into the div
d_div.innerHTML += the_link
}

 //once the loop is done, close the ul
d_div.innerHTML += "</ul>"

//and that's that. like I said, pure magic.
}
</script>
<style type="text/css">

/*I've got style*/
body {
color:#666;

/*by the way, shorthand is your friend!*/
font: .825em/1.8em Verdana, Arial, Helvetica, sans-serif;
}
em {
color:green;
margin-left:10px;
}
</style>
</head>
<body>
<div id="delicious">
</div>
</body>
</html>

And that's that- a run-through of the simplest possible usage of a JSON feed (at least in terms of extracting the data.) Next time (which should be soon) I'll look at different methods for getting dynamic data out of JSON. Oh, the fun we'll have.

(part 2 of this series | part 3 of this series)

*The note is also available- I'll use that in my next example.