Coding with Jesse

Sapper is dead! What's next in Svelte?

October 28th, 2020

In case you missed it, Rich Harris gave a presentation at Svelte Summit 2020, where he announced that Sapper v1 will never be released! Instead, he showed what's coming next in Svelte itself.

Be aware that at the time of me writing this blog post, none of this is officially released yet, and very likely will change in the near future. Nonetheless, it's exciting to see a sneak preview of what the future of Svelte will look like.

Getting started

To get started today, you can run this command in your terminal, assuming you have npm installed: npm init [email protected]

In the future, it'll probably just be npm init svelte, which is super clean and easy to remember. This will be a nice change from having to run npx degit svelte/template my-template.

Here's what you'll see if you run this command today:

█████████  ███████████    ███████    ███████████  ███
███░░░░░███░█░░░███░░░█  ███░░░░░███ ░░███░░░░░███░███
░███    ░░░ ░   ░███  ░  ███     ░░███ ░███    ░███░███
░░█████████     ░███    ░███      ░███ ░██████████ ░███
░░░░░░░░███    ░███    ░███      ░███ ░███░░░░░░  ░███
███    ░███    ░███    ░░███     ███  ░███        ░░░
░░█████████     █████    ░░░███████░   █████        ███
░░░░░░░░░     ░░░░░       ░░░░░░░    ░░░░░        ░░░

Pump the brakes! A little disclaimer...

[email protected] is not ready for use yet. It definitely can't
run your apps, and it might not run at all.

We haven't yet started accepting community contributions,
and we don't need people to start raising issues yet.

Given these warnings, please feel free to experiment, but
you're on your own for now. We'll have something to show
soon.

It'll go on to ask you if you want to use TypeScript, which is really nice for those who like to use TypeScript, and nice that it's optional for those who don't.

Here's the full directory structure you will get with an initial installation:

├── .gitignore
├── package.json
├── README.md
├── snowpack.config.js
├── src
│   ├── app.html
│   ├── components
│   │   └── Counter.svelte
│   └── routes
│       └── index.svelte
├── static
│   ├── favicon.ico
│   └── robots.txt
└── svelte.config.js

Starting the dev server

Once it's done setting up files, you need to run npm install and then npm run dev to spin up the dev server. Here's what you'll see:

snowpack

  http://localhost:3001 • http://10.0.0.180:3001
  Server started in 643ms.

▼ Console

[snowpack] installing dependencies...
[snowpack] ✔ install complete! [0.59s]
[snowpack] 
  ⦿ web_modules/                                size       gzip       brotli   
    ├─ svelte-hmr/runtime/hot-api-esm.js        22.08 KB   7.4 KB     6.29 KB    
    ├─ svelte-hmr/runtime/proxy-adapter-dom.js  5.17 KB    1.65 KB    1.38 KB    
    ├─ svelte.js                                0.18 KB    0.15 KB    0.11 KB    
    ├─ svelte/internal.js                       52.36 KB   13.16 KB   11.36 KB   
    └─ svelte/store.js                          3.3 KB     1 KB       0.88 KB    


[snowpack] > Listening on http://localhost:3000

What is happening under the hood? This is very different from the Svelte and Sapper templates that came before. There is no longer a rollup.config.js nor a webpack.config.js, because it does not use Rollup nor Webpack, at least not during development.

Instead, it uses Snowpack to handle compiling and serving client-side resources. Snowpack does not bundle your resources, and relies heavily on JavaScript’s native module system, which means development is much faster. There is even a snowpack.config.js file which gives you a place to configure Snowpack to some degree:

// Consult https://www.snowpack.dev to learn about these options
module.exports = {
    extends: '@sveltejs/snowpack-config'
};

Building your application

There is now also a new svelte.config.js file, which lets you define an "adapter", used with npm run build to build your application into a production web site:

module.exports = {
    // By default, `npm run build` will create a standard Node app.
    // You can create optimized builds for different platforms by
    // specifying a different adapter
    adapter: '@sveltejs/adapter-node'
};

The default adapter will use Rollup to build your site into a Node.js web server. It seems that this web server doesn't use Express.js, though that might change as well, or maybe there will be a special adapter for Express.

If you want to have a purely static export, you can currently replace @sveltejs/adapter-node with @sveltejs/adapter-static, but be sure to run npm install @sveltejs/adapter-static as well.

In the future, there will be many other adapters, for example, building specifically for certain web hosting platforms, serverless architectures, and who knows what else? The cool thing about this adapter approach, is that you can build your web site without necessarily knowing how it will be built or deployed. You'll be able to change the adapter without changing your code.

Dependencies

Let's have a look at the package.json:

{
    "name": "demo",
    "version": "0.0.1",
    "scripts": {
        "dev": "svelte dev",
        "build": "svelte build"
    },
    "devDependencies": {
        "@sveltejs/adapter-node": "0.0.12",
        "@sveltejs/kit": "0.0.23",
        "@sveltejs/snowpack-config": "0.0.4",
        "svelte": "^3.29.0"
    }
}

Note that there are very few dependencies here. I really like how minimal this is. Both of the scripts are using the new svelte CLI from @sveltejs/kit, though that name might change, and it's not even available on GitHub yet. For now, you can look at the npm package.

Routes

You'll notice a folder src/routes/ where you can define your routes similar to how Sapper (or Next.js, etc.) let you define routes. Basically, your folder and file structure in here will map one-to-one with the routes on your web site. This is really nice, and easy to work with, especially if you're used to using PHP or other similar web development platforms.

If you're not building a static-only web site, you can also define server-side routes, similar to what you can do with Sapper. For example, you can create a file at src/routes/api.js:

export async function get(req) {
    return {
        status: 200,
        body: {
            hello: 'world'
        }
    }
}

If you're familiar with Sapper, you might notice that you have to return an object with status and body properties, instead of using an Express res object for your response. This is because it is not Express middleware. It uses an internal Node web server, with an API similar to what you may have used with some serverless cloud functions.

To create a layout component, to provide a consistent header and footer wrapped around all your routes, you can create a file called $layout.svelte, similar to Sapper's _layout.svelte.

You can also make an error handler route called $error.svelte, to handle 404s and other programming errors. It receives a status prop and also an error prop, so you can decide how to display the error to your users.

Migrating

Rich Harris notes that migrating from Sapper or other similar frameworks should be fairly straightforward, since most of the folder structure and other concepts are pretty similar. You'll probably just need to rename some files, and change how your server-side routes work, because they will no longer be written as Express middleware.

For fetching data for both server-side and client-side rendering, the Sapper approach of having a <script context="module"> block currently still works, though it's possible that will change.

Conclusion

If you're excited about all this stuff, it's definitely too early to start building your applications using it, but I'm willing to bet that it'll be a good choice to get started by using Sapper today, with the expectation that it'll be easy enough to migrate to this in the future, once it's ready.

To see a demo, check out Rich Harris' video Futuristic Web Development

If you're interested in learning more about Svelte, check out my video course The Joy of Svelte.


7 Svelte features that bring me joy

May 6th, 2020

When you learn Svelte for the first time, there are lots of little things that put a smile on your face. Some of the little shortcuts are so elegant, they make things so much easier with cleaner, more succinct code. I recorded a video for YouTube with a demo of seven of my favourite features put together.

1. bind:property

In Svelte, you can very easily bind a property of a DOM element to a local variable. For example, you can bind a variable to the value of an input.

<script>
let name = '';
</script>

<input bind:value={name}/>

2. Directive shortcuts

There's a bunch of really cool shortcuts in Svelte that make using directives and passing props even more succinct. If the property name and variable name are the same, you can write them like this:

<script>
let value = '';
let active = false;
</script>

<input bind:value class:active/>

It's shortcuts like this that make Svelte a real pleasure to work with.

3. Scoped CSS

In your Svelte components, any CSS inside a <style> block will be scoped to that component. That means, a lot of the time you won't need to add classes or IDs for styling. You can often just use the tag name for styling, without any risk of messing up the CSS of the rest of the page.

<style>
  /* No class needed! */
  button {
    font-size: 200%;
    background: red;
  }
</style>

<button>Click me</button>

4. DOM Event Modifiers

If you've ever added a submit handler to a form, or a click handler to a link, and you wanted to handle the event without having the page refresh, you've had to call event.preventDefault() in the handler function. Svelte makes this super easy using a "modifier".

<script>
function handleSubmit() {
  // don't need to call preventDefault in here anymore!
}
</script>

<form on:submit|preventDefault={handleSubmit}>
  <input type="submit"/>
</form>

5. Loop "else" clause

