Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introducing YUI 3 AutoComplete

Ryan Grove
November 09, 2010

Introducing YUI 3 AutoComplete

An all-new AutoComplete widget is landing in YUI 3.3.0. In this talk, AutoComplete author Ryan Grove will take you on a whirlwind tour of some of the many autocomplete patterns it makes possible, as well as a deep dive into its powerful new YQL integration, filtering, and highlighting capabilities.

Video: http://www.youtube.com/watch?v=NdmQH-dciN8

Ryan Grove

November 09, 2010
Tweet

More Decks by Ryan Grove

Other Decks in Programming

Transcript

  1. In the YUI 2 world, this meant AutoComplete had to

    provide many options within a single module.
  2. New modular API provides more flexibility and easier extension to

    allow for countless autocomplete patterns. Photo: http://www.flickr.com/photos/grdloizaga/817443503/
  3. Synthetic event; fires when an input field’s value changes. AutoComplete

    modules autocomplete-base event-valuechange autocomplete- filters autocomplete- highlighters autocomplete-list Core logic and API. No UI, minimal dependencies. Optional prepackaged result filters and highlighters. Traditional list widget, just like Mom used to make.
  4. 1KB 2KB 3KB 4KB 5KB 6KB 7KB 8KB 2.8.2 3.3.0

    AutoComplete module size (min + gzip) Filters Highlighters List Base
  5. 90% feature parity with YUI 2 AutoComplete. * * This

    is a highly scientific number. I swear.
  6. autocomplete-base autocomplete-list allowBrowserAutocomplete activateFirstItem inputNode align maxResults alwaysShowList minQueryLength circular

    queryDelay visible queryDelimiter tabSelect requestTemplate zIndex resultFilters resultFormatter resultHighlighter resultListLocator resultTextLocator source yqlProtocol
  7. autocomplete-base autocomplete-list allowBrowserAutocomplete activateFirstItem inputNode align maxResults alwaysShowList minQueryLength circular

    queryDelay visible queryDelimiter tabSelect requestTemplate zIndex resultFilters resultFormatter resultHighlighter resultListLocator resultTextLocator source yqlProtocol
  8. Results can also come from a JSONP URL or a

    YQL query. This is awesome.
  9. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source)
  10. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source)
  11. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source)
  12. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source)
  13. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source)
  14. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source)
  15. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source) // Widget-style. YUI().use('autocomplete', function (Y) { var ac = new Y.AutoComplete({ inputNode: '#my-input', render : true, source : 'select * from search.suggest where query="{query}"' }); });
  16. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source) // Widget-style. YUI().use('autocomplete', function (Y) { var ac = new Y.AutoComplete({ inputNode: '#my-input', render : true, source : 'select * from search.suggest where query="{query}"' }); });
  17. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source) // Widget-style. YUI().use('autocomplete', function (Y) { var ac = new Y.AutoComplete({ inputNode: '#my-input', render : true, source : 'select * from search.suggest where query="{query}"' }); });
  18. // Plugin-style. YUI().use('autocomplete', function (Y) { Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'select

    * from search.suggest where query="{query}"' }); }); Basic usage (YQL result source) // Widget-style. YUI().use('autocomplete', function (Y) { var ac = new Y.AutoComplete({ inputNode: '#my-input', render : true, source : 'select * from search.suggest where query="{query}"' }); });
  19. Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: ['apple pie', 'peach pie', 'pecan pie', 'pumpkin

    pie'] }); Array and object result sources Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: { pies : ['apple', 'peach', 'pecan', 'pumpkin'], cookies: ['chocolate chip', 'molasses', 'peanut butter'] } });
  20. Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: ['apple pie', 'peach pie', 'pecan pie', 'pumpkin

    pie'] }); Array and object result sources Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: { pies : ['apple', 'peach', 'pecan', 'pumpkin'], cookies: ['chocolate chip', 'molasses', 'peanut butter'] } });
  21. Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: ['apple pie', 'peach pie', 'pecan pie', 'pumpkin

    pie'] }); Array and object result sources Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: { pies : ['apple', 'peach', 'pecan', 'pumpkin'], cookies: ['chocolate chip', 'molasses', 'peanut butter'] } });
  22. [ "apple pie", "peach pie", "pecan pie", "pumpkin pie" ]

    Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'http://example.com/search?q={query}&callback={callback}' }); ✓ No problem.
  23. { "status": "success", "search": { "results": [ "apple pie", "peach

    pie", "pecan pie", "pumpkin pie" ] } } Y.one('#my-input').plug(Y.Plugin.AutoComplete, { source: 'http://example.com/search?q={query}&callback={callback}' }); ✗ Bit of a problem.
  24. { "status": "success", "search": { "results": [ "apple pie", "peach

    pie", "pecan pie", "pumpkin pie" ] } } Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'search.results', source: 'http://example.com/search?q={query}&callback={callback}' }); ✓ Awesome.
  25. Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: function (response) { return response && response.search

    && response.search.results; }, source: 'http://example.com/search?q={query}&callback={callback}' }); The list locator can also be a function.
  26. { "results": [ { "created_at": "Fri, 29 Oct 2010 07:00:58

    +0000", "from_user": "EricF", "to_user_id": null, "text": "YUIConf is looking like it's gonna be crazy good!", "id": 29064356310, "from_user_id": 2469883, "iso_language_code": "en" }, ... ] } Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'results', source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}' });
  27. { "results": [ { "created_at": "Fri, 29 Oct 2010 07:00:58

    +0000", "from_user": "EricF", "to_user_id": null, "text": "YUIConf is looking like it's gonna be crazy good!", "id": 29064356310, "from_user_id": 2469883, "iso_language_code": "en" }, ... ] } Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'results', resultTextLocator: 'text', source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}' });
  28. Y.one('#my-input').plug(Y.Plugin.AutoComplete, { resultListLocator: 'results', resultTextLocator: function (result) { return result.text;

    }, source: 'http://search.twitter.com/search.json?q={query}&' + 'callback={callback}' }); Like the list locator, the text locator can be a function.
  29. Prepackaged Filters & Highlighters • All are case-insensitive by default,

    but case- sensitive versions are available (charMatchCase, phraseMatchCase, etc.). • Accent-folding versions are available as well (charMatchFold, phraseMatchFold, etc.). charMatch phraseMatch startsWith wordMatch
  30. function customFilter(query, results) { return Y.Array.filter(results, function (result) { result

    = result.toLowerCase(); // Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; }); } Custom Filter
  31. function customFilter(query, results) { return Y.Array.filter(results, function (result) { result

    = result.toLowerCase(); // Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; }); } Custom Filter
  32. function customFilter(query, results) { return Y.Array.filter(results, function (result) { result

    = result.toLowerCase(); // Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; }); } Custom Filter
  33. function customFilter(query, results) { return Y.Array.filter(results, function (result) { result

    = result.toLowerCase(); // Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; }); } Custom Filter
  34. function customFilter(query, results) { return Y.Array.filter(results, function (result) { result

    = result.toLowerCase(); // Only include results that start with an "a" and contain // the query string. return result.charAt(0) === 'a' && result.indexOf(query) !== -1; }); } Custom Filter
  35. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  36. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  37. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  38. { "results": [ { "created_at": "Fri, 29 Oct 2010 07:00:58

    +0000", "from_user": "EricF", "to_user_id": null, "text": "YUIConf is looking like it's gonna be crazy good!", "id": 29064356310, "from_user_id": 2469883, "iso_language_code": "en" }, ... ] } user: result.from_user } ); }); }
  39. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  40. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  41. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  42. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  43. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  44. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  45. function twitterFormatter(query, raw, highlighted) { return Y.Array.map(raw, function (result, i)

    { return Y.Lang.sub( '<div class="tweet">' + '<div class="hd">' + '<img src="{img}" class="photo" ' + 'alt="Profile photo for {user}">' + '</div>' + '<div class="bd">' + '<strong class="user">{user}</strong>' + '<span class="tweet-text">{text}</span>' + '</div>' + '<div class="ft">{time}</div>' + '</div>', { id : result.id, img : result.profile_image_url, text: highlighted[i], time: result.created_at, user: result.from_user } ); }); }
  46. .tweet { clear: both; margin: 6px 0; padding: 2px 0;

    } .tweet .hd { float: left; } .tweet .bd, .tweet .ft { margin-left: 52px; } .tweet .ft { color: #cfcfcf; font-size: 11px; } .tweet .photo { height: 48px; margin: 2px 0; width: 48px; } .tweet .user { margin-right: 6px; }
  47. • Synthetic event. Fires when an input node’s value changes

    due to user input. • Handles keystrokes, pastes, and IME input. • Creates a level playing field across browsers, particularly with languages that have multi-stroke characters. event-valuechange
  48. escape • Static utility methods for escaping strings. • Currently

    has methods for escaping HTML and regex strings. More to come in future releases. • Feel free to add your own escaping methods to the Y.Escape namespace (and share on the Gallery).
  49. And now, a brief interlude wherein I whine about having

    discovered, yesterday afternoon, that “Unicode” is a registered trademark of the Unicode® Consortium.
  50. ®?

  51. ®?

  52. ‚-accentfold • Helpers for creating and working with accent- folded

    strings. • Accent folding converts “résumé” to “resume” (etc.) for easier comparison. • Should be a last resort. Only use it when better tools aren’t available on the server. Don’t fold strings you will display to the user. • Will have an actual name before 3.3.0 final.
  53. Y.☃.AccentFold.fold('résumé'); // => 'resume' Y.☃.AccentFold.compare('résumé', 'resume'); // => true Y.☃.AccentFold.filter(['föö',

    'bår', 'móó'], function (str) { return str.indexOf('o') !== -1; }); // => ['föö', 'móó'] ‚-accentfold
  54. Y.☃.AccentFold.fold('résumé'); // => 'resume' Y.☃.AccentFold.compare('résumé', 'resume'); // => true Y.☃.AccentFold.filter(['föö',

    'bår', 'móó'], function (str) { return str.indexOf('o') !== -1; }); // => ['föö', 'móó'] ‚-accentfold
  55. Y.☃.AccentFold.fold('résumé'); // => 'resume' Y.☃.AccentFold.compare('résumé', 'resume'); // => true Y.☃.AccentFold.filter(['föö',

    'bår', 'móó'], function (str) { return str.indexOf('o') !== -1; }); // => ['föö', 'móó'] ‚-accentfold
  56. Y.☃.AccentFold.fold('résumé'); // => 'resume' Y.☃.AccentFold.compare('résumé', 'resume'); // => true Y.☃.AccentFold.filter(['föö',

    'bår', 'móó'], function (str) { return str.indexOf('o') !== -1; }); // => ['föö', 'móó'] ‚-accentfold
  57. ‚-wordbreak • Implements ‚ text segmentation guidelines for word breaking.

    • Handles edge cases like contractions, decimals, thousands separators, Katakana, non-Latin punctuation, etc. • Not perfect, but much smarter than /\b/. • Will have an actual name before 3.3.0 final.
  58. Y.☃.WordBreak.getWords("A kilobyte's just 1,024 bytes."); // => ["A", "kilobyte's", "just",

    "1,024", "bytes"] Y.☃.WordBreak.getWords('ύΠ͕޷͖Ͱ͢'); // => ["ύΠ", "͕", "޷", "͖", "Ͱ", "͢"] ‚-wordbreak
  59. Y.☃.WordBreak.getWords("A kilobyte's just 1,024 bytes."); // => ["A", "kilobyte's", "just",

    "1,024", "bytes"] Y.☃.WordBreak.getWords('ύΠ͕޷͖Ͱ͢'); // => ["ύΠ", "͕", "޷", "͖", "Ͱ", "͢"] ‚-wordbreak
  60. Y.☃.WordBreak.getWords("A kilobyte's just 1,024 bytes."); // => ["A", "kilobyte's", "just",

    "1,024", "bytes"] Y.☃.WordBreak.getWords('ύΠ͕޷͖Ͱ͢'); // => ["ύΠ", "͕", "޷", "͖", "Ͱ", "͢"] ‚-wordbreak
  61. highlight • Static utility methods for highlighting strings using HTML.

    • Supports phrase highlighting, start-of-string highlighting, and word highlighting. • Can use accent folded strings for comparisons while highlighting the original, non-folded string. • Smart enough not to highlight inside HTML entities like &quot;. • Doesn’t infringe any trademarks.
  62. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  63. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  64. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  65. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  66. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  67. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  68. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  69. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  70. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  71. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  72. Y.Highlight.all('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010:

    <b class="yui3- highlight">YUI</b> rocks!' Y.Highlight.start('YUIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YUI</b>Conf 2010: YUI rocks!' Y.Highlight.words('YUIConf 2010: YUI rocks!', 'yui'); // => 'YUIConf 2010: <b class="yui3-highlight">YUI</b> rocks!' Y.Highlight.allFold('YÜIConf 2010: YUI rocks!', 'yui'); // => '<b class="yui3-highlight">YÜI</b>Conf 2010: <b class="yui3- highlight">YUI</b> rocks!' highlight
  73. Why <b>? • HTML5 defines a new <mark> element for

    denoting the relevance (as opposed to the importance) of a span of text. • It recommends using <mark> “to highlight a part of quoted text that was not originally emphasized”. • But, since <mark> isn’t yet supported in all browsers, we use <b>, which HTML5 defines as a last resort for highlighting text without implying importance. • You can customize the highlighting markup by changing Y.Highlight._TEMPLATE. More details: http://www.whatwg.org/specs/web-apps/current-work/multipage/text- level-semantics.html#the-mark-element
  74. What I just said but with bullets • Modular architecture

    makes it easy to implement a range of autocomplete patterns. • Dead simple JSONP and YQL support. • Use prepackaged filters and highlighters with word breaking and accent folding, or roll your own. • Create custom result formatters with just a few lines of code. • AutoComplete and friends are available now in YUI 3.3.0pr1. Photo: http://www.flickr.com/photos/tim_norris/2789759648/
  75. Ryan Grove, YUI Team @yaypie on Twitter https://github.com/rgrove/ Slides: http://lanyrd.com/smym

    Photo: http://www.flickr.com/photos/damaradeaella/2822846819/