Thursday, February 17, 2011

Why must we still close SCRIPT elements?

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.

Sunday, January 31, 2010

HTML 5 audio player demo

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.

Wednesday, October 28, 2009

Using the "in" operator in conditionals

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

Friday, October 09, 2009

IE conditional comment gotcha

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)

Saturday, May 30, 2009

HTML 5

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.

Tuesday, February 03, 2009

Constraining elements to the browser window

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:

  • If an item was too close to the left or bottom edge of the browser window, the menu would appear partially offscreen. So I had to figure out how to constrain the menu to the visible window area.
  • I had to account for any additional window scrolling.

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);
}

Wednesday, November 12, 2008

Does your website disappear in IE8?

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.