How many times have you written an if statement to check if an array is empty, so that you can display a special "nothing here" message? Svelte makes this very easy by providing an "else" clause to the {#each} block:

<script>
let results = [];
</script>

{#each results as result}
    <p>{result}</p>
{:else}
    <p>Sorry, no results found!</p>
{/each}

6. Transitions

Whenever things appear and disappear on your page, it's really nice to use CSS transitions to have a bit of animation to make the state change feel more natural. Normally, you have to write a bunch of CSS and JavaScript to pull this off, but Svelte makes it super simple. All you have to do is import the effect you want and use the transition: directive on an element, and whenever the element appears or disappears, the transition will play.

<script>
import { fade } from 'svelte/transition';
</script>

<div transition:fade>
  This will fade in and out.
</div>

7. Slot props and the let:variable directive

My absolute favourite Svelte feature is the ability to pass data down from a component to its children. In React, the common way to achieve this is either with a function child or a render prop, and this often makes the code harder to read and understand.

Svelte builds this functionality right into the template syntax. You add a <slot/> element to the parent component, and pass props to it. Then, you receive the data using the let:prop directive:

<!-- ColorManager.svelte -->
<script>
let colors = ['red', 'green', 'blue'];
</script>

<slot {colors} />
<!-- Colors.svelte -->
<script>
import ColorManager from './ColorManager.svelte';
</script>

<ColorManager let:colors>
  <ul>
    {#each colors as color}
      <li>{color}</li>
    {/each}
  </ul>
</ColorManager>

This makes it really easy to write components that are solely responsible for data fetching and management. This allows you to separate out your rendering logic from your state logic, and end up with much simpler code.

Conclusion

Svelte has a ton of really cool features and syntax that make your web components simpler than ever. I've outlined a few of my favourites here. If you're interested in learning more, check out The Joy of Svelte.


Know when to fold 'em

February 4th, 2020

The last few weeks on Twitch, I've been working on the user authentication to use for my side projects, including my upcoming course The Joy of Svelte.

I wrote it in a generic way as Express middleware. It uses a MySQL database with express-session and express-mysql-session. It doesn't have any HTML, it just adds a REST API to your server with some routes like /auth/signup, /auth/login, /auth/forgot and /auth/reset. This way, each of my sites can have a different UI, and use fetch to do everything.

Oh man, I wasted so much time when I was building it. I wanted to try building the whole thing with pure ES modules. ES modules are now supported in Node without a flag, so I figured it's finally time to use them without a build process.

I wasted hours trying to get it to work. Of course Mocha doesn't work with ES modules yet. Eventually, I gave up and went with using a library called esm that gets things working somehow. When I thought I was done, and tried to add it to a Sapper project. Since it was now in my node_modules, and being transpiled by Rollup, it all broke. Sigh.

I was facing another never-ending black hole of googling and debugging. So you know what I did? I went and rewrote the ES modules to CommonJS syntax. It took three minutes.

Lesson is, just because a new feature is available, you still have to wait for your entire tool chain to catch up and adapt as well. It's fun to push the envelope, but it can get exhausting too. Sometimes it's faster to cut your losses and take another route.

As Kenny Rogers warns, "You got to know when to hold 'em, know when to fold 'em, know when to walk away and know when to run."

If you're curious about the authentication middleware, you can see the source code here. It's not perfect for everybody, but if you want to use it and think I should publish it to npm and document it, let me know.

If you're excited about The Joy of Svelte, it'll be launching soon. Sign up for the Coding with Jesse newsletter, and you'll get a subscriber discount as soon as it's ready.


React Hooks vs. Svelte

November 13rd, 2019

I get asked a lot on my Twitch channel to show a comparison of React and Svelte. I thought I'd record a short video to show everyone how to take a basic React example and rewrite it using Svelte:

Let's look at a basic example from the React Hooks documentation. Here we have a simple component with a button, and some text showing you how many times you've clicked the button. We're using some state in our component, to keep track of how many times the button was clicked. It's a nice, simple example of using a stateful component:

import React, { useState } from 'react';

function Example() {
    // Declare a new state variable, which we'll call "count"
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>Click me</button>
        </div>
    );
}

What would this look like written with Svelte? Turns out, most of the code above is React boilerplate that we can do without. Let's start by commenting out all the boilerplate:

// import React, { useState } from 'react';

// function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);

// return (
<div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>Click me</button>
</div>;
// );
// }

That gets us some of the way, but that's still not valid Svelte. Svelte is an extension of HTML, so we need to put our JavaScript code in a <script> block, and change it to use a local state variable instead of React's useState function:

<script>
    // Declare a new state variable, which we'll call "count"
    let count = 0;
</script>

<div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(count + 1)}>Click me</button>
</div>

This is very close to Svelte, but we have to change one more thing. We need to change React's onClick attribute to Svelte's on:click, and then change the click handler so it simply increments count:

<script>
    // Declare a new state variable, which we'll call "count"
    let count = 0;
</script>

<div>
    <p>You clicked {count} times</p>
    <button on:click={() => count++}>Click me</button>
</div>

All done! When you change React code into Svelte code, you spend most of your time deleting code, and deleting code feels amazing!

The major difference here is that your state is kept in local JavaScript variables instead of being tied up inside useState. This means you can set your state variable to a new value without calling a function, and that makes it possible to keep your component code very clean and succinct.

In fact, if you came up to me and said you had a new framework that was even simpler than Svelte, I'd have a hard time believing it! I mean, what could we remove from that Svelte component? Even Vanilla JavaScript would be way more complicated than this basic Svelte example. Svelte makes our web components as simple as possible, but no simpler.


Svelte is the most beautiful web framework I've ever seen

June 28th, 2019

I first heard about Svelte a year ago, when Rich Harris presented it at JSConf EU 2018. The demo gods were a bit harsh on him, but it didn't matter to me, because I was so impressed by his philosophy and ideas that I was already sold. I knew he'd work out the kinks, go through a few major versions, and Svelte would be mature enough in no time.

I kind of forgot about Svelte after that, that was until last week when I read Rich Harris' blog post Why I don't use web components. It reminded how simple and beautiful Svelte's syntax is, and I decided it was time to give it some serious consideration.

First, I played a little bit with the Svelte REPL, and got a sense for how it works. Then I decided to try building a Tic Tac Toe game live on Twitch and YouTube. Even though I'm a total noob when it comes to Svelte, and I'd barely read the docs, it only took me about half an hour to get a Tic Tac Toe game working. After that, I explored some different Svelte features, trying to move the game state into a non-Svelte module, and discovered a few anti-patterns in the process.

At the end, I was completely blown away when I discovered that the production build had only 2,418 bytes of JavaScript..! That's 2.4kb including the Svelte runtime!!!

How does Svelte do it? Because Svelte is a compiler. It only includes the bare minimum of JavaScript necessary to get the job done. It turns the HTML templates you write into extremely simple DOM scripting. It transpiles the JavaScript you write so that your simple variable assignments trigger a re-render. It generates JavaScript classes to represent your .svelte files and wires everything up for you, so the only boilerplate you really need is a <script> tag and a <style> tag.

If you're interested in seeing the Tic Tac Toe game I built, you can clone it on GitHub, and spin it up with npm install, and npm start.

Otherwise, I highly recommend you check out the official Svelte Tutorial and try it out for yourself!


Formatting dates with JavaScript

April 19th, 2019

There are a number of popular JavaScript date formatting libraries out there, such as Moment.js, Luxon and date-fns. Those libraries are very powerful and useful, allowing you to request various date formats using a special syntax, but they also come with many more features than most people will ever use. And that means, your users are probably downloading more JavaScript then they need to.

In this article, I'm going to show you how to use the built in basic Date object to format dates without any third-party library. In other words, we'll be formatting dates with pure vanilla JavaScript.

Feel free to copy and paste the solutions below to use as a starting point in your own code bases. I'll demonstrate how to generate a number of common format types, but you may need to modify the solutions below a little bit to format dates and times to be exactly the way you want.

What about the Internationalization API?

Before I start, I should mention that there is some formatting functionality built into JavaScript dates, using the Internationalization API.

Using the Internationalization API, you can format dates according to a specific locale, which means formatting according to the customs of the user's location and language. If you're not picky about how dates and times will be displayed, this can work well in many cases, but it depends on each user's operating system, and which locales are installed on their devices. In other words, it can be hard to predict what the format will look like in any given browser.

If you want to format dates in some specific way and have full control over what is being displayed, please read on.

Date methods

Pretty much all the information we need can be provided by a few built-in methods on the date object:

const date = new Date; // current time & date

date.getFullYear(); // Year
date.getMonth(); // Month of the year 0-11 (0 = January)
date.getDate(); // Date of the month, 1-31
date.getDay(); // Day of the week, 0-6 (0 = Sunday)
date.getHours(); // Hours, 0-23
date.getMinutes(); // Minutes, 0-59
date.getSeconds(); // Seconds, 0-59

Now you may have noticed that all these methods return numbers. But how are you supposed to get words out of it like "Thursday" or "November"? And what if you want your month or date number to start with a zero? No problem, we can use JavaScript!

Years

Get the full year

Getting the year out of the Date object is really easy, but it's a four-digit year by default:

date.getFullYear(); // returns 2019

What if you want only two-digits?

There is a getYear() function in the Date object as well, and sometimes I accidently use that instead of getFullYear(). However, it's more or less useless. In 2019, it returns 119. Why?? Because there is a Y2K bug baked into JavaScript, even though JavaScript was designed in 1995! In those first five years of JavaScript, people could call getYear() for a two-digit year, and simply add 1900 to get a four-digit year. And I guess that still works, because 1900 + 119 = 2019!

Since the getYear() function has been broken since the year 2000, I recommend getting a two-digit year using this approach instead:

function getTwoDigitYear(date) {
    return date.getFullYear() % 100; // returns 19
}

Months

Display the month as a two-digit number

The getMonth() function of the Date object returns a number between 0 and 11. That has got to be one of the biggest surprises when working with dates in JavaScript. It also mostly makes this method useless without writing more code. Let's see how to do that.

function getTwoDigitMonth(date) {
    // add one to month to make it 1-12 instead of 0-11
    const month = date.getMonth() + 1;

    if (month < 10) {
        // add a 0 to the start if necessary
        return `0${month}`;
    } else {
        // for 10, 11 and 12, just return the month
        return month.toString();
    }
}

Display the month as a string

If we want to display the month as a string of text like "February" or "Mar", then we need to use a JavaScript array with all the months. In fact, this is why the getMonth() method returns a number between 0 and 11, because arrays start counting at 0 as well!

function getMonthName(date) {
    const months = [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December'
    ];

    return months[date.getMonth()];
}

If you want to use a short form of the month, or just a single character, or another language, you can easily adapt the code above to change the contents of the array with whatever you prefer to use.

Days of the week

If you're going to be displaying the day of the week, you'll probably want to be displaying some text. You can use the same approach that we used for formatting months above. Basically, you just need to define an array of text and access it using the getDay() result as an index.

function getWeekDayName(date) {
    // make sure you start with Sunday
    const weekDays = [
        'Sunday',
        'Monday',
        'Tuesday',
        'Wednesday',
        'Thursday',
        'Friday',
        'Saturday'
    ];

    return weekDays[date.getDay()];
}

Again, if you want to return different text, like 'Sun' or 'S', just replace the entries in the array with whatever you prefer.

Day of the month

The day of the month is pretty straightforward. You can just call getDate() to get the number, and you don't have to add one to it or anything.

Display the day of the month as a two-digit number

For some date formats, you may want to get a two-digit version of the date number. That's simple enough:

function getTwoDigitDayOfTheMonth(date) {
    const dayOfTheMonth = date.getDate();

    if (dayOfTheMonth < 10) {
        // add a 0 to the start if necessary
        return `0${dayOfTheMonth}`;
    } else {
        // for 10 or greater, just return the dayOfTheMonth
        return dayOfTheMonth.toString();
    }
}

Display an ordinal with the day of the month

If you want a fancy day of the month with an ordinal after it, like 1st, 2nd, 3rd, 4th, 21st, etc., you can easily figure that out with a switch statement:

function getDayOfTheMonthWithOrdinal(date) {
    const dayOfTheMonth = date.getDate();
    const ordinal = getOrdinal(dayOfTheMonth);

    return `${dayOfTheMonth}${ordinal}`;
}

function getOrdinal(number) {
    // using the % modulo operator to get the last digit of the number
    const lastDigitOfNumber = number % 10;

    switch (lastDigitOfNumber) {
        case 1:
            return 'st';
        case 2:
            return 'nd';
        case 3:
            return 'rd';
        default:
            return 'th';
    }
}

Times

You can apply the techniques we used above with times as well, depending what you need. Let's say we want to display a time format in 12-hour time with "am" or "pm", like "9:45pm". Here's how:

function formatTwelveHourTime(date) {
    // call functions below to get the pieces we need
    const hour = getHourInTwelveHourClock(date);
    const minute = getTwoDigitMinute(date);
    const meridiem = getMeridiem(date);

    // put it all together
    return `${hour}:${minute}${meridiem}`;
}

function getHourInTwelveHourClock(date) {
    const hour = date.getHours();

    if (hour === 0 || hour === 12) {
        return 12;
    }

    // otherwise, return a number between 1-11
    return hour % 12;
}

function getTwoDigitMinute(date) {
    const minute = date.getMinutes();

    if (minute < 10) {
        // add a 0 to the start if necessary
        return `0${minute}`;
    } else {
        // for 10 or greater, just return the minute
        return minute.toString();
    }
}

function getMeridiem(date) {
    const hour = date.getHours();

    if (hour < 12) {
        return 'am';
    } else {
        return 'pm';
    }
}

Bringing it all together

We've covered how to generate all the different pieces of various date formats. How about putting all the difference pieces together?

You can use any method you like, but I suggest an approach like the following, using a template string.

function shortDateFormat(date) {
    // use the built-in function here
    const year = date.getFullYear();

    // use the functions we wrote above
    const month = getTwoDigitMonth(date);
    const dayOfTheMonth = getTwoDigitDayOfTheMonth(date);

    // put it all together, eg. "YYYY-MM-DD"
    return `${year}-${month}-${dayOfTheMonth}`;
}

You can create as many formatting functions as you have formats to generate. Here's another example:

function longDateTimeFormat(date) {
    const weekDayName = getWeekDayName(date);
    const monthName = getMonthName(date); 
    const dayOfTheMonth = getDayOfTheMonthWithOrdinal(date);
    const year = date.getFullYear();
    const time = formatTwelveHourTime(date);

    // put it together, eg. "Friday, April 19th, 2019 at 9:45pm"
    return `${weekDayName}, ${monthName} ${dayOfTheMonth}, ${year} at ${time}`;
}

Conclusion

I hope I've provided you with all the tools and techniques you might need to format dates and times. You can apply many of these techniques in other ways too, like formatting currencies. More importantly, with a little bit of custom JavaScript, you can avoid having additional dependencies for your project and downloads for your users.


Free eBook: Unobtrusive Ajax

October 22nd, 2010

Great news! O'Reilly has been kind enough to let me distribute my eBook Unobtrusive Ajax to readers of my blog, so now you can download and read it for free!

Download "Unobtrusive Ajax" eBook

From the book's cover:

Unobtrusive Ajax is about making web applications that work for everyone all the time, even if you have JavaScript turned off, or you're using a mobile phone or a screen reader, or however you happen to be using the Web. It's about the separation of behavior (JavaScript), content (HTML), and presentation (CSS).

This short cut will focus on the practical benefits of using Ajax and JavaScript unobtrusively and show you that unobtrusive web development and progressive enhancement benefit both web developers and users of the Web. You'll get to see many simple examples of building web interfaces that are unobtrusive. You'll quickly see that it is actually very easy to make web applications that everyone can use.

When you're finished reading this book, you will be able to convince anyone why developing unobtrusively is the best way to build a site with JavaScript and Ajax.

I wrote this book back in 2007, before I'd discovered jQuery, so writing unobtrusive JavaScript has become even easier than ever. That said, the principals of writing web pages that work without JavaScript remain the same: keep your content in the HTML, use real links and forms, and avoid depending on JavaScript or Flash for any critical functionality.

Nowadays, I would also suggest bearing different user interfaces in mind. Not every visitor to your website has a mouse and a keyboard, some are using their handheld touchscreen phones, and if your shopping cart requires people to drag and drop, you may end up losing customers. Be careful not to make assumptions about your visitors. If you stick with basic HTML, you can't go wrong.


Official jQuery Templating Plugin

October 11st, 2010

jQuery announced that they are now officially supporting jQuery Templates, thanks in big part to Microsoft.

Templating cleans up the job of generating HTML with jQuery. It also gives you the opportunity of keeping HTML code out of your JavaScript completely, if you wish.

Let's say you have a block of data like this:

var fruits = [
    { name: 'apples', color: 'green' },
    { name: 'oranges', color: 'orange' },
    { name: 'bananas', color: 'yellow' },
    { name: 'tomatoes', color: 'red' }
];

You want to display the data in a nice table with some color effects:

namecolor
apples green
oranges orange
bananas yellow
tomatoes red

Without templates, your code might look like this:

// create a table
var $table = $('<table class="fruit-table">');

// append a header to the table
$('<tr><th>name</th><th>color</th></tr>').appendTo( $table );

// append a row for each fruit
for (var i in fruits) {
    // create a row, and set the background to the color of the fruit
    var $row = $('<tr/>', { css: { background: fruits[i].color } });

    // create a column and append to the row
    // we use text here so all HTML is escaped, to prevent hacking
    $('<td/>', { text: fruits[i].name }).appendTo( $row );

    // do the same for the color
    $('<td/>', { text: fruits[i].color }).appendTo( $row );

    // append the row to the table
    $row.appendTo( $table );
}

// all done, stick the table on the page
$table.appendTo('body');

Unfortunately, code like this usually ends up looking kludgy, and it's often hard to visualize what the final HTML will look like.

Templates let us turn the HTML/JavaScript relationship inside-out by putting the looping right in the HTML:

// define a template for the fruit table
// we'll use slashes at the end of each line to escape the line break
// this way we don't have to concatenate strings.
$.template('fruit-table', '\
    <table class="fruit-table"> \
        <tr><th>name</th><th>color</th></tr> \
        {{each rows}} \
            <tr style="background: ${color}"> \
                <td>${name}</td> \
                <td>${color}</td> \
            </tr> \
        {{/each}} \
    </table> \
');

// instantiate the template with the fruit array passed in as 'rows'
var $table = $.tmpl('fruit-table', { rows: fruits });

// that's it. stick it on the page.
$table.appendTo('body');

Now there's no question what the HTML will look like. It's in plain view.

If you'd like to jump on the chance to take the HTML out of your JavaScript completely, you can stick the same block on the page, in a special <script> block:

<script id="fruit-table" type="text/x-jquery-tmpl">

    <table class="fruit-table">
        <tr><th>name</th><th>color</th></tr>
        {{each rows}}
            <tr style="background: ${color}">
                <td>${name}</td>
                <td>${color}</td>
            </tr>
        {{/each}}
    </table>

</script>

Now we're down to a single line of code. Beautiful, isn't it?

$('#fruit-table').tmpl({ rows: fruits }).appendTo('body');

Now that we've gotten that taken care of, let's take it to the next level, and make a template that will dump any tabular data we give it:

<script id="table" type="text/x-jquery-tmpl">

{{if !data || data.length == 0 }}
    <p>No data.</p>
{{else}}
    <table class="${className}">
        <tr>
            {{each(key) data[0]}}
               <th>${key}</th>
            {{/each}}
        </tr>
        {{each(i, row) data}}
            <tr>
                {{each(key, value) row}}
                    <td>${value}</td>
                {{/each}}
            </tr>
        {{/each}}
    </table>
{{/if}}

</script>

$('#table').tmpl({
    data: fruits,
    className: 'fruits-table'
}).appendTo('body');

Want more? Check out the documentation and official announcements for lots more information:


jQuery Live Events

February 16th, 2009

jQuery 1.3 came out on January 14th, jQuery 1.3.1 on the 21st, and with them we now have live events built into jQuery.

Live events are pretty magical at first glance. They allow you to set events only once, and they work forever in the future, even as you're creating new elements and adding them to the page.

Normally if you ran:

$('a.wizard').click(function(){
    // do some wizardry
});

and then later you added wanted to add some more <a class="wizard">s to the page, you would have to re-attach this event handler over and over.

Live events allow you to add an event that will work forever. This means you only have to add each type of event once. You would only have to write:

$('a.wizard').live('click', function(){
    // do some wizardry
});

And your wizard links will work forever, even after you add 100 new wizard links to the page dynamically.

This magic trick works by attaching the click event to the document. Whenever you click anywhere on the page, the document click event gets called. jQuery compares the target element to your wizard links and triggers your click event if the click came from inside the link.

You can also do this yourself using the new closest() function. It allows you to do something like this:

// listen for clicks on the document
$(document).click(function(e){
    // look for a possible parent element matching a.wizard
    $(e.target).closest('a.wizard').each(function(){
        // wizard it up
    });
});

These live events can really help with performance. If you're attaching events to 100s of similar elements, like photos in an album, you can also save a lot of memory and speed things up by using live events, or using the example above and checking for events on a common parent element, either document or any element.

If you're used to using closures to use data within click handlers, you will find they won't work anymore which is probably a good thing. Instead, you can use data() to store any amount of data with that element and get it out later:

// maybe this is some JSON data you got using Ajax or something
var wizards = [
   { name: 'Merlin', skill: 'magic' },
   { name: 'Mr. Wizard', skill: 'science' }
];

$.each(wizards, function(i, wizard){
    // create a new link, change the text, add the data and append to the body
    $('<a class="wizard"/>')
        .text(wizard.name)
        .data('wizard', wizard)
        .appendTo(document.body);
});

then you only need to attach the click handler once:

$(document).ready(function(){

    $('a.wizard').live('click', function(){
        // fetch the data back out
        var wizard = $(this).data('wizard');

        // get the stuff you need out of the object
        var name = wizard.name;
        var skill = wizard.skill;

        // do your thing
        alert(
            "Hello my name is "
            + name
            + "and I'm better than you at "
            + skill
            + "!!!"
        );
    });

});

And you can actually do these things in reverse, the order doesn't matter because of the magic and universality of live events.

Pretty cool, eh? This way of developing has always been possible using JavaScript, but after learning about this with jQuery 1.3, it changed the way I look at programming with data and events.

What do you think? Any questions, corrections or suggestions? Leave a comment.


Stop CSS Background Flickering in Internet Explorer 6

October 18th, 2008

I was once again reminded of an IE6 bug I had forgotten about - background images flashing or flickering when the mouse hovers over them.

So, I went looking for a solution. Here's what I found:

try {
  document.execCommand("BackgroundImageCache", false, true);
} catch(e) {}

Works like a charm. Turns out it's due to the browser not caching the background images. This command turns on background image caching.

Do you think this would be good code to add to the core jQuery library?


JavaScript Functions are Variables

December 29th, 2007

JavaScript functions are variables, and this is a big difference between JavaScript and many other programming languages. It can be a bit of a paradigm shift for people new to JavaScript, but it allows for some really cool things you can't do in many other languages.

When I say functions are variables, I mean that they're treated much the same as arrays, numbers, strings and objects. That means you can do some neat things.

You can define and redefine functions as local variables:

var myFunc;

if (Math.random() < 0.5) {
    myFunc = function() {
        alert('heads');
    };
} else {
    myFunc = function() {
        alert('tails');
    };
}

myFunc(); // alerts "heads" or "tails" depending on random value

You can also pass functions as parameters to other functions, which is very useful for callback functions:

function do_something(callback_function) {
    alert('hello');

    if (callback_function) {
        callback_function();
    }
}

var my_callback = function() {
    alert('goodbye');
};

do_something(my_callback); // alerts "hello" and then "goodbye"

You can also return a function from a function:

function get_multiplier(factor) {
    return function(num) {
        return num * factor;
    };
}

var times_5 = get_multiplier(5);
var result = times_5(10);

alert(result); // alerts "50"

var six_times_two = get_multiplier(6)(2);

alert(six_times_two); // alerts "12"

You can also define "anonymous" functions without a name and even execute them:

(function(first_name, get_last_name) {
    var last_name = get_last_name();

    alert(first_name + ' ' + last_name); // alerts "Jesse Skinner";
})('Jesse', function(){ return 'Skinner'; });

Note that in order to execute an anonymous function you have to wrap it in () parentheses first.

So that's just some of the unusual stuff you can do with JavaScript. Once you get familiar with the concept, you can really start to reuse code in great new ways.


Getting an Image's onload to Fire

September 28th, 2007

Are you attaching an onload function to an image but finding it doesn't always get called?

The problem is almost always this: you need to set the onload before you set the src. The reason is obvious when you think about it: if the image is in the cache, the image will be loaded immediately after setting the src, even before the onload is set.

Adding fuel to the fire, Firefox and Safari will let you set an onload immediately after (it seems they don't call the onload until the block of JavaScript finishes). This doesn't happen in Internet Explorer or Opera.

Long story short:

// evil:
var image = new Image();
image.src = 'image.jpg';
image.onload = function() {
    // sometimes called
    alert('image loaded');
};

// good:
var image = new Image();
image.onload = function() {
    // always called
    alert('image loaded');
};
image.src = 'image.jpg';

No matter how many times I come across this bug and find the solution, I end up making the same mistake a few weeks later. I'm hoping that blogging about it will get this stuck in my long-term memory.


Using Hash for JavaScript Debugging

September 26th, 2007

No, I don't recommend smoking hash before doing your JavaScript debugging :) But I did figure out a technique which you can use to switch a page in and out of debugging mode.

What happens if you need to debug a site that's getting traffic, and you don't want everyone getting your alert() messages or whatever else you need to debug?

Well why not check location.hash and look for a special keyword:

function debug_mode() {
    return location.hash == '#debug';
}

if (debug_mode()) {
    alert('We are in debug mode!');
}

Now you can just add #debug to the URL (and hit enter) to turn on debug mode, or just drop the #debug to turn it off. You can even have more than one special "mode" and use different hash values to switch between these modes.


Detect Internet Explorer 6 in JavaScript

September 21st, 2007

Update: As some of the comments mention, the technique below doesn't work anymore. It's best to use object detection to accomplish what you need, or use conditional comments. But if you need to detect IE6, this should work: /MSIE 6/i.test(navigator.userAgent)

Sometimes you just have to sniff for Internet Explorer 6 (and under) in JavaScript. Using conditional comments is a decent solution, but I don't want them scattered all over my code.

With a bit of help from Dean Edwards, I worked out the following:


var IE6 = false /*@cc_on || @_jscript_version < 5.7 @*/;

With just a single conditional comment, you can have a JavaScript variable that you can reuse throughout your code.

(IE6 will be true in Internet Explorer 6 and under, but does anyone really care about IE5 anymore? Thought not.)

You could use this technique to sniff for other things:


// exactly Internet Explorer 7
var IE7 = false /*@cc_on || @_jscript_version == 5.7 @*/;

// at least Internet Explorer 7
var gteIE7 = false /*@cc_on || @_jscript_version >= 5.7 @*/;


// any Internet Explorer (thanks to Dean)
var isMSIE = /*@[email protected]*/false;

Note: browser sniffing is evil but sometimes painfully necessary.


jQuery 1.2 Notes

September 11st, 2007

Great news: jQuery 1.2 is out, and the heavily-awaited jQuery UI (an "official" replacement to Interface) will be out on Sunday.

Be sure to have a read of the jQuery 1.2 release notes, but here are a few highlights that I can't wait to use:

  • Partial .load()

    If you want to load a whole web page with Ajax, but only want to cut out a portion of the page and stick it in to an element on your page, you can now do this:

    $('#stats').load('same_page.php #stats');
    

    This will make it really easy to refresh an element on the page without needing to change the server-side logic.

  • New Traversing Features

    Traversing and selecting remain the most powerful features in jQuery, and it'll be great to be able to use .andSelf() to include the original set when traversing, and .prevAll()/.nextAll() to select all the siblings to one side or the other.

  • New Effect Features

    There are a ton of new Effects features, but my favourite is probably .stop(), allowing us to finally stop animations. Together with the new :animated selector, it's trivial to stop all ongoing animations at once:

    $(':animated').stop();
    

    Some of the other Effects features have a lot of potential, like being able to use percents or animating by a relative amount (eg. add 100px).

    Extremely powerful is the new ability to write extensible animations. I assume they did this to ease in the development of jQuery UI, but this allows all of us to harness the power of jQuery animations to do anything we want! Okay I'm starting to drool...

Oh, one more thing. If you're used to creating elements in jQuery like this:

var link = $('<a>');

then you need to stop using invalid HTML and either make it XHTML compliant or add a closing tag, otherwise it will break in Internet Explorer. This is true since jQuery version 1.1.4, and I've had to make a lot of changes in my code as a result.

Now any of the following is okay:

// okay, XHTML-style
var xhtmllink = $('<a/>');

// also okay, valid HTML style
var htmllink = $('<a></a>');

Private JavaScript Variables

August 26th, 2007

I find myself needing to generate unique IDs in JavaScript a lot lately. Mostly this happens when I'm creating a lot of elements dynamically and I need to assign some unique ID to them so that I can find them later. And I need to do this because I've realised that storing pointers to elements in JavaScript uses a LOT more memory than just storing the ID of an element and finding it later with getElementById().

So anyway, what's a good way of generating unique IDs?

A simple way is to just keep a counter going and increment it every time you access it, like this:

var guid_counter = 0;

for (var i=0;i < 100;i++) {
    // create a new <div> element
    var div = document.createElement('div');

    // assign a unique ID and increment counter
    div.id = 'div_' + (guid_counter++);

    // append to the page
    document.body.appendChild(div);
}

With this you'll end up with 100 <div>s with IDs from "div_0" to "div_99". But our guid_counter is just sitting out there in the open! Someone could come along (like us making a typo) and write guid_counter = 0 or guid_counter-- and mess everything up! We could end up with 2 elements with the same ID!

We can improve on this by using a private variable. Private variables give us more control over global variables like our guid_counter, because we can choose when and how they are accessed and updated. Let's create a function which contains our guid counter as a private variable:

// 'guid' is assigned to the return value of this outer function
var guid = (function() {
    // same guid counter, but we keep it hidden inside here
    var guid_counter = 0;

    // return a function that has access to guid_counter
    return function() {
        // whenever guid() is called, return and increment the counter
        return guid_counter++;
    };

})(); // note the () - this executes the outer function right now!

If this is the first time you're seeing this syntax (or the hundredth) it can be a bit confusing. Really, we're doing the same thing as this:

function generate_guid_function() {
    // nothing outside of generate_guid_function() can access this
    var guid_counter = 0;

    // return a function that has access to guid_counter
    return function() {
        // return and increment the counter
        return guid_counter++;
    };
}

// 'guid' is assigned to return value of generate_guid_function()
var guid = generate_guid_function();

The magic of all this is in JavaScript Closures. The inner function has access to guid_counter because of where it's defined, but our code outside has no way to change or access guid_counter. This is exactly like private instance variables in Java and other languages.

Now, we can safely rely on our guid() function to generate unique IDs without worrying about our guid_counter being touched:

for (var i=0;i < 100;i++) {
    // create a new <div> element
    var div = document.createElement('div');

    // assign a unique ID from our new guid() function
    div.id = 'div_' + guid();

    // append to the page
    document.body.appendChild(div);
}

addDOMLoadEvent Revisited

August 14th, 2007

I've gone back and reworked addDOMLoadEvent. I got rid of the global variables and reduced the size down to 563 bytes!

For the new script and the demo pages, check out addDOMLoadEvent.

Update [Aug. 19, 2007]: Now the script preserves any existing window.onload function, and also executes functions instantly when called after the page has already loaded. But now the compressed version is a hefty 617 bytes.


Unobtrusive Ajax

July 27th, 2007

I wrote a Short Cut for O'Reilly that just came out called Unobtrusive Ajax. You can buy it online for just $9.99.

It's a 57-page PDF that goes into depth on using JavaScript and Ajax unobtrusively, an extension of my Unobtrusive Ajax presentation. From the description:

Unobtrusive Ajax is about making web applications that work for everyone all the time, even if you have JavaScript turned off, or you're using a mobile phone or a screen reader, or however you happen to be using the Web. It's about the separation of behavior (JavaScript), content (HTML), and presentation (CSS).

This short cut will focus on the practical benefits of using Ajax and JavaScript unobtrusively and show you that unobtrusive web development and progressive enhancement benefit both web developers and users of the Web. You'll get to see many simple examples of building web interfaces that are unobtrusive. You'll quickly see that it is actually very easy to make web applications that everyone can use.

When you're finished reading this short cut, you will be able to convince anyone why developing unobtrusively is the best way to build a site with JavaScript and Ajax.

I'd like to give a big thanks to the king of Unobtrusive JavaScript, Christian Heilmann, for giving me a great technical review.

If you're into this sort of thing, or even if you're not but wondering if you should be, I recommend you check it out.


Confusing JavaScript Equality

July 25th, 2007

I got tripped up today by something that took me a few minutes to figure out. I wrote this:

if (a == b == 0) {
    // only execute if both a and b are zero
}

But this was wrong. In fact, you can write this:

alert(3 == 4 == 0); // alerts "true"

Why is that? Because of the order things are evaluated. I made the mistake of thinking == has the same result as doing =:

var x, y;

x = y = 10;

alert(x); // 10
alert(y); // 10

But when you use == like that, it actually compares the firsts two values, then compares the result (true or false) against the 3rd value. It's the same as writing:

alert(3 == 4 == 0); // true
alert((3 == 4) == 0); // true

because 3 == 4 is false, and false == 0 is true!


Hidden Ajax Errors with jQuery

July 4th, 2007

If you use Ajax with jQuery, you may have noticed that you don't get any error messages when things go wrong. Even if you have major bugs in your callback functions, jQuery just silently fails, sweeping any errors under the rug, and leaving you clueless as to what just happened.

For example, you can do this and you won't get any errors:

$.get('page.html', function(){
    this_function_does_not_exist();
});

There are two ways to fix this. You can use $.ajax with the error parameter, passing an error handling function for that particular Ajax call. Or, you can define a global Ajax error handler. If you do a lot of Ajax, and you use Firebug, the latter is a great option. Try this:

$(document).ajaxError(function(){
    if (window.console && window.console.error) {
        console.error(arguments);
    }
});

After running this code, you'll start getting error messages in your Firebug console (if anything breaks with your Ajax calls or callbacks). The error messages aren't the greatest, but at least you don't have to stay in the dark any longer.


Detecting and Debugging Timeouts and Intervals

May 28th, 2007

When you start to cram a lot of JavaScript animations and Ajax onto a web page, it can become tricky to know all the code that's running in the background. When you start to detect some performance issues, it's equally tricky to track down what code is being executed.

Luckily, the only way to get code to run in the background with JavaScript is through the functions setTimeout and setInterval. And more luckily, we can overwrite these functions so that we can know whenever they are being called:

window.setInterval_old = window.setInterval;
window.setInterval = function(fn, time){
    console.log('interval', fn.toString(), time);

    return window.setInterval_old(function(){
        console.log('interval executed', fn.toString());
        fn();
    }, time);
};

window.setTimeout_old = window.setTimeout;
window.setTimeout = function(fn, time){
    console.log('timeout', fn.toString(), time);

    return window.setTimeout_old(function(){
        console.log('timeout executed', fn.toString());
        fn();
    }, time);
};

This will send output to Firebug whenever a timeout or interval is first initiated, and again when the function is actually called.

This solution is rather nice because it will let you know what 3rd party JavaScript widgets are doing in the background as well without needing to add debugging messages to them. You could overwrite nearly any method like this to get similar debugging messages as well (like document.getElementById or Array.prototype.push or practically anything).


Resizing a web layout based on browser size

May 19th, 2007

Some people thought that my new layout was too thin, and I had to agree. Originally, I wanted the width of the text on the page to be in a more narrow, more readable column. I also tried to stick to a layout that could fit within a browser on an 800x600 resolution. The result was a column of text that was less readable because it was too narrow.

Today, I added a bit of JavaScript to the page to resize the layout for anyone with a browser wider than 930px. The JavaScript looks like this:

var body_check = setInterval(function(){
    if (document.body) {
        clearTimeout(body_check);

        if (document.body.clientWidth > 930)
            document.body.className += ' wide';
    }
}, 10);

Every 10ms, this script checks if the body is available yet. As soon as it is, the checking is cancelled, and the 'wide' class is added to the body if the browser is wider than 930px.

I opted for a polling technique instead of using window.onload, or even instead of addDOMLoadEvent, so the design wouldn't noticeably jump when the class was added.

To go along with this JavaScript, I added the following in the CSS:

#body { width: 760px; }
#main h1 { width: 560px; }
#main .section { width: 444px; }

body.wide #body { width: 910px; }
body.wide #main h1 { width: 710px; }
body.wide #main .section { width: 594px; }

I isolated the 3 fixed widths that would need to change, and simply increase each of them by 150px whenever the 'wide' class is added to the body.

I hope this wider design is a bit more readable for the 98% of you with a higher resolution.


Detecting focus of a browser window

May 16th, 2007

If you have some constantly running Ajax or JavaScript updates on a page, it might be nice to pause these when the browser is minimized or in the background, or when the user switches to another tab. After all, there's no sense in using the user's CPU and network if they aren't even watching what you're doing.

To achieve this, we can use the window.onfocus and window.onblur events like this:

function onBlur() {
	document.body.className = 'blurred';
};
function onFocus(){
	document.body.className = 'focused';
};

if (/*@[email protected]*/false) { // check for Internet Explorer
	document.onfocusin = onFocus;
	document.onfocusout = onBlur;
} else {
	window.onfocus = onFocus;
	window.onblur = onBlur;
}

These events work in every major browser (Firefox, Internet Explorer 6/7, Safari & Opera).

Unfortunately, there's no way to tell with JavaScript if the browser is visible to the user. For example, they might be chatting on IM in a small window in front of the browser. You'll also find that the page is 'blurred' when you click into the location bar of the browser (except in Safari). You might want to display a message like "PAUSED" (think Super Mario Brothers) so people know why everything has stopped moving.

I've set up a demo page where you can try this out.

[Update October 14, 2008 - Seems the blur event handler would fire in Internet Explorer when the focus went from the body to an input or link. I've changed them to use document.onfocusin and document.onfocusout instead, which seems to work better. Now, putting focus from the body into an input causes them both to be fired, but one right after the other (onfocusout, onfocusin) resulting in a final "focused" state.]


JavaScript-only Links

March 19th, 2007

JavaScript-only interfaces often have some links that activate some kind of click handler but don't actually go to another page. These are called JavaScript-only links, and there are a bunch of different ways to make them, listed here from crappiest to best:

  • <a href="#" onclick="myFunc();return false">

    This method is really common, and not that great. If you are scrolled down the page, and forget to include the return false, the page will jump up to the top. Also, the user will see "#" in the status bar (not a real problem, but kind of messy). The link requires JavaScript to work. It's basically one big workaround for making some text clickable.

  • <a href="javascript:myFunc()">

    This method gets a lot of slack, but I think it's slightly better than using href="#". True, it's a totally invalid href attribute, and it's pure luck that browsers actually support the javascript: protocol. But it works (if you have JavaScript enabled). And it has a slight benefit (or downside?) that users actually see the name of the function in the status bar, giving them a clue as to what is going on. You also don't need to worry about returning false from the click function.

  • <a href="#myFunc" onclick="myFunc()">

    Very similar to using href="#", this method actually changes the URL to something slightly meaningful. This method can be useful if it changes the page in a repeatable way (displaying a specific tab or hidden area). This way you can add JavaScript that looks in the URL, sees the #myFunc, and recreates that change when the user refreshes the page. If you're tricky, you can even get the back and forward buttons to work. The downside: if the user has scrolled down, the page will scroll back to the top.

  • <span onclick="myFunc()" style="text-decoration:underline; color:#00f; cursor:pointer">

    This is my favourite obtrusive method. If you're going to make a JavaScript-only link, why use a link at all? Anything can be clickable in JavaScript, and using CSS you can make a span look exactly like a link (even the mouse cursor). Downside: this method is still obtrusive, requiring JavaScript, but at least it's honest about it.

  • <a href="alternate.html" onclick="myFunc();return false">

    This is a highly superior method to the others. If JavaScript is disabled, the link still works, and ideally goes somewhere that does the same stuff that the JavaScript function would do.

Disclaimer: I don't recommend using the onclick or style attributes on a regular basis. I also don't recommend making JavaScript-only interfaces ever (unless you have to, and you rarely have to). For more on how not to do all this bad stuff, check out my presentation on Unobtrusive Ajax.


target="_blank" With XHTML 1.1

January 24th, 2007

I received a question this morning from someone asking:

In XHTML 1.1 we cannot use attribute 'target=blank', so what is the solution?

The solution is to use regular links, but to make them open into a new window using JavaScript. To do this, we can add something to the links to flag them as being special, perhaps a class called new-window:

<a href="page.html" class="new-window">Page</a>

Then, use JavaScript to find all the links that have this class name and tell them to open in a new window:

window.onload = function() {
    var links = document.getElementsByTagName('a');
    for (var i=0;i < links.length;i++) {
        if (links[i].className == 'new-window') {
            links[i].onclick = function() {
                window.open(this.href);
                return false;
            };
        }
    }
};

or using jQuery:

$(function(){
    $('a.new-window').click(function(){
        window.open(this.href);
        return false;
    });
});

If you have any other questions like this, feel free to ask me and I'll be happy to answer them here.


Submit a Form in IE with Enter

January 23rd, 2007

Usually, you want to be able to submit a form, like a login or search, just by hitting enter. But sometimes it doesn't work.

Firefox will always let you submit any form by pressing Enter, but Internet Explorer is a little bit picky. I figured out a generalised rule of when it won't work:

There is more than one text/password field, but no <input type="submit"/> or <input type="image"/> is visible when the page loads.

This rule has the following interesting consequences:

  • If you just have a <button/>, hitting enter won't submit the form.
  • Hiding submit buttons by using display:none, positioning them off the page, hiding them inside an overflow:hidden, or any other method will break the enter-to-submit functionality.
  • If a form is hidden when the page loads and is displayed using JavaScript, the enter-to-submit will be broken.

It appears that Internet Explorer scans the page at load time and figures out which submit buttons are visible, then attaches the enter-to-submit functionality to those forms.

To fix these scenarios, you can use the following JavaScript:

function addInputSubmitEvent(form, input) {
    input.onkeydown = function(e) {
        e = e || window.event;
        if (e.keyCode == 13) {
            form.submit();
            return false;
        }
    };
}

window.onload = function() {
    var forms = document.getElementsByTagName('form');

    for (var i=0;i < forms.length;i++) {
        var inputs = forms[i].getElementsByTagName('input');

        for (var j=0;j < inputs.length;j++)
            addInputSubmitEvent(forms[i], inputs[j]);
    }
};

(Of course, it's better if you use addDOMLoadEvent instead of window.onload and addEvent instead of onkeydown.)

If you use jQuery, you can just write:

$(function(){
    $('input').keydown(function(e){
        if (e.keyCode == 13) {
            $(this).parents('form').submit();
            return false;
        }
    });
});

The return false is rather important in Internet Explorer, because it prevents that beep that you might hear if you hit return. The beep is saying "you can't hit enter here!", but return false cancels the key press and therefore the browser won't warn.

Update: As Eric Lentz pointed out, my previous code would submit a form when hitting enter in a textarea. I fixed the solution so the onkeydown event is added to <input> elements only.


When to use inline JavaScript and CSS

December 10th, 2006

Unobtrusive Ajax is about keeping all JavaScript and CSS out of the HTML. This is a good idea. You certainly shouldn't be using onclick or onmouseover attributes in your HTML, and you should really avoid using the style attribute. But what about putting JavaScript inside a <script> element or CSS in a <style> element directly in the HTML?

Most people would recommend that you never do this, that you keep every line of your JavaScript and CSS in an external file. I'd say, never say never. There are certainly occasions I do this, although rare, but I think these scenarios make sense. So let me outline some of these scenarios where you may want to use inline JavaScript and CSS:

  • The JavaScript or CSS is specific to one page

    You may be doing something really specific, such as some animations or ajax in a web application interface, where this code won't be reused on any other pages on your site. In this case, you won't gain any benefits of reuse in an external file.

  • The page won't be accessed very often

    If the page is an obscure page in your web application that users only access once, or only once every few months, then you won't gain the benefits of having the browser cache these external files. You'll actually slow things down by making the browser make two extra requests for the JavaScript and CSS.

  • You just want to use a few lines of JavaScript or CSS

    This is a common scenario with a lot of JavaScript libraries. The instructions often involve including an external file, then putting maybe 1-3 line of JavaScript on the page that initializes some widget. It would be totally wasteful to create an external file containing only 1-3 lines of code. The time wasted downloading an extra file would counteract any benefit gained from caching the file later.

These scenarios don't happen so often. As long as you don't care about the browser caching the CSS and JavaScript separately, and you don't need to reuse the CSS and JavaScript throughout your site, or it's just a few lines of code that isn't worth caching, I see nothing inherently wrong with using inline JavaScript and CSS. Your visitors won't notice or care. It's even valid still, as long as you keep the <style> in the <head> and wrap your JavaScript in a CDATA (for XHTML). So if it makes things a bit easier for you, I say go for it.


Defining functions in a loop

October 19th, 2006

Do you ever try to define a function in a loop, like an event handler for example, and find that it's always the last variable in the array that is pointed at by every event handler? It can take a while to debug this stuff. Let's say you do something like this:

var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
    var link = links.item(i);
    link.onclick = function() {
        alert(link.href);
        return false;
    };   
}

(Stupid example, I know). Well if you run this on a page, you would expect that everytime you click a link, you will get an alert with the href of that link. Well that's wrong! You actually get the href of the last link on the page every time!

This happens because of closures in JavaScript. You would expect that the link variable has the same value inside the onclick function as it does outside. But in fact, every time the loop comes around, link gets a new value, and actually the link variable in all those other onclick functions changes. So when the loop finishes, every onclick function will alert the href of the last link.

So to avoid this, you can do a few things. You can make a separate function that attaches the event like so:

function attachLinkEvent(link) {
    link.onclick = function() {
        alert(link.href);
        return false;
    };   
}

var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
    var link = links.item(i);
    attachLinkEvent(link);
}

This works because inside the attachLinkEvent function, the value of link points at the argument, which is a copy of the link in the loop. This way it doesn't change when the original link changes. Confusing? You bet.

Rather than use this workaround, you can just avoid using the link variable altogether in the onclick function, and replace it with this. Inside an event handler like onclick, this will point at the element that the event handler is attached to. In this case, this points at the link:

var links = document.getElementsByTagName('a');
for (var i=0;i < links.length;i++) {
    var link = links.item(i);
    link.onclick = function() {
        alert(this.href);
        return false;
    };   
}

Of course, real life examples are a bit more complicated. There are also different workarounds to fix the problem. But typically, one of these two solutions will fix things.

For way more information on closures than you will ever need, read Richard Cornford's essay JavaScript Closures


Working around "Click to activate and use this control"

August 29th, 2006

As you may likely be aware, the latest versions of Internet Explorer and Opera have decided not to give Eolas any money for their patent. Instead, users are forced to click on plugins (movies, sound, Flash and Java applets). In other words, anything inside object, embed or applet tags.

Thankfully, there is a way around this. Microsoft has documented some workarounds. I've put together a solution here that works in Opera as well:

// only execute code if 'getElementsByTagName' and 'outerHTML' are supported
if (document.getElementsByTagName && document.body.outerHTML) {
    // repeat code for each affected tag
    var tags = ['object','embed','applet'];

    for (var i in tags) {
        // get all elements with tag
        var objs = document.getElementsByTagName(tags[i]);

        for (var j=0;j < objs.length;j++) {
            var obj = objs.item(j);

            // find param tags within object
            var params = obj.getElementsByTagName('param');
            var inner = '';

            // if there are params, but param tags can't be found within innerHTML
            if (params.length && !/<param/i.test(obj.innerHTML))
                // add all param tags to 'inner' string
                for (var x=0;x < params.length;x++)
                    inner += params.item(x).outerHTML;

            // put 'inner' string with param tags in the middle of the outerHTML
            obj.outerHTML = obj.outerHTML.replace('>', '>' + inner);
        }
    }
}

Download this script here.

The code only executes on browsers which support getElementsByTagName and outerHTML (Internet Explorer, Opera and Safari - but not Firefox). It gets around the patent by dynamically "altering" the outerHTML (even though nothing is changed from the original HTML). But beware: you need to put it in an external JavaScript file to work.

To do this, simply put the code into a file. Let's say it's called eolas.js. Then put this line anywhere after all the object, embed or applet tags:

<script type="text/javascript" src="eolas.js"></script>

Alternatively, you can keep the script tags out of the body by wrapping the code in eolas.js in a function, then use my addDOMLoadEvent function to run the Eolas script once the page has loaded. So if you change eolas.js like so:

function fixEolas() {
    // same code above
}

then just put this in the <head> of the page:

<script type="text/javascript" src="eolas.js"></script>
<script type="text/javascript" src="adddomloadevent.js"></script>
<script type="text/javascript">
addDOMLoadEvent(fixEolas);
</script>

Unfortunately, the JavaScript file will be downloaded unnecessarily for users who don't use Internet Explorer or Opera. But if Safari or other browsers implement the same thing, this JavaScript code will already be in place to fix it.

And there you have it. Three cheers for horrible software patents!

Update: The code I posted before breaks objects with <param> tags in Internet Explorer. It turns out that outerHTML doesn't contain <param> tags, so extra work needs to be done to make sure they stay intact. I've updated the script accordingly.


Avoiding Comment Spam with JavaScript

August 16th, 2006

Originally I explained this on the Code Igniter forum, and since others are blogging it, I thought I should bring it here.

I guess I was nervous about sharing my anti-spam techniques on my own blog in case any spam bots are smart enough to read this article and somehow mutate and adapt. We'll see.

For a while, I had no problems with comment spam. Then I started to get a couple. Then one day I got like 50 at once, so I did something "extreme" - I made it so users have to have JavaScript to submit comments. I have a randomly generated spam key in PHP, and then use something like this on the page:

<form id="cform" style="display:none">
    <input id="txtauthor" name="<?= $spam ?>a"/>
    <input id="txtemail" name="<?= $spam ?>e"/>
    <input id="txturl" name="<?= $spam ?>u"/>
    <textarea id="txtbody" name="<?= $spam ?>b" rows="10" cols="40"></textarea>
    <input type="hidden" id="antispam" name="antispam"/>
</form>
<script type="text/javascript">
    document.getElementById('cform').style.display = 'block';
    document.getElementById('antispam').value = '<?= $spam ?>';
</script>
<noscript>Sorry, you need JavaScript to post comments.</noscript>

So if the spam key is 'xxxx' the author field is 'xxxxa', email 'xxxxe', etc. The spam key is filled using JavaScript. Then on the server side I do this:

if (isset($_POST['antispam'])) {
    $antispam = $_POST['antispam'];
    $cauthor = $_POST[$antispam . 'a'];
    $cbody = $_POST[$antispam . 'b'];
    $cemail = $_POST[$antispam . 'e'];
    $curl = $_POST[$antispam . 'u'];
    if ($cbody && $cauthor)
        addComment($id, $cemail, $cauthor, $cbody, $curl);
}

This has majorly cut down on the number of comment spam I get. I still get the occasional one here and there, but they must all be done by hand instead of with some automated bot.

Unfortunately, this method means that users without JavaScript can't post comments on here. I regret that, but since nobody posts comments on here anyways, I figure it's not such a loss. :) One day, I would like to add some kind of captcha or approval system to allow posting of comments without JavaScript.


Let people turn off ads Part 2

August 13rd, 2006

As I warned yesterday, I've put ads back on the site. Currently, I only have them on the individual blog post pages (the pages that have comments on them). If you only read from the home page, or if you read via RSS, you won't see them.

I also walked my own talk by letting people turn off the ads if they wish. I did it entirely with JavaScript, so I thought I'd share my methodology with you.

The HTML just looks like this:

<div class="ads">
    <script type="text/javascript" src="banner-ad.js"></script>
</div>

And the JavaScript in banner-ad.js looks like this:

/* Customized Google Adsense codes */
google_ad_client = "pub-3809601305027895";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text";
google_ad_channel ="8087340205";
google_color_border = "99bbee";
google_color_bg = "99bbee";
google_color_link = "000000";
google_color_url = "000000";
google_color_text = "000000";

/* cookie functions from QuirksMode */
function setCookie (name,value,days) {
    if (days)
    {
        var date = new Date();
        date.setTime(date.getTime()+(days*24*60*60*1000));
        var expires = "; expires="+date.toGMTString();
    }
    else var expires = "";
    document.cookie = name+"="+value+expires+"; path=/";
}

function getCookie (name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++)
    {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}

/* onclick function to remove ads */
function removeAds() {
    /* set a cookie for 10 years to stop showing ads */
    setCookie('hideads', '1', 365*10);

    /* hide every div on the page with the class name 'ads' */
    var ads = document.getElementsByTagName('div');
    for (var i=0;i < ads.length;i++) {
        if (ads[i].className == 'ads') {
            ads[i].style.display = 'none';
        }
    }

    return false;
}

/* don't show the ads if the cookie is there */
if (getCookie('hideads') != '1') {
    /* write out the link to hide the ads */
    document.write('<a href="#" onclick="return removeAds()">Remove Ads</a>');

    /* write out the regular Adsense script tag */
    document.write('<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>');
}

When the 'Remove Ads' link is clicked, the ads are instantly hidden using CSS and a cookie is set. Next time this script is loaded, as long as the cookie is set, the ads won't be written to the page anymore.


Cross-Domain JSON without XHR

August 11st, 2006

I was just reading on the Ajaxian a quote from an article Why XHR should become opt-in cross-domain, so I started thinking, isn't there a way around this already?

A quick explanation of what I'm talking about: XMLHttpRequest (the function behind Ajax) will only let you connect to URLs on the same domain as the page you're on. This means the Ajax happening on xyz.com can only connect to URLs on xyz.com. Even if XYZ wanted to use Ajax to pull some data from foobar.com, they wouldn't be able to.

Now there is already one popular way around this. You can make a proxy page that gets the data for you. Using the same example, XYZ could make a URL like xyz.com/json_proxy that pulled data over from foobar.com behind the scenes, thus making the foobar.com data available on xyz.com. The problem with this is, XYZ would have to handle double the bandwidth of this data unnecessarily. This can be a big problem for smaller companies.

I have an alternative solution to this problem. Instead of using XMLHttpRequest to load the data, we can just use <script> tags instead. These don't have the same cross-server restrictions as XMLHttpRequest. In fact, this is how Adsense works. You stick a <script> tag on the page which executes JavaScript coming from Google's server, and this writes out the ads on the page.

To add a <script> tag to our page, we only need to call this function:

function addScript(url) {
    var script = document.createElement('script');
    script.src = url;
    document.body.appendChild(script);
}

If we have a URL that returns some JSON data, we just pass that URL to this function and the page will load that data. Well, not entirely. There's a few things we need to do different. With JSON, you have some data like:

{
   "firstname": "Jesse",
   "lastname": "Skinner"
}

and then use XMLHttpRequest with eval() to assign it to a variable. If you just stuck a script tag on the page to load this, nothing would happen. It would just load and the JavaScript parser would say "Great, nice little piece of data." But it's not going to assign it to a variable, so you'll have no way to access it.

There's actually no way around this unless you change what is returned by the JSON data URL. This is the special trick. We would need the JavaScript to execute some function or assign the data to a variable. It would need to look like this:

json_callback({
   "firstname": "Jesse",
   "lastname": "Skinner"
});

Now, we just need to make a simple function called json_callback() that takes the data and does something with it. Voila! Cross-server JSON.

Rather than hardcode some callback function name, it would probably be better to add an optional "callback" parameter that would specify the function name. If the callback parameter is missing, the server can return standard ol' JSON.

Let's say our simple JSON example above came from "foobar.com/data?format=json". If example.com was kind enough to provide this flexibility, they could add the parameter so that "foobar.com/data?format=json&callback=my_json_callback" formats the data as a function call, passing the data object to my_json_callback().

Now, we just need to wait for web APIs with JSON to start supporting this parameter.

Update: I've since discovered that Yahoo!'s JSON API already provides a 'callback' parameter exactly as I've described above, and I suspect that other APIs out there may support this as well. Either I'm psychic or there are time travellers working at Yahoo! ;) Here are the details from Yahoo!


