Archive for the 'javascript' Category

Inline Prototype Templates

If you've ever used Prototype's Templates, you'll know how powerful they are. If you haven't tried them, you should. One thing that makes them even more powerful is creating them from in-your-page HTML, rather than as JS Strings. This lets your designer build templates in raw HTML, and then you can transparently suck them into Template objects for use in dynamic rendering. At it's simplest:

<div id="myTemplate"><h1>#{text}</h1></div>
<script type="text/javascript">
  t = new Template($("myTemplate").innerHTML);
</script>

There are a couple gotchas with this technique, however.

The first is that your markup must be valid (X)HTML in the context of your document itself. So if you need a template for a table row, you have to put the markup for that template inside a "throwaway" table. If you can't package your template as valid markup, then you need to supply it into the DOM as the value of a TEXTAREA element, and extract it from the TEXTAREA element's value (using $F()) . This is a slightly inferior solution because you're not right in the DOM at that point, but only marginally.

When can't you package a template as valid markup? Here's a simplified example from an app I was working on today:

<textarea id="template_tableSkeleton">
<table>
<thead>
  <tr>#{headers}</tr>
</thead>
<tbody>
  #{rows}
</tbody>
</table>
</textarea>

The problem here is that you can't put content (the substitutions) directly in TR or TBODY elements. If you put that markup directly in the page, you'll end up with the substitutions in front of the opening TABLE tag, which is definitely not what you want. Using the TEXTAREA makes the browser treat it as raw text, so you don't get the rendering-based flip-flop.

The second one is a bit more subtle, and has to do with links. Take a look at this example:

<div id="myTemplate"><a href="#{href}">#{text}</a></div>
<script type="text/javascript">
  t = new Template($("myTemplate").innerHTML);
</script>

The problem is that the 'href' attribute of an A element in the DOM is url-escaped as part of parsing.  At least that's what the symptoms seem to indicate.  The template still contains the '#{text}' substitution, but it no longer contains the '#{href}' substitution, instead it contains the string '#%7Bhref%7D' (i.e. the braces were url-escaped). You could get around it by using the TEXTAREA trick, but you can solve it more directly by doing a 'replace' on the template string:

