Recently in JavaScript Category

JavaScript coding tips, optimizations, development libraries, and tools.

  1. Feb08

    HTML5 video markup, compatibility and playback

    The emerging HTML5 specification lifts video playback out of the generic <object> element and into specialized <video> handlers. Explicit markup for audio and video places elevates moving pictures to a similar native rendering capacity as <img> markup we are used to but with more fine-grained details about underlying formats and compression available before loading. In this post I will dive into implementation details of HTML5 video based on currently available consuming agents and outline some of the nuances of preparing media for playback.

    1. Inside the video element
      1. Browser workflow
      2. JavaScript-based workflow
    2. Implementation nuances
    3. Player UIs
    4. HTML5 video and Flash
    5. Summary

    Inside the video element

    The video element is the top-level element of a cascading element set designed to handle graceful degradation across a wide array of HTML rendering engines. If a web browser or other consuming agent unpacks the DOM and does not understand what you have described it should process child elements until something makes sense or it reaches the end of your element tree.

    <video width="480" height="320" id="video" poster="video_frame.jpg" controls="true" autobuffer="true">
      <source src="video_high.mp4" type="video/mp4; codecs=&quot;avc1.64001E, mp4a.40.2&quot;" />
      <source src="video_base.mp4" type="video/mp4; codecs=&quot;avc1.42E01E, mp4a.40.2&quot;" />
      <source src="video.ogv" type="video/ogg; codecs=&quot;theora, vorbis&quot;" />
      <object id="flashvideo" width="480" height="320" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab#version=9,0,115,0" standby="Loading your video...">
        <param name="movie" value="video-player.swf" />
        <param name="quality" value="best" />
        <param name="allowfullscreen" value="true" />
        <param name="loop" value="false" />
        <param name="flashvars" value="movie=video_high.mp4" />
        <!--[if !IE]>-->
        <object type="application/x-shockwave-flash" width="480" height="320" data="video-player.swf" standby="Loading your video...">
          <param name="quality" value="best" />
          <param name="loop" value="false" />
          <param name="allowfullscreen" value="true" />
          <param name="flashvars" value="movie=video_high.mp4" />
        <!--<![endif]-->
          <img alt="animated GIF" src="video_animated.gif" width="480" height="320" />
      <p class="robots-nocontent">We tried to show you a video but your browser does not support native video playback and does not have a copy of <a rel="nofollow" href="http://get.adobe.com/flashplayer/">Adobe Flash</a> installed. Please upgrade your browser and plugins.</p>
        <!--[if !IE]>-->
        </object>
        <!--<![endif]-->
      </object>
    </video>

    Look complicated? It is! The static markup above describes six possible video interactions with the web browser. Three different source videos are described in HTML5 markup: a MP4 file container with a H.264 video track using the High profile Level 3 and low-complexity AAC audio (suitable for desktops); a MP4 file container with a H.264 video track using the Baseline profile and low-complexity AAC audio (suitable for mobile phones); an Ogg file container with a Theora video track and a Vorbis audio track. I will dive deeper into file format and codec nuances in a separate post. If the HTML5 video markup fails, or none of the three specified source videos are compatible with the consuming agent the markup falls back to double-baked markup for the Flash Player plugin. If HTML5 video fails and Flash embedding fails the markup includes simple information about the video and an animated GIF preview.

    Behind the scenes the web browser is converting your markup string into its own set of mapped elements, passing off to the appropriate handler, and adjusting page layout based on its new discoveries.

    Browser workflow

    1. Read the markup string.
    2. Build an element tree.
    3. Find a <video> element.
    4. I know how to process a <video> element. Map defined attributes.
      1. Found width and height attributes. Prepare the page layout for new content.
      2. The controls attribute is present and I know how to process the attribute. The publisher would like to use the default playback UI built-in to my video handler.
      3. Found a src attribute. Try to load the referenced resource. Similar handling to an <img> src.
      4. Found an autobuffer attribute and I know how to process the attribute. Start buffering the movie resource before the viewer initiates playback.
      5. Found a poster attribute and I know how to process such an attribute. The publisher would like to show a poster frame image inside the video object dimensions before the viewer initiates playback.
      6. The src attribute is either undefined, unavailable, or incompatible. Continue parsing child elements for a better content match.
        1. Found a source element and I know how to process such an element.
          1. The type attribute value references an Internet media type I recognize and support for Internet video. It's possible I might be able to read the file format and unpack the video container after download.
            1. A codecs parameter is specified within the type attribute, defining the video codec and audio codec needed to decode the container's video and audio tracks respectively.
          2. The src attribute exists. Queue the referenced resource for network loading after the viewer initiates playback, or immediately if autobuffer was specified in the video element.
        2. No suitable source element found. Continue searching.
    5. Found an <object> element with an object handler specified using the classid attribute. My name is most likely Trident/IE.
      1. The classid attribute value matches a plugin installed on the viewer's computer: Adobe Flash Player.
      2. The version of Flash Player currently installed on the viewer's computer is less than the minimum specified value in the codebase attribute.
        1. Attempt to download and install a new Flash Player ActiveX control at or above version 9.0.115 "MovieStar." The specified Flash Player version is capable of handling a MP4 video container with an H.264 video track and AAC audio track.
        2. Stop processing the video object; reload later.
      3. A <param> element exists with a name attribute of movie and a resource location declared in the value attribute.
      4. Display text specified in the object's standby attribute value while I attempt to load the Adobe Flash browser plugin and its SWF file interpreter. Pass the specified param element key-value pairs into the Flash interpreter as well as the FlashVars query parameter describing dynamic values interpreted by the SWF at runtime.
    6. I don't care about conditional comment blocks targeting Trident/IE or such a conditional evaluates as true.
    7. Found an object element with an object handler specified using the type attribute.
      1. The type attribute specifies an Internet media type connected to a known plugin registered in the plugin system (most likely NPAPI).
      2. The data attribute exists and specifies a valid resource.
      3. Display text specified in the object's standby attribute value while I attempt to load the Adobe Flash browser plugin and its SWF file interpreter. Pass the specified param element key-value pairs into the Flash interpreter as well as the FlashVars query parameter describing dynamic values interpreted by the SWF at runtime.
    8. No acceptable video player found. Display an animated GIF preview of the movie. Let the viewer know they are missing out on the full content experience.
    9. Acceptable movie found and queued.
      1. Attempt to progressively download or stream the specified video element.
        1. Does the Content-Type returned by the server match our expected value(s)?
        2. Does the server accept downloading individual pieces of a file at a time (Accept-Ranges)?
        3. Did the resource return a X-Content-Duration header specifying expected playback length in seconds?
      2. Send downloaded video pieces to the video decoder for decompression.
      3. Initiate a playback buffer.
      4. Fire events related to the final loaded stage of the process.

    Yes, I have over simplified.

    JavaScript-based workflow

    It is possible to test playback capabilities of the browser and its related plugins through JavaScript (if JavaScript is available on the page of course). If you are considering supporting HTML5 video at some point in the future but are curious how many of your visitors could support the new playback method you could track analytic events today to influence your product roll-out months down the road.

    Video element support

    Test the current consuming agent's support for the <video> element by declaring a new DOM object and evaluating the browser's default handlers. If the created DOM object contains functions present in a default HTMLVideoElement or HTMLMediaElement interface we know the consuming agent applied special handling to our video element declaration and likely supports HTML5 video.

    !!document.createElement('video').canPlayType

    Individual codec support

    Testing support for the video element is only the first step. We also need to check playback support for the specific video and audio codecs used in our source videos. The canPlayType method returns the likelihood a given file container, video codec and audio codec are supported by the consuming agent.

    var v = document.createElement('video');
    var supported = v.canPlayType('video/mp4; codecs="avc1.58A01E, mp4a.40.2"');
    if ( supported == 'probably') { return true; }

    Detect Flash

    Flash Player 9.0.115 and above is required to play MP4 file containers with H.264 video and AAC audio. The Flash Player detection kit provides client-side detection libraries and automatic upgrade capability for site visitors not already using the latest version of Flash.

    Check for an ActiveXObject of ShockwaveFlash.ShockwaveFlash.10 or ShockwaveFlash.ShockwaveFlash.9 and compare the full version string.

    In a NPAPI plugin environment check the navigator.mimeTypes array for the key "application/x-shockwave-flash," verify the associated plugin is enabled, and parse the version number from the plugin's description string.

    DOM insertion

    Once your script has determined the best available video playback method you can insert the appropriate markup using a subcomponent of the markup used above.

    Implementation nuances

    In the static markup method of describing content the consuming agent cycles through possible <source> elements one at a time in search of a suitable match. In my testing on mobile WebKit (iPhone OS) this test cycle removes the poster frame image described in the <video> element and instead places a broken video image inside the element dimensions instead. If a later <source> element matches a generic playback image is added to the element. Source element cycling is the new flash of unstyled content for the HTML5 video world.

    The dynamic insertion method relies on the canPlayType method and its return values of "probably" or "maybe." Maybe is not good enough for my needs if I have a Flash fallback option, but if you are in a constrained playback environment such as low-power mobile devices then acting on a response of "maybe" is better than nothing. Just be sure to send along some alternate HTML as a failure fallback.

    Player UIs

    Each web browser supporting HTML5 video uses its own backing software to power the video playback experience. Chromium and Google Chrome use a specially patched version of FFmpeg. QTWebKit uses Phonon. Layer on top platform-specific video acceleration, UI, and handling and you will see a variety of final UIs across browsers and platforms. Including the controls in your <video> element is the quickest path to launch but you will give up control over interactions.

    If a web browser supports HTML5 video it almost certainly supports native vector graphics as well. It's possible to craft your own UI with supported JavaScript methods triggering play, pause, and final frame handling in the native video handler.

    HTML5 video and Flash

    Flash is the dominant method of video playback on the web today. Native browser support of HTML5 video and business excitement to reach low-power devices such as the iPhone provide compelling reasons to offer content using HTML5 video markup. Flash supports progressively loading MP4 files with H.264 video and AAC audio since 2007. Flash Player 10.1, expected in the next few months, speeds up playback with less resources thanks to specialized GPU handling and more efficient code. HTML5 video and Flash playback solutions will need to co-exist for maximum reach (that's the reason you are using Flash in the first place).

    Playback is only one component of the total video experience. You will need to develop analytics and advertising capabilities to match or exceed your current Flash experience. Advertisers don't publish interactive advertisements in <canvas>. The high-CPM pre-roll and post-roll video advertisements we see today are based on a Flash ecosystem built up over the years. HTML5 video and your money maker of choice will need to find a way to co-exist (banner and text advertisements still work well) and drive your development budget. I expect to see better JavaScript libraries from the open-source community as well as advertising networks solve some of the problem in the near future, just like a suite of XHR handlers popped up once Ajax started to take off.

    Summary

    HTML5 video has arrived and is deployed across a wide enough user base for sites and developers to stand up and pay attention. File support and markup varies by browser and there is currently no native support in Internet Explorer. Developers are excited to take advantage of the performance gains of native video handlers and reach new audiences in the smartphone market. If you are thinking of getting implementing HTML5 video in the future it's possible to start measuring your audience's playback compatibility today so you at least know your deploy targets.

  2. Feb06

    iPhone web app performance

    iPhone iPod touch web apps

    The Exceptional Performance group at Yahoo! just released a detailed performance analysis of web applications on the iPhone. Yahoo! analyzed the full capabilities of the iPhone's Safari browser including browser cache and transfer speeds.

    Cache persistence

    The Safari browser on iPhone allocates memory from the shared system memory but does not save web content into persistent storage. Any cached objects (CSS, JavaScript, images, etc.) are removed from memory on reboot.

    Optimal component size

    Safari for iPhone will only cache files 25 KB or smaller served using the Expires explicit expiration time or Cache-Control max-age directive HTTP headers. Safari decodes the file before saving it cache, meaning your total unzipped file size must squeeze under the 25 KB ceiling to hit the cache. Components already in cache are only replaced by new cacheable components using the least recently used algorithm.

    Safari for iPhone is able to cache a maximum of 19 external components, placing a maximum cache limit at around 475 KB.

    Download speed

    Yahoo! found typical download iPhone download speeds vary from 82 kbps to 150 kbps when connected to a GSM cellular data network. Wi-Fi connections over an 802.11b/g networks obviously speed up the experience, but pages should assume cellular data load times when designing for a compelling user experience.

    Summary

    Works with iPhoneWeb applications built for the iPhone's Safari browser need to specifically target web performance these small devices and special cache rules. Desktop browser best practices such as zipped components and combined files for CSS and JavaScript may be too bloated for the Safari mobile browser. A few tips:

    • Limit cacheable components to a decompressed size of 25 KB or less
    • Limit yourself to 19 or less cached components
    • Minify CSS and JavaScript for slimmer file weights.
    • Use CSS sprites to combine multiple small images into a shared image under 25 KB
  3. Feb05

    Sniff browser history for improved user experience

    The social web has filled our websites with too much third-party clutter as we figure out the best way to integrate content with the favorite sites and preferences of our visitors. Intelligent websites should tune-in to the content preferences of their visitors, tailoring a specific experience based on each visitor's favorite sites and services across the social web. In this post I will teach you how to mine the rich treasure trove of personalization data sitting inside your visitor's browser history for deep personalization experiences.

    I first blogged about this technique almost two years ago but I will now provide even more details and example implementations.

    1. Evaluate links on a page
    2. Test a known set of links
    3. Live demos and examples
      1. Online aggregators
      2. Social bookmarks
      3. OpenID providers
      4. Mapping services
    4. Summary

    Web browsers store a list of web pages in local history for about a week by default. Your browsing history improves your browsing experience by autocompleting a URL in your address bar, helping you search for previously viewed content, or coloring previously visited links on a page. Link coloring, or more generally applying special CSS properties to a :visited link, is a DOM-accessible page state and a useful method of comparing a known set of links against a visitor's browser history for improved user experience.

    • New Site
    • Visited site

    A web browser such as Firefox or Internet Explorer will load the current user's browser history into memory and compare each link (anchor) on the page against the user's previous history. Previously visited links receive a special CSS pseudo-class distinction of :visited and may receive special styling.

    <style type="text/css">
    ul#test li a:visited{color:green !important}
    </style>
    <ul id="test">
      <li><a href="http://example.com/">Example</a></li>
    </ul>
    

    The example above defines a list of test links and applies custom CSS to any visited link within the set. Your site's JavaScript code can request each link within the test unordered list and evaluate its visited state.

    Any website can test a known set of links against the current visitor's browser history using standard JavaScript.

    1. Place your set of links on the page at load or dynamically using the DOM access methods.
    2. Attach a special color to each visited link in your test set using finely scoped CSS.
    3. Walk the evaluated DOM for each link in your test set, comparing the link's color style against your previously defined value.
    4. Record each link that matches the expected value.
    5. Customize content based on this new information (optional).

    Each link needs to be explicitly specified and evaluated. The standard rules of URL structure still apply, which means we are evaluating a distinct combination of scheme, host, and path. We do not have access to wildcard or regex definitions of a linked resource.

    In less geeky terms we need to take into account all the different ways a particular resource might be referenced. We might need to check the http and https versions of the page, with and without a www. prefix to more thoroughly evaluate active use of a particular website and its pages.

    I group my tests into sets of URLs with the most likely matches placed at the beginning of the set. I evaluate each link in the set until I find a match thereby exhausting positive indicators of site activity while prioritizing the data scan.

    Live demos and examples

    Sniffing a visitor's browser history has good and evil implications. An advertiser can determine if you visited Audi's website lately, drill down on exact Audi models, and offer related information without ever placing code on the Audi website. I have been scanning the browser history of my site visitors for the past few months and I have coded a few examples to show benevolent uses for improved user experience.

    Online aggregators

    Feed aggregator button grid

    Clusters of feed subscription buttons clutter our websites, displaying tiny banner ads for online aggregators of little use to most of our site visitors. My blog checks a known list of online aggregators against the current visitor's browser history and adds a targeted feed subscription button for increased conversion. A Google Reader user will see an "Add to Google button" and a Netvibes user will see an "Add to Netvibes" button without cluttering up the interface. I insert direct links to each site's feed handlers to help convert the current visitor into a long-term subscriber.

    Once I match a particular service I could also check to see if the current visitor is already subscribed to my feed. I would simply need to run a second test against the data retrieval URL, such as feedid=1234, to match web traffic with subscriber numbers.

    Visit my live example of link scanning popular online feed aggregators for a demo and the applicable code.

    Social Bookmarks

    Social bookmark button sample

    I like to see my latest blog posts spread all over the web thanks to social bookmarking sites and other methods of content filtering and annotation. Most sites spray a group of tiny service icons near their blog posts and hope a visitor recognizes the 16 pixel square and takes action. Suck. There has to be a better way.

    I can scan a current visitor's browser history to determine an active presence on one or more bookmarking sites. Once I determine the current visitor is also a Digg user I can show live data from Digg.com to prompt a specific action such as submitting a story or voting for content. I can create a much better user experience for 3 services I know my visitor actively uses instead of spraying 50 sites across the page.

    Visit my live example of link scanning popular social bookmarking sites for a demo and the applicable code.

    OpenID providers

    Pibb OpenID signin

    OpenID is an increasingly popular single sign-on method and centralized identity service. OpenID lets a member of your site sign-on using a username and password from a growing list of OpenID providers including your instant messenger, web portal, blog host, or telephone company account. Visitors signing up for your site or service shouldn't have to know anything about OpenID, federated identities, or other geeky things, but should be able to easily discover they can sign-in with a service they already use and trust every day.

    I can scan a list of sign-in endpoints for a list of OpenID providers and only present my site visitor with options actually relevant to their everyday web usage. Prompting a user to sign-in to your service with their WordPress.com account should be much more effective than an input field sporting an OpenID icon. Link scanning for active usage should increase new member sign-ups, reduce support costs due to yet another username and password, and make your members happy.

    Visit my live example of link scanning current OpenID providers for a demo and applicable code.

    Mapping services

    Facebook map drop-down

    Online mapping services have changed the way we interact with location data. Need to get to 123 Main Street? Not a problem, I'll just send that data over to your favorite mapping service to help you find your way.

    I can scan a visitor's browser history to determine their favorite mapping service. Perhaps she is most comfortable with MapQuest, Google Maps, or Yahoo. Or maybe she uses a Garmin GPS unit and would prefer a direct sync with that specialized service. Determining my visitors' favorite mapping tool helps me deliver a valuable visualization or link I know they prefer.

    Visit my live example of link scanning map API providers for a demo and applicable code.

    Summary

    Websites should take advantage of the full capabilities of modern browsers to deliver a compelling user experience. Built-in capabilities such as XMLHttpRequest took years of implementation before finding its asynchronous groove in data-heavy websites. I hope we can similarly probe other latent useful features to improve the social web through more personalized and responsive experiences.

    I have been the browser history of my website visitors for the past few months to gracefully enhance adding my Atom feed to their favorite feed reader. Easily recognized branding such as "Add to My Yahoo" has yielded much higher conversion rates than a simple Atom link with a minimal effect on page load performance. Dynamically checking for active usage of 50 or so aggregators allows me to extend my total test list and promote an obscure tool that might never make the cut for permanent on-screen real estate.

    How will your site utilize your visitor's browser history for a more custom user experience? How will you connect data in new ways once you have concrete knowledge of the new feature developments that will be most useful to your visitors' online lifestyle?

  4. Oct29

    Widget Basics

    The total number of widget platforms and deployment options intimidate many newcomers. Each platform offers a unique audience and features, but widget basics remain the same across Windows Vista, Nokia S60, Google, MySpace. and more. In this post I will outline the basic components of a widget including static assets, user preferences, processing remote data, and rendering your final widget.

    Create a default view

    Every widget has an at-rest state. Your widget might have a background image or color, static text, or other fixed pieces not dependent on external data updates. You will likely define areas of your widget that will later contain the result of dynamic data updates. This at-rest state, or default view, is the first step towards a complete widget and the first thing your users will see before retrieving their custom data.

    Apple Dashboard widget front background

    Apple's Dashboard weather widget has a slim background with a simple layout showing the current temperature and weather condition. Today's temperature is displayed on the right, with a degree sign built-in for data context. The current weather condition (sunny, cloudy, raining, etc) will appear in the center.

    Gather custom preferences

    Every widget platform supports user customizations collected using a familiar user interface and stored on the parent platform along with other user data. In some cases you might be able to tap into preference data already stored on the platform such as a user's location, age, or preferred number of headlines per widget.

    iGoogle weather preference

    iGoogle's weather widget stores both a country and a city as your weather location. A weather widget might use different data APIs by country or region, or adjust its wording based on a user's location (i.e. Postal Code vs. ZIP Code).

    Widget preference data is stored on the widget platform -- Windows Vista, iGoogle, etc. -- inside a user data store. Preferences can be structured as drop down lists, sliders, numbers, text, and much more depending on the data you would like to capture.

    Retrieve dynamic data

    Dynamic widgets receive regular data updates from a remote source and process the results. You will likely customize the data retrieved based on the user preferences defined above and create a targeted experience for each user. Data updates are often cached and processed by each widget platform to save development time and increase your widget's performance. The data formats below can be cached and processed by the parent widget platform to produce fast response times and allow same domain access to your data.

    Web feed
    RSS 2.0 or Atom Syndication Format 1.0. Your site might already produce these syndicated feeds popular for distributing the latest news headlines from your site to specialized feed readers. Widget platforms typically store a subset of all available feed elements (e.g. title, content, link, and author) which may or may not meet your needs.
    JSON
    JavaScript Object Notation, a lightweight data interchange format. JSON offers fast processing and manipulation of custom data. Your website may already produce JSON output to power asynchronous JavaScript requests that can be extended into your widgets. Most widget platforms will convert your JSON objects into their appropriate number, text, or list types for easy manipulation by your widget.
    XML
    XML is a popular data interchange format for custom-defined data sent between computers and their programs. XML snippets power Ajax (Asynchronous JavaScript and XML) functionality on many websites and can also provide your widgets with dynamic data updates.
    Plain text
    Plain text is the most lightweight data format and is swallowed whole by widget platforms. Plain text is a good data choice for small pieces of content (i.e. current temperature is 65) or complete HTML snippets you would like to insert directly into your widget. You could also process a list of comma-separated values or other forms of structured data as a plain text data source.

    Creating dynamic data in an appropriate syndication format is often the first step widget publishers need to take before kicking off a broader widget program. You will need to expose your data in a syndicated format before distributing your widget.

    Draw your widget

    You have defined your blank widget, collected preferences and customizations from the user, and retrieved your widget's dynamic data. You now need to place your processed data into the locations you defined in your initial view.

    Your widget code should construct the appropriate HTML or Flash markup describing your updated data. In the weather widget described above we need to update the temperature field with the current temperature and display a picture corresponding to current weather conditions if available. We have already assigned an identifier to our widget's temperature and weather condition areas, and update these elements with our latest data.

    Final Apple Dashboard weather widget

    Pictured above is an example of a final widget view. The current weather in San Francisco is 58 degrees and cloudy.

    The term "San Francisco" is a verified location based on the user's defined preferences (you were able to interpret the input and map it to a unique identifier compatible with your data provider). Our weather API returned two pieces of data: a condition of cloudy and temperature of 58. We displayed a pre-defined picture of the cloudy weather condition inside our widget's condition area and updated the temperature text to 58.

    Summary

    Every widget platform has a similar method of widget construction and processing. Available features, caching, proxies, and widget helpers vary by platform but the basic approach and mentality remains the same. You are creating a tiny application on the Web, mobile, or desktop that may respond to its environment and collect live updates from the Internet.

    A provided a simplified view of a very broad development area. I'll dive a bit deeper into each platform type and the capabilities of major widget platforms in future blog posts. This post expands on my opening slide from my recent 8-minute "high order bit" presentation at the Web 2.0 Summit.

  5. Aug10

    YUI Rich Text Editor for blog comments

    This blog's comments are now enhanced with the YUI Rich Text Editor. I'm already familiar with the YUI JavaScript library, so when this new feature was included last week's 2.3 release I decided to try out a progressive commenting enhancement. In this post I'll walk you through how to implement YUI's Rich Text Editor on your own blog with comment-specific features.

    YUI rich text editor blog comments

    Why the change?

    Leaving a blog comment can be a mysterious task. Is HTML allowed? If so, what elements are permitted? Maybe I should just paste the full link URL into the comments just in case, and hope the blog software either converts it into a link or a reader might copy and paste the text into their browser location bar.

    I wanted to take some of the guesswork and complexity out of leaving a comment and encourage more relevant citations. I also wanted to make my comments field feel a bit like a miniature blog entry and most modern blog software today includes WYSIWYG editing. Perhaps enhancing the comment field could make bloggers feel at home even when they are away from their admin interface.

    Progressive enhancements

    The new rich text editor is only shown to capable JavaScript-enabled web browsers. I swap out the standard comment textarea with an entirely new sub-page containing the editor, and save any new content back to the textarea before submitting the form. If a site visitor has JavaScript turned off, or for some reason the JavaScript throws an error in their browser, the regular non-enhanced comment form is still available and fully functional.

    What's included

    I customized the editor with buttons I think might be useful within blog comments. You can add bold, italic, or underlined text, add a link, or include a remote image at the size and text-wrap you desire. You can also insert an ordered or an unordered list with the click of a button. All changes appear live and some features include keyboard shortcuts.

    Implementation details

    If you'd like to implement a similar feature on your blog you'll need to include some YUI JavaScript files, a CSS skin for button and control styling, and some local JavaScript to customize the editor, instantiate, and save its content.

    YUI libraries

    The rich text editor is part of the YUI JavaScript libraries version 2.3 or above. The libraries which can be uploaded to your blog's web server or served directly from Yahoo's geo-distributed server farms. Your text editor has a few YUI dependencies you will need to load before it reworks your entry pages.

    utilities

    The utilities file is a roll-up of the YUI core and all the YUI library utilities into one download.

    YUI core consists of the YAHOO global library, the DOM collection, and the event utility. This file provides cross-browser compatibility for common tasks such as selecting and altering elements on a page or listening for events such as a mouse click or window scroll.

    Other loaded YUI utilities include support for animated controls, asynchronous connection management for form submissions, drag and drop for image movements, and more.

    container
    The container library helps the rich text editor display tooltips, action panels, and other manipulation tasks.
    menu
    The menu library powers your toolbar and drop-down menus.
    button
    The button control creates all of the buttons and checkboxes inside the text editor and handles their various events.
    editor
    The rich text editor we've been waiting to instantiate.

    Customize your button bar

    The default YUI Rich Text Editor includes font selectors, subscripts, superscripts, color wheels, highlighters, alignment, and indentation controls that are a bit overkill for a simple comment field. I decided to customize the editor toolbar with only the buttons I care about.

    My custom editor configuration instantiates a new editor with specific configuration options. I turned off the dompath status bar (too geeky) and set the height and width of the editor to match my CSS for the textarea The editor will try to guess the appropriate height and width based on your textarea's existing rows and cols markup, if present, or you can explicitly define your editing area.

    Next I define three button groups: textstyle, lists, and insert. Each button's listener, tooltip, and behavior is defined as a JavaScript object.

    {
    type: 'push',
    label: 'Bold (Ctrl Shift B)',
    value:'bold'
    }

    In the example above I defined a button with a push behavior, a text label, and a built-in action value to alter the editor.

    Choose a skin

    You'll need to skin your editor to avoid ugly output. I'm using Sam Lind's skin by including a CSS file hosted by Yahoo! in my head and a body class value of yui-skin-sam. All button graphics are served from a single image using sprites, making the skin a pre-optimized package.

    Consider a submit handler

    All markup entered into the editor needs to be transferred back to your textarea before submitting your form. You could define handleSubmit at instantiation, but there are currently some issues with YUI 2.3 and proper handling within Safari.. I decided to call the saveHTML method during my form's onsubmit instead.

    Beta software

    The YUI Rich Text Editor is currently in beta, which means things may not quite work the way you'd expect or prefer and there may be major changes to the library in the next YUI release. You might also consider alternate rich text editing JavaScript libraries such as FCKeditor, the dojo editor, or TinyMCE.

    The YUI Rich Text Editor does not output XHTML 1.0 Strict compliant markup, even in "semantic" mode. If you are strict you still might have some post-processing work to keep your entry pages compliant.

    Summary

    Progressively enhancing my comments field with a rich-text editor and minimal button options could encourage richer content markup. It also looks pretty slick, differentiating my blog comments from the countless other textareas found across the Web. I'm already familiar with YUI libraries, so implementation was easier than starting over again with something new and I could re-use the base libraries loaded on my pages for other future features.

    In short: the comment form has changed and hopefully it's for the better. If you're interested in implementing a similar rich editor on your own blog, just view the source and non-minified examples for a quick setup.

Niall Kennedy Niall Kennedy is a web technologist in San Francisco, California in the United States. I am very interested in the world of... MORE »

Search this weblog:

Subscribe:

Recently Popular

Archives: Popular Categories

Sites: More from Niall