JSON is not just Object Notation

August 4th, 2006

Until now, I've just considered JSON to be the equivalent to regular JavaScript object literal notation. Jonathan Snook has now explained the difference. It turns out JSON is a bit more picky than regular object literals. Key names must be quoted, and you can only use double-quotes around keys and values. Also, functions are not allowed as values.

I gave an example of JSON some time ago that turns out to be wrong. I used single-quotes in my JSON example. I'll leave them there since the example works, but I just can't call it JSON.

If you're doing JSON-style coding by hand for your Ajax communications, you can get away with using any object literals. So why care if it fits the JSON standard? Well, there are parsers and tools out there which can work with the JSON format, and if you don't fit the standard, they might not work.

I have no idea why the designers of JSON didn't just make the standard flexible enough to handle single quotes and unquoted keys. It's also confusing that JSON stands for JavaScript Object Notation, and that Object Notation somehow means something different from the object literal notation in JavaScript. Great.

Moral of the story: it probably doesn't matter if you use the JSON standard until you have to interoperate with JSON tools or feel the need to offer your JSON data to the public. You should probably stop calling it JSON if you use single-quotes, though.


addDOMLoadEvent

June 21st, 2006

Update March 2014: This code is now available on GitHub

There has been a problem with using window.onload in JavaScript. This event handler waits until all the images and other files load before executing. If you need some JavaScript to execute when the page loads, you usually only need the HTML to be downloaded, not all the images.

