The Strange Zen Of JavaScript View RSS

We got your Ajax right here.
Hide details



Why must we still close SCRIPT elements? 17 Feb 2011 2:00 PM (14 years ago)

I was wondering why in the world we still have to terminate SCRIPT elements with </script> tags in 2011, fer Pete's sake.

i.e. why does this work:

<script src="foo.js"></script>

but this:

<script src="foo.js" />

does not?

For years I thought it was just a bad implementation from the NS4 days that nobody bothered to correct. Not so! Turns out there's a valid (if a bit irritating) reason.

The tl;dr answer is: <script /> is valid XML, but invalid HTML.

If you serve your HTML pages as text/html, a browser will likely use its HTML parser to render the page. And since <script /> is invalid HTML, the HTML parser treats it as unrecognized junk.

(In theory, if you serve your pages as text/xml or application/xhtml+xml, the browser would use its XML parser, recognize the <script /> as valid, and load/execute the associated JS. But that also presumes your documents are also perfectly-formed, valid XML and not tag soup. Can you make that claim?)

As to the question of why <script /> is invalid while <img /> is perfectly okay:

The XHTML spec (and therefore HTML5, which imports the element definitions from the XHTML DTD) defines the SCRIPT tag as containing "document text." It cannot, by definition, be "empty."

SCRIPT is often considered analogous to the IMG element -- and mistakenly so -- because they both use the "src" attribute. In reality, SCRIPT is more akin to the P (paragraph) element, which also cannot be empty (writing <p /> is invalid -- you must instead write <p></p>)

For comparison, here are the starting element definitions from the XHTML DTD:

<!ELEMENT script (#PCDATA)>

<!ELEMENT img EMPTY>

This is why <img /> is fine, while <script /> is a no-no.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

HTML 5 audio player demo 31 Jan 2010 3:48 PM (15 years ago)

To teach myself a little about HTML 5 and its built-in support for rich media, I built a proof-of-concept HTML5 audio player, written entirely in HTML 5, JQuery and CSS 3. It more or less replaces all the functionality of the Flash audio player on my music site.

It took a little over two hours starting from nothing. Developed it in Safari 4.0 and works great with that browser. Haven't yet tested it in Firefox 3.5, which claims HTML 5 audio/video media support as well. (UPDATE: Firefox doesn't support the MP3 format. You're stuck with Ogg or WAV.) It "works" in Mobile Safari on my iPhone 3G, but it's not pretty: the audio element is visible and the MP3 launches a Quicktime player.

Think what you want about the iPad and Apple's non-support of Adobe Flash. If you're a web developer and not excited about creating rich media experiences with HTML, Javascript and CSS, you just might be a dried-out husk.

Feel free to hack this code. You can send screenshots (links only, please!) and bug reports to me here.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Using the "in" operator in conditionals 28 Oct 2009 2:30 PM (15 years ago)

Most of us are familiar with the for..in loop construct:

for (var i in object) {
   /* some iterative action here */
}

Less well-known is the use of "in" as an operator to test if a named property exists on an object:

>>> var x = {foo:1, bar:2, baz:'cat'};

>>> ('foo' in x)
true

>>> ('bar' in x)
true

>>> ('qux' in x)
false
This works on methods, too:
>>> ('toString' in x)
true

It's particularly neato for testing the existence of properties with values that could be interpreted as false:

>>> var x = {foo:null, bar:false, baz:0};

>>> (x.foo)
null

>>> ('foo' in x);
true

>>> (x.bar)
false

>>> ('bar' in x)
true

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

IE conditional comment gotcha 9 Oct 2009 4:49 PM (15 years ago)

Here's one for the books:

<!-- [if IE]>

<p>Some IE-specific stuff here</p>

<![endif] -->

This will be ignored by IE. Why?

Because IE doesn't like the whitespace between the comment delimiters and square brackets. Here's the correct way:

<!--[if IE]>

<p>Some IE-specific stuff here</p>

<![endif]-->

Note the difference:

<!-- [if IE]> (bad)
<!--[if IE]>  (good)

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

HTML 5 30 May 2009 10:05 AM (15 years ago)

The recent buzz over HTML 5 this week at Google I/O could not have come at a better time.

It reminds me of another time, back in 2005, when the economy was just starting to recover from the dotcom bust. Ajax made the scene (heavily backed by Google in the form of Suggest and Maps) and got people excited about doing cool things on the web again.

Now the economy is in the crapper again, and here comes HTML 5 (again backed by Google in the form of Chrome and Wave). Looking at some of the demos, I feel the same exhilaration I once felt when I first recognized the potential of DHTML. In many ways, HTML 5 picks up where DHTML -- or DOM scripting of whatever -- left off long ago.

I offer no analysis. It's the most excited I've been about web development in years.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Constraining elements to the browser window 3 Feb 2009 2:08 PM (16 years ago)

I recently worked on a demo that used tooltip-like "popover" menus that appeared when the mouse pointer hovered over certain page elements. The initial approach was pretty straightforward: find the offsetTop and offsetLeft of the target element, then move the menu to those coordinates.

Two challenges:

The following seemed to do the trick. Note that this is pseudo-JavaScript; I leave it to you to figure out how to actually obtain the necessary values and make the appropriate substitutions.

// element = target element that gets the menu
// menu = the menu popover DIV

var bottomEdge = element.offsetTop + menu.height - document.scrollTop;
if ( bottomEdge > window.height )  {
    menu.top = element.offsetTop - (bottomEdge - window.height);
}

var leftEdge = element.offsetLeft + menu.width - document.scrollLeft;
if ( leftEdge > window.width)  {
    menu.left = element.offsetLeft - (leftEdge - window.width);
}

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Does your website disappear in IE8? 12 Nov 2008 7:28 AM (16 years ago)

Mine did.

I was recently given a pretty large redesign project, and during the planning stages I took the opportunity to add a long-missing DOCTYPE to the site. I chose a nice HTML 4.01 Transitional flavor because there was no way in hell the site was going to resemble anything close to XHTML anytime soon. At the very least, it would 1) get the site out of quirks mode, and 2) give us an eventual path to validation.