<div id="myTemplate"><a href="#{href}">#{text}</a></div>
<script type="text/javascript">
  t = new Template($("myTemplate").innerHTML.replace(/#%7B([a-zA-Z0-9_-]+)%7D/gi, "#{$1}"));
</script>

That will unescape any url-escaped substitutions in the template, allowing it to again render correctly.

These couple tricks apply to whatever templating toolkit you're using, because they're outside the templating engine itself.  Prototype's is the one that I'm most familiar with, but there are others.  TrimPath is one example with a far richer templating language (conditionals, loops, macros, inline script, etc.).  Overkill in many cases, especially if you're already using Prototype, but very rich if you need the flexibility.

JavaScript. At Last.

Working on gpsracr.com this evening, and whipped open my favorite chunk of JS: Prototype. It just gets better all the time. They've added support for automatically executing text that is returned from an Ajax.Request with an appropriate MIME type (like text/javascript), which means you don't even need result handlers. Just dispatch to your URL with whatever parameters you need, and let CF emit back whatever JS should run as a result of the submission (error, failure, success, whatever). Talk about sweet.

Now that might not sound like a big deal, but it really is. I use Fusebox. So all my links are parameterized with XFAs. But XFAs are server-side. Client-side JS (especially static JS in *.js files) can't really access XFAs very well, so URL building is a pain for remoting endpoints and/or hard context switches, But if you're generating the JS server-side….

The specific use case was a login form. I want to "submit" it asynchronously and either display a "denied" message if there's a failure, or forward to the inside of the app if it succeeds. Ordinarily, you'd have to submit the credentials, get some sort of status back from the server, and then use JS to do something with it. Now I can just submit the credentials and let CF emit the JS that I want to execute as a result. I love it.

Improved ComboBox Demo

After Koen (a coworker) pointed out that my online ComboBox demo was rather user-unfriendly (like how you just had to "know" to type an 'a' in the fields), I threw some instructions around it. It's available at /web/widgets/, as before. There's also a new distro at /web/widgets/widgets-b2231.zip, and the project page has been updated as well.  The actual code is unchanged, only the demo file has been updated.

ComboBox Widget Update

I've been making tweaks to my ComboBox widget (first published in July 2005) in various apps of mine that use it, and figured it was time to publish an update. You can download the archive at /web/widgets/widgets-b2218.zip, play with the online demo (which is just the expanded archive) at /web/widgets/, or visit the project page for most info.  I believe I've fixed every bug that people have brought to my attention, but if I missed something, let me know.

New and notable features (in no particular order):

  • multiple delimited values (defaults to comma delimited)
  • the ESC key will now close the dropdown, if it's open
  • the TAB key will select the current value and move focus off the combobox
  • automatic hiding of SELECT, APPLET, and EMBED elements under the dropdown (for IE)
  • automatic setting of autocomplete="off"
  • changed to update only ever 250ms, rather than on every key press, which greatly reduces needless churn with JS remoting-based dropdowns
  • added "Loading …" message while waiting for dropdown values (for JS remoting-based dropdowns)
  • removal of already-selected items from the dropdown (only useful for multi-select)
  • added wrapping with the arrow keys (pressing 'up' when the first item is selected will move the selection to the last item)
  • support for arbitrary HTML in the dropdown values that will be stripped to create the actual form field value

There are a few known issues:

  • if your dropdown is larger than it's allowed side, scrollbars will appear, but the scrolling won't reset when new items are loaded
  • untested on IE aside from a few spot checks. However, except for the hiding of SELECT/APPLET/EMBED elements (which is fixed), I've not received any IE-specific complaints, so I'd say it's pretty sound.
  • documentation is somewhat lacking.

I'm sure I'm missed some, but those are the high points. As always, it's MIT licensed so you can basically do whatever. If you find it useful (or think it's a piece of garbage), I'd love to hear about it.

Scriptaculous Droppables Tweak

Scriptaculous has some fairly slick drag and drop facilities built in, though they're clearly targeted at backing some of the other features (like Sortables). I was playing today and noticed the lack of an onUnhover callback corresponding to the onHover method of a Draggable (i.e. a drop-zone). That's not needed for Sortables, but it is if you want to give any sort of visual feedback on hover (like green/red for allowed/disallowed) that needs to be reset at the end of the hover. A quick couple lines to dragdrop.js and bingo: onUnhover Scriptaculous patch.

To use, define an onUnhover callback just like the standard onHover on, excepting that you only get passed the droppable (which is the onHover callback's second parameter).

DHTML ComboBox Widget

With the wife and kid out of town, I didn't have much to do, so I spent the afternoon building a simple DHTML combobox widget. Yes, I'm that much of a geek. I haven't done a lot of testing, but I built it exclusively in FireFox, aiming for compatibility, and IE6 behaved exactly as expected on the first try, so I'm hoping other browsers will behave as well.

It's available for download at widgets.zip, a demo is available at /web/widgets/ (try entering the letter 'a'), and there is a project page as well. The demo is included in the zip file as well.

The architecture will likely seem a bit odd at first, but it's built the way it is for good reason. In a nutshell, you put an INPUT in your document, and pass it's ID and a callback function to the ComboBox constructor, and the rest happens by magic. You'll notice that you don't pass in any options, but that's where the callback function comes in. That function will be called any time the value entered in the field changes and the field has focus, and will be passed the current value. It's responsibility is to return an array of strings that should be displayed.

The demo just uses a simple array of names for the values, but I set it up in the seemingly overflexible way specifically to allow for JS remoting calls to be used for generating the dropdown list. Currently, you only get synchronous processing (since the callback has to return the values), but I might add async capabilities (so your can pass in an array of options at any time).

As always, download and run with it, and give me any feedback you have along the way.