The event to use for this is "DOMContentLoaded", but only Firefox (and recently, Opera 9) support this. There are different ways to do this in every other browser, and people have been working on finding a complete solution to this for some time.

Very recently, this problem has been solved by Dean Edwards, Matthias Miller and John Resig. There is a unique solution for Internet Explorer, Safari, and for W3C-compatible browsers (Firefox and Opera 9).

Very soon after, Dan Webb adapted the solution to prototype. Unlike the original solution, this code allows you to add more than one function to be executed when the DOM loads.

Inspired by Simon Willison's addLoadEvent function, I wanted to create a standalone generic solution that anyone could use without needing a specific framework.

So here's the js file: adddomloadevent.js, and here's the actual code:

/*
 * (c)2006 Jesse Skinner/Dean Edwards/Matthias Miller/John Resig
 * Special thanks to Dan Webb's domready.js Prototype extension
 * and Simon Willison's addLoadEvent
 *
 * For more info, see:
 * http://www.thefutureoftheweb.com/blog/adddomloadevent
 * http://dean.edwards.name/weblog/2006/06/again/
 * http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype
 * http://simon.incutio.com/archive/2004/05/26/addLoadEvent
 * 
 *
 * To use: call addDOMLoadEvent one or more times with functions, ie:
 *
 *    function something() {
 *       // do something
 *    }
 *    addDOMLoadEvent(something);
 *
 *    addDOMLoadEvent(function() {
 *        // do other stuff
 *    });
 *
 */
 