(I'm including this info to illustrate: we had a proper DOCTYPE, and knew the site would not be valid HTML going in.)

Months after the project was complete and launched, word came down that the site was missing in IE8. Not broken. MISSING. Like, "the page is blank."

No partial loading, no sudden flicker, no error message. The site simply would not render.

I was considering hard liquor as my preferred solution when one of my co-workers mentioned something he'd heard about a new META element, something about versioning. Ah, yes, that version targeting thing.

I added the following to the global website header:

<meta http-equiv="X-UA-Compatible" content="IE=7" />

...and suddenly the site worked in IE8.

This is not a "solution." I still don't know why the site vanishes in IE8. I mean, it rendered perfectly fine in IE5, IE5.5, IE6 and IE7 so OF COURSE IT MAKES TOTAL SENSE THAT IT COMPLETELY VANISHES IN IE8. Right?

No. It does not make sense. Anyway, when I do discover the root cause, I'll post it here. In the meantime, I guess I'll be asking IE8 to dumb it down to my level.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Missing form ACTIONs 17 Jul 2008 7:00 AM (16 years ago)

Say you want to disable a submit button when it's clicked, to prevent the user from submitting twice:

<form name="myform">
  <input type="submit" value="Submit" onclick="this.disabled = 'true';">        
</form>

On Windows, this works fine in IE but not in Firefox. Or so it appears. What's going on?

Oops, you forgot an ACTION attribute in your form. (It's okay, this is common if you're planning an Ajax-style app.) Without it, IE just ignores the submit click, but Firefox uses the current page as the default action. So it effectively reloads the page, resetting the button state.

The thing is, the submit button is being disabled in FF, just like in IE, but depending on how fast the page reloads, you might not even notice.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Function reference vs. function call 16 Jul 2008 10:30 AM (16 years ago)

Here's a JavaScript function definition:

function foo() {
  alert('foo!');
}
You can refer to that function like this:
foo
And here is how to call (execute) the function:
foo()
Even experienced developers new to JS tend to get these confused. For example, jQuery's getJSON method allows you to specify a function to be executed once the operation is complete. Be careful not to plug in the function call when what you really need is a function reference:
$.getJSON( "action.php", {data:'some data here'}, foo() );
This will execute the foo function instantly. Not what you want. So leave off the parens:
$.getJSON( "action.php", {data:'some data here'}, foo );
This merely passes a reference to the function. It won't execute until it's called by jQuery.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

A good enough addEvent 16 Apr 2008 9:00 AM (17 years ago)

O HAI, this blog is nearly dead these days. But before it truly shuffles off into the sunset, allow me to point you to filosofo's "good enough" addEvent. Austin gets around PPK's pesky requirements by ignoring one of them (namely, having a corresponding removeEvent).

I love this solution because it mirrors much of my recent experiences with JavaScript. Honestly, I can't remember the last time I needed anything more than some variation of toggle() and I can count on one hand the number of times I've need to detach an event since 1998.

Kudos to Austin for thinking differently.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Some reader contributions to Calendar tutorial 1 Jul 2007 6:31 PM (17 years ago)

When I started SZOJ I promised myself I wouldn't abruptly shut down the blog if I lost interest; I'd just go radio silent until my interest was piqued again. That said, it's been a long while. So here are two things:

Rory Parle wrote in to point out you can use Math.ceil to determine the number of rows you'd need to safely render a calendar:

Math.ceil((monthLength + startingDay) / 7);

Michiel van der Blonk suggested a way to determine the number of days in a month:

var d = new Date(nYear, nMonth + 1, 0); // nMonth is 0 thru 11
return(d.getDate());

The mechanism here is the Date constructor will attempt to convert year, month and day values to the nearest valid date. So in the above code, nMonth is incremented to find the following month, and then a Date object is created with zero days. Since this is an invalid date, the Date object defaults to the last valid day of the previous month — which is the month we're interested in to begin with. Then getDate() returns the day (1-31) of that date — voilá, we have the number of days.

Bonus: it also seems to automatically correct for leap year. It feels like a hack but it seems to work consistently.

Thanks to Rory and Michiel for their contributions.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

How to build a simple calendar with JavaScript 19 Mar 2007 9:34 PM (18 years ago)

While there are lots of JavaScript-based calendar widgets out there, there's not much in the way of explaining how they work for the JS acolyte. I recently had the opportunity of building one from memory (and best of all, for no particular reason), using none of the popular JS libraries. This is the tutorial I wish I had found five years ago.

This series of posts will cover:

  1. how to create a simple calendar view with JavaScript
  2. how to tie the calendar to an HTML element for rendering
  3. how to add next/previous month controls
  4. how to add date picker functionality

Part One: a basic calendar display

We're ready to start laying the groundwork for our calendar widget. Here are the steps we'll be taking:

  1. define some global variables to hold common values
  2. define the calendar object and its arguments
  3. write a method to generate the HTML needed to render the calendar
  4. write a method to return the HTML

The Date object

I used to fear the JavaScript Date object, but it's actually fairly simple. In short, here's what it does:

  1. parses a given date, and provides useful information about that date
  2. if no date is specified, the current date is used as the default

A full discussion of the Date object is beyond the scope of this tutorial. You can find documentation here. However, for the purposes of this project, it's important to understand what the Date object doesn't do:

Surprisingly, the Date object isn't used in this widget as much as you'd expect. It's primarily used to determine the current date (if needed) and the starting day of the week for the specified month.

Global variables

As stated above, the Date object doesn't provide us with everything we need, so we have to compensate with a few predefined arrays of values.


// these are labels for the days of the week
cal_days_labels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

// these are human-readable month name labels, in order
cal_months_labels = ['January', 'February', 'March', 'April',
                     'May', 'June', 'July', 'August', 'September',
                     'October', 'November', 'December'];

// these are the days of the week for each month, in order
cal_days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
These are intentionally defined in the global scope, as they may be shared with multiple calendar widgets. The naming convention is arbitrary; you may prefer to append them to the calendar constructor to prevent collisions with other variables. Since the Date object returns integers for month (0-11) and day (1-31), we'll be able to use those as indexes for looking up the human-readable labels, as well as the number of days in the specified month.

But wait, how do we compensate for leap year when February is hard-coded at 28 days? Don't worry, it's coming later in this tutorial.

In addition, we'll need a Date object representing the current date, as a fallback:
// this is the current date
cal_current_date = new Date(); 

The Calendar constructor

Our constructor is pretty basic at the moment. I've designed it to take two arguments, a month and year (both integers) which will represent the intial calendar state. If these arguments are missing or null, the global default date object will be used instead.
function Calendar(month, year) {
  this.month = (isNaN(month) || month == null) ? cal_current_date.getMonth() : month;
  this.year  = (isNaN(year) || year == null) ? cal_current_date.getFullYear() : year;
  this.html = '';
}

Right now you might be saying "hey, why the long syntax? Can't we just do a shortcut like this?"

this.month = month || cal_current_date.getMonth();

One of the pitfalls of this method is that zero can be interpreted as false, which means if we specify zero (January) for our month, this expression would use the default month instead.

We also want the option to pass null for one or both of the values. The isNaN() function returns true when passed a null value, which will produce an incorrect result. So we have to test for both conditions: the argument must either be not a number or null for the default to be used.

HTML generation

Here's where we take the date info and stitch together an HTML calendar grid view. Let's start with an empty method definition:

Calendar.prototype.generateHTML = function(){

}

Not familiar with object prototyping in JavaScript? Here's some good reading.

Now let's step through everything this method has to handle.

First day of the week

March 2007 begins on a Thursday, but the Date object doesn't know that. However, if we specifically give it the date of "March 1 2007" to parse, it can tell us that day of the week as an integer (from 0-6). So, let's feed it such a date:

var firstDay = new Date(this.year, this.month, 1);

We can now query the new Date object for the day of the week:

var startingDay = firstDay.getDay();
// returns 4 (Thursday)

So now the widget knows that March 2007 starts on the 5th day of the first week (remember, we're counting from zero like computers do).

Number of days in the month

This one's easy. Just use the numeric month value to look up the value in our days-in-month array:

var monthLength = cal_days_in_month[this.month];

Compensate for leap year

Right, how does the widget know if it's leap year or not? This stumped me until I did a Google code search and found a widely-implemented approach that uses the modulus operator:

if (this.month == 1) { // February only!
  if ((this.year % 4 == 0 && this.year % 100 != 0) || this.year % 400 == 0){
    monthLength = 29;
  }
}

Damn, I'm glad I didn't have to figure that out. Thanks, Google Code!

Constructing the HTML

We're ready to start building the HTML string for our calendar view. First, the header stuff:

var monthName = cal_months_labels[this.month];
var html = '<table class="calendar-table">';
html += '<tr><th colspan="7">';
html +=  monthName + "&nbsp;" + this.year;
html += '</th></tr>';
html += '<tr class="calendar-header">';
for (var i = 0; i <= 6; i++ ){
  html += '<td class="calendar-header-day">';
  html += cal_days_labels[i];
  html += '</td>';
}
html += '</tr><tr>';

It's pretty straightforward: we begin with a table and header containing the month and year. Then we use a for loop to iterate over our human-readable days-of-the-week array and create the first row of column headers.

There are more efficient ways of concatenating HTML, but I thought this was the most clear. And don't gimme no lip about using a TABLE vs. a bunch of floated DIVs — I may be a standardista but I'm not a masochist.

Now for the tricky part, the remaining boxes. We need to make sure that we don't start filling in boxes until we've reached the first weekday of the month, and then stop filling them in when we've reached the maximum number of days for that month.

Since we don't know how many rows we'll need, we'll just generate a safe number of rows — like ten or so — and break out of the loop once we've run out of days. Here we go:

var day = 1;
// this loop is for is weeks (rows)
for (var i = 0; i < 9; j++) {
  // this loop is for weekdays (cells)
  for (var j = 0; j <= 6; j++) { 
    html += '<td class="calendar-day">';
    if (day <= monthLength && (i > 0 || j >= startingDay)) {
      html += day;
      day++;
    }
    html += '</td>';
  }
  // stop making rows if we've run out of days
  if (day > monthLength) {
    break;
  } else {
    html += '</tr><tr>';
  }
}

html += '</tr></table>';

this.html = html;

The real brain-twister is here:

if (day <= monthLength && (i > 0 || j >= startingDay)) {

...which roughly translates to "fill the cell only if we haven't run out of days, and we're sure we're not in the first row, or this day is after the starting day for this month." Whew!

Here's the complete code for our HTML-generating method:

Calendar.prototype.generateHTML = function(){

  // get first day of month
  var firstDay = new Date(this.year, this.month, 1);
  var startingDay = firstDay.getDay();
  
  // find number of days in month
  var monthLength = cal_days_in_month[this.month];
  
  // compensate for leap year
  if (this.month == 1) { // February only!
    if((this.year % 4 == 0 && this.year % 100 != 0) || this.year % 400 == 0){
      monthLength = 29;
    }
  }
  
  // do the header
  var monthName = cal_months_labels[this.month]
  var html = '<table class="calendar-table">';
  html += '<tr><th colspan="7">';
  html +=  monthName + "&nbsp;" + this.year;
  html += '</th></tr>';
  html += '<tr class="calendar-header">';
  for(var i = 0; i <= 6; i++ ){
    html += '<td class="calendar-header-day">';
    html += cal_days_labels[i];
    html += '</td>';
  }
  html += '</tr><tr>';

  // fill in the days
  var day = 1;
  // this loop is for is weeks (rows)
  for (var i = 0; i < 9; i++) {
    // this loop is for weekdays (cells)
    for (var j = 0; j <= 6; j++) { 
      html += '<td class="calendar-day">';
      if (day <= monthLength && (i > 0 || j >= startingDay)) {
        html += day;
        day++;
      }
      html += '</td>';
    }
    // stop making rows if we've run out of days
    if (day > monthLength) {
      break;
    } else {
      html += '</tr><tr>';
    }
  }
  html += '</tr></table>';

  this.html = html;
}

Returning the HTML

By design, the generateHTML method doesn't return the finished HTML string. Instead, it stores it in a property of the calendar object. Let's write a getter method to access that string.

Calendar.prototype.getHTML = function() {
  return this.html;
}

Okay, let's see what we've got!

Using the calendar

The widget may be complex on the inside, but it's ridiculously easy to implement. Here's how to embed a calendar displaying the current month into a web page:

<script type="text/javascript">
  var cal = new Calendar();
  cal.generateHTML();
  document.write(cal.getHTML());
</script>

Now let's set it to September 2009:

<script type="text/javascript">
  var cal = new Calendar(8,2009);
  cal.generateHTML();
  document.write(cal.getHTML());
</script>

The Demo!

Here's our calendar widget in action.

That's nice, but...

We're stuck displaying only one month?

Shouldn't generateHTML and getHTML be called automatically?

And WTF is up with that lame document.write?

Yep, this simple calendar is "simple" alright. Later this week we'll look at a much slicker way to integrate the calendar into webpages and add controls to transform our humble static calendar view into a full-blown datepicker.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

The Mark Of The n00b 7 Mar 2007 12:15 PM (18 years ago)

When I'm debugging or otherwise vetting someone's JavaScript work, I tend to look for evidence that indicates:

  1. the author is new to JavaScript, and/or
  2. the author cut-and-pasted this code

Neither of these are showstoppers, especially if the result is a neat-o web application or widget. But it does tend to drive me a little nuts sometimes.

Case in point: using "javascript:" in HREFs, a pet peeve of mine. It's not technically incorrect and it "just works" almost all modern browsers. It's just "wrong," in the same way that using <b> instead of <strong> is "wrong" and using TABLEs for layout is "wrong."

It's wrong from a craft point of view. It's the JavaScript equivalent of "eh, let's just shove the table markup into the database along with the content."

(Worse yet is "JavaScript:" (camel-cased) or something like "onclick='JavaScript:...'" I don't know of a single browser that falls over when the latter is used, but when someone sends me broken code to debug that has "JavaScript:" in the event handlers, the first thing I wonder is, "hm, what else is being overlooked here?")

Thing is, I don't expect JS newbies to know this stuff* because other than some tongue-clucking from old JS cranks like myself, there aren't any hard consequences.

And you. Do you have any favorite "mark of the newbie" things that drive you insane?

* that's why I run this blog

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Return false to prevent jumping scrollbars 7 Mar 2007 11:43 AM (18 years ago)

I like what Harry Maugans is doing over at his site with his JavaScript how-tos. It's back-to-basics stuff, but judging from the number of Diggs he gets, people are interested in how this stuff works under the hood.

In his tutorial "How To Create A Collapsible DIV with JavaScript and CSS," Harry writes regarding the use of the HREF attribute in anchors that trigger JS actions:

Another common do-nothing insert to use for the href property is a pound sign (#), and while that will work, it'll also move the user’s scroll bar to the very top of your website, which can get quite annoying.

The way to get around the scrolling problem is to include a "return false;" in your event handler:

<a href="#" onmousedown="toggleDiv('mydiv'); return false;">
  Toggle Div Visibility
</a>

Again, basic stuff, easily overlooked.

(I wrote to Harry about this but I don't think he approved my comment. That's okay; it's why we have Trackback :)

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

How to parse HTML strings into DOM nodes without innerHTML 26 Feb 2007 9:47 AM (18 years ago)

Here's a way of parsing a string of HTML into a series of DOM nodes in Firefox and other Mozilla-similar ("mozimilar") browsers:

var range = document.createRange();
range.selectNode(document.body);
var parsedHTML = range.createContextualFragment(SomeStringOfHTML);

document.body.appendChild(parsedHTML)

The call to selectNode() is required as you have to point the empty range at something before you can use it; Firefox throws an exception without it. You can use other range positioning methods as well.

Also of note: createContextualFragment() is not part of any DOM specification AFAICT, so you should feel appropriately shameful about using it as you would innerHTML.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

SCRIPT and innerHTML: a losing combination 9 Feb 2007 2:32 PM (18 years ago)

One day you may be tempted to skip all the cumbersome creation of SCRIPT elements via DOM methods and just cram stuff into the page with innerHTML. Woe unto you. This does not work:

var script = '<script type="text/javascript">alert("boo");</script>';
document.body.innerHTML = script;

In fact, this will produce an unterminated string error in IE6 and Firefox, because you still need to split up that closing </script> like in ye olde days:

var script = '<script type="text/javascript">alert("boo");</scr' + 'ipt>';
document.body.innerHTML = script;

Hooray, no error!

Except it still won't execute the script.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Fix your login system, you freakin' idiots 8 Feb 2007 10:22 AM (18 years ago)

Don't say "username" when you want "email address"

This is simple: if your app uses email addresses as usernames (ala OpenID or whatever), don't put "enter username" on the login page. That way I don't have to put a fist through the screen after "scottandrew" is rejected 99 times.

Wrong username or password? Which is it?

Did I type the wrong username? Did I misspell the password? Did I screw up both? Tell me. Don't get lazy and display "Incorrect username or password." What, do I get a prize if I guess correctly? Freakin' lazy, that's what you are.

Edit: I'm totally off-base on this one. A reader points out that specifying the incorrect field give hackers a vital clue. This is why I'm not in security; I'm all about making the user experience smoother — for hackers.

Tell me if you're going to limit login attempts, and don't hide password retrieval.

Some apps will throttle the number of login attempts to thwart malicious hackers. Generally I think this is dumb, but if you do this, be kind and let me know. Don't suddenly cut me off after three attempts.

And if you do cut me off, for the love of god, don't also lock me out of any password retrieval link. Move that link outside of whatever if-then you're using to display the login box. Please.

When possible, redirect me to where I was headed

I click "Edit Profile" in your app. My session has timed out, so you redirect me to a login to reauthenticate. Then you dump me on the "Welcome!" page. Wha?

Okay, so won't always have an HTTP_REFERRER to work with, but if I'm already working within your application and have to be redirected to a login, can't you save the original destination from the GET request or something?

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Dynamic SCRIPT elements and Safari 2.0 29 Jan 2007 12:57 PM (18 years ago)

Patrick Hunlock's excellent article Howto Dynamically Insert Javascript And CSS reminded me of a caveat I ran across last summmer: early 2.0 versions of Safari have a bug that prevents dynamic SCRIPT elements from loading JavaScript content. The bug was fixed in version 2.0.2.

That's probably the edgiest of edge cases, but it had me slamming my forehead against the wall for a few days. I was forced to do version sniffing since dynamic SCRIPT insertion was the only way to perform the cross-domain Ajax operations required.

Here are two bugs I found while researching the problem last year.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

IE6 SELECT value gotcha 25 Jan 2007 1:30 PM (18 years ago)

Consider the following HTML and JavaScript snippet, which attempts to grab the selected value of a SELECT element:

<select name="city" id="citySelector">
  <option>Seattle</option>
  <option selected="true">Tacoma</option>
  <option>Bellingham</option>
</select>
var s = document.getElementById("citySelector");
var v = s.options[s.selectedIndex].value;
alert(v);

In Firefox the result will be the string of selected city ("Tacoma"). IE6 however will return a blank string. Why?

Look at the HTML. None of the options have a value attribute. This is pretty common in markup where the option text is the same as the value. When used with a form POST or GET, most browsers send the option text in lieu of the missing value attribute.

But when accessing the value from JavaScript, Firefox plays fast and loose, whereas IE6 is stricter, returning nothing for the value because none was explicity given.

Here's a solution that works in both browsers:

var v = s.options[s.selectedIndex].text;

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Non-attachment 8 Mar 2006 10:08 AM (19 years ago)

When Kitano Gempo, abbot of Eihei temple, was twenty-eight he studied Chinese calligraphy and poetry. He grew so skillful in these arts that his teacher praised him.

Kitano mused: "If I don't stop now, I'll be a poet, not a Zen teacher." So he never wrote another poem.

— Zen Flesh, Zen Bones

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Happy birthday "Ajax" 15 Feb 2006 12:35 PM (19 years ago)

Well, almost. February 18th is the official one-year anniversary of Jesse James Garrett's essay that coined the term "Ajax" to describe what applications like Google Maps were using under the hood.

Thus rekindling interest in what some longtime web developers have pooh-poohed as old news: DHTML. But man. Books. Articles and tutorials. Conferences! Workshops! And the prime indicator that a concept has gained serious traction, haters!

Not bad for something once considered just a few short years ago to be too broken, unevenly-supported, and often downright ridiculous to be of any use.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Y! Design Pattern Library 14 Feb 2006 10:50 AM (19 years ago)

While it's awesome that Yahoo! has released a suite of JS tools, I'm actually more enamored of the Yahoo! Design Pattern Library.

The Yahoo! Design Pattern Library is an evolving set of sophisticated design guidelines for building web pages and applications. Our design patterns do not require the Yahoo! UI Library components, although using our UI Library can help you more easily implement the patterns described in these pages.
Very cool.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Safari quirk: Copying innerHTML 14 Feb 2006 10:36 AM (19 years ago)

Here's a fun one. Given the following HTML, let's attempt to copy the contents of div1 to div2, then grab a reference to the SPAN element "foo."

<div id="div1">
  <span id="foo">Foo span</span>
</div>

<div id="div2"></div>

The easy way out is to just copy innerHTML from one element to the other, then destroy the contents of the original element:

document.getElementById('div2').innerHTML = 
    document.getElementById('div1').innerHTML;
document.getElementById('div1').innerHTML = '';
alert(document.getElementById('foo'));

In most browsers, the alert dialog displays "Element SPAN" or something similar. But in Safari 1.3 and above, the dialog displays null. Why?

For a brief moment, when we copy the innerHTML, we create two SPANs with identical IDs of "foo." Even after we destroy the contents of div1 (leaving only the one SPAN), the DOM is screwed up enough to confuse Safari, which continues to return null even after the DOM is "fixed." By contrast, Firefox for OS X is far more forgiving.

The solution seems to be: duh, don't copy from element to element. Instead, hold the contents in a variable until it's safe to reinsert them into the page:

var tempHTML = document.getElementById('div1').innerHTML;
document.getElementById('div1').innerHTML = '';
document.getElementById('div2').innerHTML = tempHTML;
alert(document.getElementById('foo'));

The dialog should now display "Object SPAN" in Safari. Of course, astute JS developers would probably prefer to skip using innerHTML altogether

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Automatic JavaScript documentation generation 16 Jan 2006 11:43 AM (19 years ago)

I'm looking for some recommendations for auto-generating API docs for JS libraries. There doesn't seem to be a lot of info out there on Google, so let's build a list here.

I'm interested in everything from mods to existing packages like DocBook, JavaDocs, etc. to Perl scripts you've stitched together back in 1999. Have at it!

Non-Blogger members can email me and I'll post your link.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?

Tip: the MS Script Debugger 13 Jan 2006 12:53 AM (19 years ago)

I'm surprised that more JavaScript developers don't know (or maybe have forgotten) about the Microsoft Script Debugger. If you're weary of IE script errors that report "Line 324: Object expected" and no filename when you have 500K of script divided over 12 files, install this beastie, then open IE and go to Tools > Internet Options > Advanced and uncheck "Disable Script Debugging (Internet Explorer)."

Restart IE and the next time you encounter a JS error, a dialog box should pop up asking you if you wish to debug it. Clicking OK will launch the debugger, taking you to the file (hallelujah!) and the line at which it occurred.

It's not specifically tailored for JS like Venkman, but it's waaaaay better than IE's native JS error reporting.

Add post to Blinklist Add post to Blogmarks Add post to del.icio.us Digg this! Add post to My Web 2.0 Add post to Newsvine Add post to Reddit Add post to Simpy Who's linking to this post?