addDOMLoadEvent = (function(){
    // create event function stack
    var load_events = [],
        load_timer,
        script,
        done,
        exec,
        old_onload,
        init = function () {
            done = true;

            // kill the timer
            clearInterval(load_timer);

            // execute each function in the stack in the order they were added
            while (exec = load_events.shift())
                exec();

            if (script) script.onreadystatechange = '';
        };

    return function (func) {
        // if the init function was already ran, just run this function now and stop
        if (done) return func();

        if (!load_events[0]) {
            // for Mozilla/Opera9
            if (document.addEventListener)
                document.addEventListener("DOMContentLoaded", init, false);

            // for Internet Explorer
            /*@cc_on @*/
            /*@if (@_win32)
                document.write("<script id=__ie_onload defer src=//0><\/scr"+"ipt>");
                script = document.getElementById("__ie_onload");
                script.onreadystatechange = function() {
                    if (this.readyState == "complete")
                        init(); // call the onload handler
                };
            /*@end @*/

            // for Safari
            if (/WebKit/i.test(navigator.userAgent)) { // sniff
                load_timer = setInterval(function() {
                    if (/loaded|complete/.test(document.readyState))
                        init(); // call the onload handler
                }, 10);
            }

            // for other browsers set the window.onload, but also execute the old window.onload
            old_onload = window.onload;
            window.onload = function() {
                init();
                if (old_onload) old_onload();
            };
        }

        load_events.push(func);
    }
})();

Update: I added a demo page, as well as a compressed version (only 876 bytes).

Update: Thanks to Adam Schlag's suggestion, I fixed the memory leak in IE.

Update: Thanks again to Rob Cherny and Alistair Potts, I've updated the solution so that it works over HTTPS without any security issues. I also decreased the compressed size to 761 bytes. Now it's perfect! (I hope!)

Update: I've gone back and reworked the code to get rid of the global variables, and I've managed to reduce the compressed size to 563 bytes!

Update [Aug. 19, 2007]: Now the script preserves any existing window.onload function, and also executes functions instantly when called after the page has already loaded. But now the compressed version is a hefty 617 bytes.


Unobtrusive JavaScript

June 2nd, 2006

You might have heard of unobtrusive JavaScript, but what the hell is it? Well, it's the separation of all your JavaScript from your HTML. That means putting all your JavaScript either in a <script> block, or even better, in an external file.

Why bother? Yes, sometimes it's easier to put JavaScript into onclick or onload attributes. But it's better to separate your content (HTML) from your presentation (CSS) from your behaviour (JavaScript). It keeps your HTML clean. It also makes things easier to maintain.

It's not just about the physical separation of JavaScript from HTML. It's the absolute separation. Your HTML should work without JavaScript. For example, if you have an empty <body> tag, and then attach use Ajax to pull content from the server and stick it into the body, that sucks. If somebody comes to your site without JavaScript (ie. the Google spider), they will get an empty page.

It's important that your whole site works without JavaScript. This way, your content can be used by people without JavaScript. Google's spider doesn't support JavaScript (to my knowledge). So, if you want your content to be indexed, make sure your HTML content can stand alone without JavaScript.

Unobtrusive JavaScript means adding JavaScript functionality on top of an already functional HTML site. This improves things for those with JavaScript without taking anything away from those without. Let's look at some simple examples.

The onload attribute on the <body> tag is popular. Instead of doing this:

<script>
function myLoadScript() {
   // do fancy stuff
}
</script>

<body onload="myLoadScript()">

you can just do this:

<script>
window.onload = function () {
   // do fancy stuff
}
</script>

<body>

There's no difference in functionality, but it does let you put your script in another file and have less stuff in the HTML. This way, you can change or remove your onload function without changing the HTML.

Here's another example. Let's say you have some JavaScript in a link to launch a popup window:

<script>
function showPopup() {
    window.open('myPopupPage.html',null,"width=600,height=400,toolbar=no");
}
</script>

<a href="javascript:showPopup()">I love popups!</a>

If somebody doesn't have JavaScript, this won't work. That sucks. You can do this instead:

<script>
function showPopup() {
    window.open('myPopupPage.html',null,"width=600,height=400,toolbar=no");
    return false;
}

window.onload = function () {
   document.getElementById('popupLink').onclick = showPopup;
}
</script>

<a id="popupLink" href="myPopupPage.html" target="_blank">I love popups!</a>

If somebody doesn't have JavaScript, they'll still get the normal link popup from the HTML alone. You won't be able to customize the size, toolbars, etc. but at least it will still partially work.

Notice that the showPopup() function now returns false. This will stop the page from doing what it normally does when you click the link. In other words, you'll only get the JavaScript popup, not two popups.

This technique can be applied to more complex JavaScript including Ajax techniques. If you have a form that is submitted in the background, and uses JavaScript to redraw the page without refreshing, great. Just add this functionality using JavaScript alone. Make sure that without JavaScript, the form just submits and the page refreshes the old-fashioned Web 1.0 way.

I will quickly mention something to silence those who are screaming at their monitors. document.getElementById doesn't work in every browser (like Internet Explorer 4 or Netscape 4), so you may want to use object detection to make sure it's supported. My policy is that if someone is using an older browser, they get the same thing as if they had JavaScript turned off.

I hope these techniques will help you to create wonderful, clean, sexy and modern unobtrusive JavaScript. Have fun!


Firebug 0.4

May 30th, 2006

If you haven't yet, go get the Firebug 0.4 extension for Firefox from Joe Hewitt. Roger Johansson mentioned it, and Justin Palmer posted an in-depth walk through of the many really great features in Firebug.

The days of using alert() for debugging are over (except, perhaps, in other browsers. You can log messages and objects to the console (at info, warn, debug and error levels), trace execution, and even fire up a breakpoint with the keyword 'debugger'! Not to mention all the typical error reporting stuff and DOM walkthrough features you'd expect. Go check it out!


Using Ajax Without Server-Side Scripting

May 23rd, 2006

Ajax, by which I mean XMLHTTPRequest, is almost always used with some sort of server platform, such as PHP or Java, usually to retrieve data from a database. This might scare off some people from using XMLHTTPRequest, especially those who don't have the ability or knowledge to do server-side scripting. This is fine. You can actually do some things without it. I'll do a simple example with populating select boxes.

HTML

First off, we need some HTML to work with. For this example, we'll have two select boxes. When the first one changes, we want the second one to fill up with data from the server.

<form>
    <select id="one">
        <option value="">Please choose one...</option>
        <option value="colours">Colours</option>
        <option value="numbers">Numbers</option>
        <option value="letters">Letters</option>
    </select>

    <select id="two">
    </select>
</form>

Data

Now we need some data files. Before I begin, I'll just mention that there are several ways to get back data from the server. Some people use XML, others use something called JSON which stands for JavaScript Object Notation, you can also get back raw HTML or JavaScript, and then there are other ways as well. For this example, I'm going to use JSON because it's pretty simple. You can do really advanced stuff with JSON, but I'm just going to use a JavaScript array. I'm going to create a file for each select option in select box 'one'. Each file will have an array of values to put into select box 'two'.

Update: technically this isn't JSON, even though it works. More information here.

select_colours.js:
['red','orange','black','purple','yellow','forest green']

select_numbers.js:
['3242','84930','12','5433344','8837845','1980']

select_letters.js:
['G','f','s','j','P','m']

This example isn't using a lot of data, but chances are in real life, you wouldn't use Ajax unless you had a lot of extra data to get back from the server. Otherwise, it wouldn't be worth going to the server to get the data, and you might as well just put the data directly into the page.

JavaScript

Any usage of XMLHTTPRequest starts with a function similar to this. It finds the XMLHTTPRequest object in a way that works across all browsers, then it sets up a callback function to do the dirty work, and sends the request to a url:

function httpRequest(url, callback) {
    var httpObj = false;
    if (typeof XMLHttpRequest != 'undefined') {
        httpObj = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        try{
            httpObj = new ActiveXObject('Msxml2.XMLHTTP');
        } catch(e) {
            try{
                httpObj = new ActiveXObject('iMicrosoft.XMLHTTP');
            } catch(e) {}
        }
    }
    if (!httpObj) return;

    httpObj.onreadystatechange = function() {
        if (httpObj.readyState == 4) { // when request is complete
            callback(httpObj.responseText);
        }
    };
    httpObj.open('GET', url, true);
    httpObj.send(null);
}

Now, we'll add three more JavaScript functions. First, we'll add a callback function to take our JSON data and put it into the select box:

function fillSelect(JSON) {
    var selectTwo = document.getElementById('two');
   	
    // clear out existing options
    while (selectTwo.options.length) {
        selectTwo.options[0] = null;
    }
	
    // fill with new options from JSON array
    var data = eval(JSON);
    for (var i=0;i < data.length;i++) {
        selectTwo.options[selectTwo.options.length] = new Option(data[i]);
    }
}

Notice we use the eval() function to turn our JSON text string into a real JavaScript object.

Next, we'll add an event handler function to react when the first select box changes. This will send off the actual XMLHTTPRequest:

function onSelectChange() {
    // get the value of the selected option in select box 'one'
    var selectOne = document.getElementById('one');
    var selectedOption = selectOne.options[selectOne.selectedIndex].value;

    if (selectedOption != "") {
        // find the appropriate javascript file
        httpRequest('select_' + selectedOption + '.js', fillSelect);
    } else {
        // empty the options from select box two
        fillSelect('[]');
    }
}

Lastly, we need to assign the onSelectChange() event handler to the select box. We'll do this in a window.onload function:

window.onload = function() {
    var selectOne = document.getElementById('one');
    selectOne.onchange = onSelectChange;
}

Conclusion

There you have it! If you want to see it in action, click on the files listed below:


Finding Max and Min

April 24th, 2006

Maybe this is common knowledge to some, but I just figured it out today, so I thought I'd share it.

Before, if I wanted to find the maximum or minimum number in a list of 3 or more numbers, I'd do something like:

max = Math.max(Math.max(Math.max(a, b), c), d);
min = Math.min(Math.min(Math.min(a, b), c), d);

But Math.max and Math.min take as many arguments as you need, so you can do this instead:

max = Math.max(a, b, c, d);
min = Math.min(a, b, c, d);

Setting a form field to null or undefined

April 4th, 2006

I came across another browser difference while debugging some JavaScript. Let's say you were trying to set the value of a form field the following way:

document.getElementById('formField').value = someFunction();

But, it just so happens that someFunction() returns null. What happens when you set a form field value to null? Well, in Firefox and Safari, the value will be set to the empty string, "". Internet Explorer and Opera will set the field to the string "null".

Similarly, if the function forgot to return a value, the form field would get set to "undefined" (this is the same in every browser).

The moral of the story is, be careful not to let a form field value get set to null. You could avoid both these situations by doing something like:

document.getElementById('formField').value = someFunction() || "";

Note: This would actually replace undefined and null as well as 0 and false with "".


onAfterClick

March 10th, 2006

Okay, some time ago I posted onAfterPaste, a way to run some code after pasting. You can actually do the same thing with any event. Let's say you want to put an onclick handler on a submit button, but you don't want the function to execute until after the form is submitted. You may want to close the window after launching a new window or submitting a form, but putting window.close() in the onclick would prevent the form from being submitted. Then, do this:

function onAfterClick (e) {
     window.close();
}

var submit= document.getElementById('submit');
submit.onclick = function(e) {
     setTimeout(function() {
          onAfterClick(e);
     }, 1);
}

That's it. It's like the browser processes the click handler, then goes and does it's default click behaviour, then executes the timeout function. So it's almost a kind of magic.. but not really. Anyway, setTimeout is a great tool for making things happen at the right time.


Bursting the Event Bubble

February 15th, 2006

Have you ever wanted to prevent a JavaScript event from firing when it has been bubbled up from a child element? For example, let's say you have an onMouseOver event for a parent element. Anytime the mouse moves over any of the children elements, the parent's onMouseOver event will keep firing. Or, let's say you have an onMouseDown event on a parent and another on its child. Both onMouseDown events will fire when someone clicks the child. Sometimes this can be a real pain.

Anyway, enough examples, let's look at a way to avoid this. We need a way to fire an event only when the element with the event handler is the target element of the event. Here's the solution event handler:

function eventHandler(e) {
     e = e || window.event;
     var target = e.srcElement || e.target;
     if (target != this) return;
     
     // the rest of the function
}

Simple enough, once we have the code. Let's look at what it does.

First, we find the event object. In Firefox and Safari, it's a parameter to the function. In Internet Explorer, it's in window.event.

Having that, we need to find the target in the event object. This is the first element the event fires on, the inner-most or top-most element. In Safari and Firefox, it's target, but in Internet Explorer it's srcElement.

Finally, we will compare against the element the event is attached to. The element the event handler is attached to can be accessed easily with the this variable.

For more information on event handlers, I highly recommend the excellent resources at QuirksMode:

Update: you need to do a little bit more if you are dealing with mouseout events. If you move the mouse into a child, this triggers the mouseout event on the parent. In this case, the target will match the parent. So, you'll also have to get the 'related' element (the child) and make sure the target isn't one of the ancestors. Here is the updated function for mouseout events:

function mouseOutEventHandler(e) {
     e = e || window.event;
     var target = e.srcElement || e.target;
     if (target != this) return;

     var related = e.relatedTarget || e.toElement;
     while (related != this && related.nodeName != 'BODY')
          related = related.parentNode;
     if (related == this) return;
   
     // the rest of the function
}

Special thanks to QuirksMode's Mouse Events page for helping me figure out that one!


Element dimensions on QuirksBlog

January 23rd, 2006

Peter-Paul Koch just announced that he's put up a new test page comparing the JavaScript dimension and positioning variables (offsetWidth, scrollHeight, etc.) across multiple browsers.

As a quick side note, QuirksMode is my favourite JavaScript and CSS reference, when it comes to figuring out browser support and differences. His examples are so clean and to the point. Thanks, Peter-Paul!


JavaScript Speed Detection

December 14th, 2005

This question came up on the css-discuss mailing list today (asked by Howard 'duke' Holtz):

I would like to include a Flash header on the homepage, but switch to a standard gif header for viewers with slow connections. I was hoping that there is a way to maybe have two “includes”, and use one or the other – but first I need some kind of 'conditional switch' or 'logic switch'.

This is a great question. I don't think any website has addressed this issue through scripting. We've all seen websites that start off with "Click here for Flash, Click here for slow connections" pages. But what about using JavaScript to test the speed of a connection?

I started experimenting with JavaScript and came up with a fairly simple, clean solution. It is all based on the onload event of an image. What we will do is time how long it takes to load an image. If the time is below our threshold for "high speed", we will display the flash content. Otherwise, we will do nothing and display the default content.

<script type="text/javascript">
var start = (new Date()).getTime();
var THRESHOLD = 2500; // a number we will calculate below

function speedTest() {
    var duration = (new Date()).getTime() - start;
    if (duration < THRESHOLD) {
        // fast connection (display flash)
    } else {
        // slow connection (do nothing?)
    }
}
</script>
<img src="some-image.jpg" onload="speedTest()">

The onload event can go on any image on the page, preferably the largest image to give us a better estimate. Let's say we only want users downloading faster than 10k/s to get the flash code. We check the size of our some-image.jpg, and see that it is 25kb. We can easily calculate the THRESHOLD value like so: (25kb) / (10k/s) = 2.5s. Converting this to milliseconds gives us 2500, so this is what we will use for the THRESHOLD.

If you're using Bobby VanDerSluis' Unobtrusive Flash Objects 2.0, you can put the UFO code to load Flash in the block for users with a fast connection. You can then do nothing for users with a slow connection and just let them see the default content on the page.

I have thought of one problem with this solution: if the image is in the user's cache, the number will be very low. This will cause the flash to be loaded if they come back later. To avoid this, you could add a no-cache rule on the webserver for this image. Or, instead of using an image on the page, you could load an image in the background with JavaScript using a random URL like so:

var testImage = new Image();
testImage.onload = speedTest;
testImage.src = "some-image.jpg?random=" + Math.random();

Of course, if you are using PHP or another scripting language on the server, you could generate the random URL a number of different ways. This fix also has the downside that the visitor must download this extra image every time. You'll have to balance these factors in your solution to come up with one that fits.

Good luck! I'd love to hear your comments, suggestions and questions about this. Let me know how it goes on your own web sites!


No-JavaScript CSS

November 28th, 2005

On one of my web sites, I use JavaScript to show/hide div layers containing each section. I have links to "#section-five" for example, but the onclick event changes the section-five div to display: block; and the previous div to display: none;. This is designed so that browsers without CSS or JavaScript will still be able to use the links as regular anchor links. This assumes, though, that the visitor would either have both CSS and JavaScript, or neither.

Since I wrote this, I've been struggling with how to deal with visitors who have CSS but not JavaScript. Today I figured out a way to do this.

The typical solution would be to have display: block; set by default, then to use JavaScript to set display: none; onload. I tried this solution, but I didn't like how all the layers appeared at first, then suddenly disappear. I wanted a solution that wouldn't affect the experience for most users.

All I needed to do was display different CSS to users without JavaScript. To accomplish this, I first considered setting disabled on the style sheet, then using JavaScript to enable the CSS link. This didn't work right in Firefox because Firefox ignores the disabled attribute of link tags.

Next, I tried setting the href on the link tag to href="", then dynamically setting the href using JavaScript. This worked good, except then users without JavaScript would be forced to see the page without CSS. It was perfectly usable but looked pretty boring.

Finally, I came up with this compromise. The link tag on the page is:

<link rel="stylesheet" type="text/css" media="screen"
 href="nojavascript.css" id="stylesheet"/>

so by default, visitors will get a "nojavascript.css" CSS file. This file contains:

@import "screen.css";
div.section { display: block; }
#ads { display: none; }

So without JavaScript, the page has all the div layers displayed, except the ads. (Adsense and Chitika won't work without JavaScript anyway.) Also, by using an @import, I won't have to maintain two almost-identical CSS files.

Next, I added a line to the JavaScript file attached to the page:

document.getElementById('stylesheet').href = "screen.css";

Note that I didn't put it in an onload function. I don't want the page to change once it's been loaded; I want it to look right from the start. If you do this, you'll have to make sure that the <script> tag comes after the <link> tag.

The other nice thing about this method: it ensures that users with browsers that doesn't support getElementById will still get the nojavascript.css file. This is important because it is the only method I use for handling the onclicks.

That's it. Simple, yet powerful.


A Few More Tools

November 14th, 2005

People have started using some of the tools I put up lately, so I thought I'd take a few minutes and add a couple more common ones. I'm open to suggestions, if you think of anything else that would be useful.


onAfterPaste

November 10th, 2005

Working with designMode="on" or contentEditable (what can we call this? I want to say Rich Text Editors or Midas Editors Web-Based HTML Editors or something. We don't really have a nice buzzword for this..), a common problem is dealing with pasted content. The user can paste any HTML into these areas, and more often than not, web applications don't want form elements or IFrames or other HTML included in the content.

It's not so difficult to process the innerHTML of a document to strip out bad HTML using regular expressions. The problem is, when can this cleaning happen?

Mozilla has no paste events at all. Internet Explorer has onBeforePaste and onPaste events, but no onAfterPaste. onPaste fires when the user pastes, but before the HTML actually goes into the editor. The idea is that the developer has a chance to look into the clipboard using window.clipboardData.getData(). Unfortunately, you can only retrieve the contents in URL or Text format, not HTML. Instead, it would be easier to allow the HTML to be pasted, then process the editor contents afterwards.

To accomplish this in Internet Explorer, we can simply set a timeout in the onPaste event. This works by allowing the browser time to finish its internal onPaste event before executing the code in the timeout. The onPaste event needs to be attached to the BODY of the editor IFrame using designMode, or the DIV element when using contentEditable.

function onPasteHandler(e) {
     setTimeout(function() {
          // editor cleaning code goes here
     }, 1); // 1ms should be enough
}

In Firefox, we can't use paste events. However, probably the best we can do is set a keypress handler and look for CTRL+V or SHIFT+INSERT and then do the same thing with a timeout. The keypress event handler needs to be attached to the document element in the IFrame.

function onKeyPressHandler(e) {
     if ((e.ctrlKey && e.keyCode == e.DOM_VK_V)
      || (e.shiftKey && e.keyCode == e.DOM_VK_INSERT)) {
          setTimeout(function() {
               // editor cleaning code goes here
          }, 1);
     }
}

This should only work with Mozilla/Firefox because e.DOM_VK_V and e.DOM_VK_INSERT are not defined in Internet Explorer.

Also note that there are still other ways to get HTML into the editor through Drag-and-Drop, or by using Edit>Paste on the Firefox menu. If you are serious about stripping HTML you will need to do it at other times as well. At least this way it will happen quickly enough that the editor won't misleadingly contain these elements, only to strip them out at an unpredictable time in the future.


New Tools

November 6th, 2005

Just for fun, I added a few simple JavaScript powered conversion tools, and PHP powered "geek" tools. There are just a few, but I will probably add more in the future. I don't know if they will be of use to anyone (except me perhaps), but feel free to steal the JavaScript for your own purposes.