Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Saturday, August 23, 2008

How To Efficiently Search A JSON Array

We all know that JavaScript can be a great tool for enhancing the user experience of your website. However, when written poorly that tool can actually do the opposite. Today we will be addressing how to efficiently iterate on a JSON Array to see if one of the JSON objects contains a desired unique value (this technique only works with values that will be unique). A JSON Array is simply a JavaScript Array where each element is an Object, and each Object have the same keys. Lets consider the following JSON Array for a list of tag objects:

Example 1: Tags JSON Array

var tags = [ {tagId: 1, tagName: ‘tag 1′}, {tagId: 2, tagName: ‘tag 2′}, {tagId: 3, tagName: ‘tag 3′}, … {tagId: 99, tagName: ‘tag 99′} {tagId: 100, tagName: ‘tag 100′} ];

The ‘tags’ JSON Array contains 100 JSON objects, each containing the ‘tagId’ and ‘tagName’ keys. Now, assume that we have a text input somewhere on the page where a user can add a new tag. Before we let that user add a tag, we want to make sure that it doesn’t exist, therefore you might do the following:

Example 2: Common Way to Test For Existence

var hasTag = function(tagName) { var i = null; for (i = 0; tags.length > i; i += 1) { if (tags[i].tagName === tagName) { return true; } } return false; };

If you use Array.js then you could write the same method as:

Example 3: Common Way to Test For Existence Using Array.js

var hasTag = function(tagName) { return tags.batch(function(tag) { if (tag.tagName === tagName) { has = true; return has; } }); };

Now, if you only had to test if a tag existed once, then this technique would be fine. You can just call the ‘hasTag’ method once and then you are done. However, in the case we outlined above, each time a user enters a new tag, we have to call the ‘hasTag’ method. This is a very inefficient operation to call repeatedly and although it won’t severely affect performance in this example, because it is not done on page load or very frequently, it is bad practice. The more efficient way to test if a value exists inside of one of the contained objects, is to iterate through the Array once and create a Map of the keys to their values. Then the ‘hasTag’ method need only check against this map, which is much faster than iterating on the list:

Example 4: Creating a Map of Keys

var tagMap = {}; var i = null; for (i = 0; tags.length > i; i += 1) { tagMap[tags[i].tagName] = tags[i]; } var hasTag = function(tagName) { return tagMap[tagName]; };

The ‘hasTag’ method in Example 4 has a negligible performance hit, so you could even use it in a ‘mousemove’ event callback on the ‘document’, wihtout ruining your user’s experience. The point is that if you have a JSON Array (or an Array of like Objects) and have to search for a key value on an Object inside that Array (more than once), it is far more efficient to create a map of the ‘key’ or ‘keys’ you wish to search on, than to search the Array each time. Using this technique will improve the performance of your application and improve your the experience of your users.

posted by Matt Snider at 3:50 pm  

Wednesday, August 20, 2008

Tab Manager With Simple History

We recently released a new version of the Mint.com homepage with a tabbing system that: allows linking to a specific tabs, restoring the last tab on a page refresh, and basic tab history management in the better browsers (FireFox and Safari)… along with managing the tabs themselves to display the appropriate content. I have taken that concept, rewriting it to be lightweight (only 8kb uncompressed), leverage YUI and be configurable, in case you do not use the same conventions that we use at Mint. The new widget is HashTab and we will be today’s topic.

This widget uses a combination of naming convention and DOM queues to know which tab to display in a variety of situations. Lets discuss the simplest (default) case where you have a collection of tabs inside of a UL tag and a collection of tab content inside of a DIV tag. Your HTML will look something like this:

Example 1: Default HTML

<ul id=”tab-example”> <li class=”selected” id=”tab-nav1″><a href=”#nav1″>tab 1</a></li> <li id=”tab-nav2″><a href=”#nav2″>tab 2</a></li> <li id=”tab-nav3″><a href=”#nav3″>tab 3</a></li> </ul> <div id=”content-example”> <div class=”selected” id=”content-nav1″>Content 1</div> <div id=”content-nav2″>Content 2</div> <div id=”content-nav3″>Content 3</div> </div>

First you should notice that all the IDs consist of two words separated by a dash. Each word is a key used by HashTab to identify elements and interact with the DOM. The tab UL container ID begins with the key “tab”, which is also the first key of the IDs for each tab. The second word in the tab UL container ID is used to uniquely identify this tab container from any other on the page and won’t be explicitly used by HashTab. The content container works in the same way, where the first key in the container ID is the same as the first key in the IDs of each of the content sections. The second word in each of the tabs and content sections should have a 1 to 1 ratio with each other, so if there is a tab with ID “tab-nav1″, then there should be a content section with ID “content-nav1″. That is how the HashTab knows that clicking on the first tab should show the first content section. Lastly, inside of each tab is an anchor, that should link to the same hash as the second key in the tab ID (so “tab-nav1″ should have an anchor tag with a href of “#nav1″).

In addition to responding to clicks, HashTab also understands that if the page is requested with one of the hashes that is recognizes, then it will display that tab when the page loads. For example, the url: “http://www.mattsnider.com/hashTabTest.html#tab4″ will initialize the page with the 4th tab selected. HashTab is also smart enough to ignore hash values that are not part of the tab system, so you can still link to other anchors on your pages while using HashTab. In addition to linking to specific tabs, when the page refreshes HashTab will initialize the tabs to the last selected tab. And if you are using Safari or FireFox, HashTab will handle both forward and backwards history navigation (these browsers could be supported with only 6 lines of code).

Instantiating a new HashTab is easy, you need to only pass in the ID for the tab and content containers, plus an optional config Object as a third parameters:

Example 2: HashTab Instantiation

var hashTab = Core.Widget.HashTab(’tab-example’, ‘content-example’);

The configuration object allows you to customize almost any part of the widget. Below is an explanation of the keys recognized by HashTab:

Example 3: HashTab Configuration

delimiter: the character used to separate keys in the ID attribute for hashTab DOM nodes; default is ‘-’ elementContent: the type of node used for each of the content nodes; default is ‘div’ elementTab: the type of node used for each of the tab nodes; default is ‘li’ interval: the poll time to check for history changes; default is 250 (1/4 second) selected: the class to apply when a node is selected; default is ’selected’ triggerEvent: the event to trigger tabbing; default is ‘click’

Here is a Test Page showing how, in the first example, the default HashTab work, and how to use a few of the configuration settings in the second. Most importantly, notice how both HashTab widgets work independent of each other.

posted by Matt Snider at 9:31 am  

Tuesday, August 12, 2008

Multi-State Checkboxes

Recently, I was asked to build a 3-state system for checkboxes, so that there would be a checked, unchecked, and mixed state. So, I built a re-usabled widget that supports any number of states (not just the 3 mentioned above), and any number of checkboxes. I also desired that it be simple to use and easy to read.

The widget requires that you provide it a node in the DOM (preferably, an empty one) to contain the checkboxes, which it will clear and build your checkboxes inside. The DOM will be built as follows:

Example 1: Checkbox DOM

<ul> <li><label><input name=”nameOfCheckbox1″ type=”checkbox” value=”stateOfCheckbox1″/>Label for Checkbox 1</label></li> <li><label><input name=”nameOfCheckbox2″ type=”checkbox” value=”stateOfCheckbox2″/>Label for Checkbox 2</label></li> … </ul>

We will be styling the checkboxes to be invisible (opacity = 0) and applying a ‘click’ event to each checkbox, which will be used to trigger the checkbox state change. The checkbox is wrapped in a label, so that supporting browsers will allow users to click on the label to fire the checkbox ‘click’ event as well. Also, by using an invisible checkbox we allow users to tab through the checkboxes and use space to trigger the ‘click’ event, not limiting users to mouse only interaction (this is why I didn’t use the traditional ‘hidden’ input to store state). Since the checkbox is invisible, we will need to use a background image attached to the label for our multi-state checkboxes. Here are the styles that I recommend:

Example 2: Checkbox Styles

.checkbox ul li { clear: both; list-style: none; } .checkbox ul li input { opacity: 0; filter: alpha(opacity=0); } .checkbox ul li label .checkbox ul li label.unchecked { background: transparent url(images/unchecked.png) 0px 2px no-repeat; } .checkbox ul li label.mixed { background: transparent url(images/mixed.png) 0px 2px no-repeat; } .checkbox ul li label.checked { background: transparent url(images/checked.png) 0px 2px no-repeat; }

Example 3: State Images

uncheckeduncheckedunchecked

For the most part the this widget was designed to just work. However, it requires that you provide a DOM node and a list of states you wish to setup. The state list specifies what classes should be applied to the label tag for each state, the value of the state, and what the next state will be when the user clicks on the checkbox. You may also pass an optional configuration parameter which may include: the initial data representing the checkboxes, whether you want to render the checkboxes immediately, and/or a function you would like to attach to the ‘onCheck’ event. Here is an example of how to initialize a checkbox widget:

Example 4: Initialize the Checkbox

Mint.Widget.Checkbox(’checkbox’, [ {klass: ‘unchecked’, next: ‘B’, state: ‘R’}, {klass: ‘mixed’, next: ‘B’, state: ‘G’}, {klass: ‘checked’, next: ‘R’, state: ‘B’} ], { data: [ {name: ‘tag-A’, label: ‘Tag A’, value: ‘R’}, {name: ‘tag-B’, label: ‘Tag B’, value: ‘G’}, {name: ‘tag-C’, label: ‘Tag C’, value: ‘B’}, {name: ‘tag-D’, label: ‘Tag D’, value: ‘G’}, {name: ‘tag-E’, label: ‘Tag E’, value: ‘R’} ], render: true });

If you do not provide a configuration parameter, you can call the public ‘render’ Function at any time to build the checkboxes. You may also call the ‘render’ Function with a new data set at any point in the future to redraw the checkboxes with new values. I have illustrated several different instantiations of this widget for your testing pleasure: Checkbox Widget.

posted by Matt Snider at 11:09 pm  

Friday, August 8, 2008

Calling Flash From JavaScript

First, let me say that there are many different ways in which to embed Flash content on your website, including using nested HTML tags, browser detection, and/or JavaScript. There are also different Flash movie formats, but most people choose the SWF format, so we will discuss how to show SWF files. I do not regularly need to work with Flash, as I can do almost everything I want with JavaScript, however, there have been clients who demand its use. And my experience with Flash and SWF files has led me to chose SWFObject as my favorite technique for embedding Flash content on a page.

When embedding Flash content on a page, you will encounter issues in at least these 3 areas:

  • Is Flash supported in the client browser?
    Flash needs to be installed and enabled in the client browser, and you need to handle situations when it is not.

  • How does this browser handle Flash ?
    Flash is a proprietary technology and different browsers have chosen to support it in differing ways, especially detecting Flash versions and rendering alternative content when the flash player is not present.

  • Is the required Flash version support?
    Your code was written for Flash 9, but the client browser only has Flash 8. The user should be asked to upgrade their Flash version.

I am normally adamant about following standards and accessibility practices, which dictates that I not use JavaScript, but an HTML technique, such as found in the List Apart Article: Flash Satay: Embedding Flash While Supporting Standards. This method worked great for me, and until I started working on secure servers (SSL) where this technique causes IE 6 to show a security warning, it was my first choice.

When the Satay method failed me, I then looked to Adobe, the owner of the proprietary Flash technology. Adobe supports several methods for embedding Flash: Adobe Flash Detection Kit. I was hoping that since I was not able to use the more accessible Flash Satay method, that I could at least use officially supported technologies. Unfortunately, I was still receiving the same security warning on secure servers in IE 6 (although, the Adobe site claims that their script works around this).

Then, I asked myself it the IE 6 security warning was really a serious issue. And It is a huge issue, as the site is most likely secure, because it contains sensitive information. Users of a site, where their browser frequently warns them that they not secure, will be less likely to trust it that site. And even though the warning is bogus, your users won’t care.

The only technique successfully defeating the IE 6 security warning bug, is SWFObject, which is why I choose to use it above all others. It is also simple to use, and easy to setup alternative content. Here is a simple example:

Example 1: Using SWFObject

var so = new SWFObject(’/assets/swf/mySWF.swf’, ‘embeddedSwfId’, ‘950′, ‘950′, ‘9.0.115′, ‘#FFFFFF’); so.addParam(’quality’, ‘high’); so.addParam(’wmode’, ‘transparent’); so.addParam(’salign’, ‘tl’); so.write(’flash-container’); // DOM node to embed flash content into

This little code snippet will embed the ‘mySWF.swf’ inside the ‘flash-container’ element and assign the embedded SWF the id of ‘embeddedSwfId’. The create DOM node will have width and height of 950 and a background of black. Lastly, we tell the ‘SWFObject’ that we require version ‘9.0.115′ of the Flash player.

In addition to all the issues above, you may encounter server-related issues when requesting the Flash content (usually a SWF file) as a result of compression and/or caching techniques. Fortunately, these issues are well documented and you will most likely find a reasonable solution with a quick google search.

posted by Matt Snider at 8:59 pm  

Tuesday, August 5, 2008

Simple Image Viewer With Captions

It seems that, if I am to leave a legacy after this blog is gone, it will be my Super Simple Image Viewer, as more people read and ask about this widget than any other part of this blog. I attempted to address most people concerns in last weeks Somewhat Simple Image Viewer article, but I was only partially successful, as I did not add in the second most requested feature: captions. So I went back to the drawing board and have written a third version of the Image Viewer.

Version 3 supports both single collection and multiple collections of images (such as portfolios or events), and captions are optional. This way it is nearly backwards compatible with Version 2 that we discussed last week.

Users will quickly notice that the PhotoViewer Function now only requires two parameters: ‘cfg’ and ‘data’. Where ‘cfg’ is an Object containing DOM references and ‘data’ is the collection of image URLs (and other meta data). Below is an example of the ‘cfg’ Object:

Example 1: Configuration Object

var cfg = { caption: ‘viewer-caption’, // optional image: ‘viewer-image’, // required next: ‘viewer-next’, // required prev: ‘viewer-prev’ // required };

While the caption value is optional, if any of the other 3 DOM references are missing, PhotoViewer will alert you and not initialize. The ‘data’ can be in two different formats, first it can just be a Array of image URLs, second it can be an Object where each key is the DOM ID of the anchor tag to select that set and the value is the Array of images. Here are 3 different valid data sets:

Example 2: Data Object

// 3 sets of objects, containing the image and caption var data = { set1: [// this needs to be the DOM ID attribute for the anchor tag to select this set {image: ‘http://farm1.static.flickr.com/142/331222000_76f271e745.jpg?v=0′, caption: ‘frozen landscape’}, {image: ‘http://farm1.static.flickr.com/159/384269775_f8b21b5e52.jpg?v=0′, caption: ‘mountain stream’}, {image: ‘http://farm1.static.flickr.com/178/415791942_4a4e411464.jpg?v=0′, caption: ‘mountain lake’} ], set2: [// this needs to be the DOM ID attribute for the anchor tag to select this set {image: ‘http://farm1.static.flickr.com/186/421171930_80ef8ee7b2.jpg?v=0′, caption: ‘cool canpopy’}, {image: ‘http://farm1.static.flickr.com/155/421172107_2f20ec7aeb.jpg?v=0′, caption: ‘cool tree’}, {image: ‘http://farm1.static.flickr.com/54/133256387_e99cd967ff.jpg?v=0′, caption: ‘vibrant rose’} ], set3: [// this needs to be the DOM ID attribute for the anchor tag to select this set {image: ‘http://farm1.static.flickr.com/46/167939744_2357ffe1af.jpg?v=0′, caption: ‘white mountains’}, {image: ‘http://farm1.static.flickr.com/48/177853809_ad9454a89e.jpg?v=0′, caption: ’small turtle’}, {image: ‘http://farm1.static.flickr.com/54/183725415_ff609f45e4.jpg?v=0′, caption: ‘mountain sunset’} ] }; // 3 sets of image URLs var data2 = { set4: // this needs to be the DOM ID attribute for the anchor tag to select this set [’http://farm1.static.flickr.com/142/331222000_76f271e745.jpg?v=0′, ‘http://farm1.static.flickr.com/159/384269775_f8b21b5e52.jpg?v=0′, ‘http://farm1.static.flickr.com/178/415791942_4a4e411464.jpg?v=0′], set5: // this needs to be the DOM ID attribute for the anchor tag to select this set [’http://farm1.static.flickr.com/186/421171930_80ef8ee7b2.jpg?v=0′, ‘http://farm1.static.flickr.com/155/421172107_2f20ec7aeb.jpg?v=0′, ‘http://farm1.static.flickr.com/54/133256387_e99cd967ff.jpg?v=0′], set6: // this needs to be the DOM ID attribute for the anchor tag to select this set [’http://farm1.static.flickr.com/46/167939744_2357ffe1af.jpg?v=0′, ‘http://farm1.static.flickr.com/48/177853809_ad9454a89e.jpg?v=0′, ‘http://farm1.static.flickr.com/54/183725415_ff609f45e4.jpg?v=0′] }; // an array of image URLs var data3 = [ ‘http://farm1.static.flickr.com/142/331222000_76f271e745.jpg?v=0′, ‘http://farm1.static.flickr.com/159/384269775_f8b21b5e52.jpg?v=0′, ‘http://farm1.static.flickr.com/178/415791942_4a4e411464.jpg?v=0′, ‘http://farm1.static.flickr.com/186/421171930_80ef8ee7b2.jpg?v=0′, ‘http://farm1.static.flickr.com/155/421172107_2f20ec7aeb.jpg?v=0′, ‘http://farm1.static.flickr.com/54/133256387_e99cd967ff.jpg?v=0′, ‘http://farm1.static.flickr.com/46/167939744_2357ffe1af.jpg?v=0′, ‘http://farm1.static.flickr.com/48/177853809_ad9454a89e.jpg?v=0′, ‘http://farm1.static.flickr.com/54/183725415_ff609f45e4.jpg?v=0′ ];

Data set 1 shows how to add captions into your PhotoViewer, while set 2 is exactly the same as we used last week, and set 3 is supported to for those of you who don’t need multiple image collections. You can see an example of all 3, Image Viewer Example and the JavaScript code here. Also, to support this more powerful version, I needed extra library code, and you will need to include the following 3 files before importing the PhotoViewer script.

Example 3: JavaScript Files to Include

// core dom and event framework <script type=”text/javascript” src=”http://yui.yahooapis.com/2.5.2/build/yahoo-dom-event/yahoo-dom-event.js”></script> // required for type detection <script type=’text/javascript’ src=”http://www.mattsnider.com/assets/js/core/type.js”></script> // required for object manipulation <script type=”text/javascript” src=”http://www.mattsnider.com/assets/js/object.js”></script>

Lastly, version 3 has a brand new feature, that improves the initial performance of the PhotoViewer widget. I have noticed that in some browsers, like FF2+, even though we are pre-caching the images (using circa 1999 technology), the first time we clicked to see an image it takes an exceptionally long time to load, as if it wasn’t pre-cached (and it probably isn’t). Browsers have become rather smart and to improve performance they don’t always download files, when they don’t believe they are necessary. Therefore, to defeat the browser intelligence, I have created a hidden node at the end of the body tag, in which we will append each image that we want to cache. This tricks the browser into downloading (and pre-caching) the images, thereby drastically improving the performance the first time iterates through the images in PhotoViewer.

posted by Matt Snider at 9:56 am  

Saturday, August 2, 2008

Boolean Functions

There is very little you need to attach to Boolean, as it is a very simple object. I have only found two methods that I like to attach to the ‘Boolean’ Object: ‘is’ and ‘get’. The ‘is’ method is a type detection like I have attached to other native JavaScript objects. The ‘get’ method is to convert falsy/truthy values into ‘false’ and ‘true’. While this isn’t necessary because you can use “! yourObject”, however I frequently like to use strict, type comparisons operations, such as ‘===’ and ‘!==’.

Check out object.js here and the unit test here.

As a side note, I have been updating the blog layout a bit. Hopefully, I am making this blog more useful.

posted by Matt Snider at 5:33 pm  

Wednesday, July 30, 2008

Somewhat Simple Image Viewer

The last two days I have been catching up on blog comments and related emails. Sorry for any delays. For the most part I have either responded to comments directly in the blog, and written an occasional email response as necessary.

Anyway, one of the most requested features is an improvement to the Super Simple Image Viewer (SSIV) to allow multiple instances on the same page. As the SSIV is designed to be really simple, I do not want to muck around with it. So I have created a second version that is not quite as simple, but works by instantiation instead of a static global object.

Somewhat Simple Image Viewer

To get this code to work, include the following on your site just before the closing body tag:

Example 1: Somewhat Simple Image Viewer

<script type=”text/javascript” src=”http://yui.yahooapis.com/2.5.2/build/yahoo-dom-event/yahoo-dom-event.js”></script> <!– This is the Framework I use for building JavaScript applications; designed by YAHOO –> <script type=”text/javascript” src=”http://mattsnider.com/assets/js/widget/matts_photo_viewer2.js”></script> <!– This is the actual JavaScript Photo Viewer application –> <script type=”text/javascript”><!– // replace these with the actual names of your files var data = { portfolio1: // this needs to be the DOM ID attribute for the anchor tag to select this set [’http://farm1.static.flickr.com/142/331222000_76f271e745.jpg?v=0′, ‘http://farm1.static.flickr.com/159/384269775_f8b21b5e52.jpg?v=0′, ‘http://farm1.static.flickr.com/178/415791942_4a4e411464.jpg?v=0′], portfolio2: // this needs to be the DOM ID attribute for the anchor tag to select this set [’http://farm1.static.flickr.com/186/421171930_80ef8ee7b2.jpg?v=0′, ‘http://farm1.static.flickr.com/155/421172107_2f20ec7aeb.jpg?v=0′, ‘http://farm1.static.flickr.com/54/133256387_e99cd967ff.jpg?v=0′] }; var data2 = { portfolio3: // this needs to be the DOM ID attribute for the anchor tag to select this set [’http://farm1.static.flickr.com/142/331222000_76f271e745.jpg?v=0′, ‘http://farm1.static.flickr.com/159/384269775_f8b21b5e52.jpg?v=0′, ‘http://farm1.static.flickr.com/178/415791942_4a4e411464.jpg?v=0′], portfolio4: // this needs to be the DOM ID attribute for the anchor tag to select this set [’http://farm1.static.flickr.com/186/421171930_80ef8ee7b2.jpg?v=0′, ‘http://farm1.static.flickr.com/155/421172107_2f20ec7aeb.jpg?v=0′, ‘http://farm1.static.flickr.com/54/133256387_e99cd967ff.jpg?v=0′] }; var photoViewer1 = Core.Widget.PhotoViewer(data, ‘image’, ’showNext’, ’showPrev’); var photoViewer2 = Core.Widget.PhotoViewer(data2, ‘image2′, ’showNext2′, ’showPrev2′); // additional photo viewers go here, just pass the HTML ID for the main image, and the next/previous anchors –></script>

Instead of calling the ‘addPhoto’ method, as we did with the original Image Viewer, that created and managed the Image Viewer data, we now create the data manually. In this case, the data Object needs a key for each collection of images needed for the Image Viewer; if there is only 1 collection of images, then ‘data’ can just be an Array, instead of an Object. If you have multiple collections of images, say different portfolios (as in Example 1), then the key for each collection of images needs to be the same as the ID attribute of the anchor tag to be used for choosing that collection in the DOM. This anchor will have an event attached to it that updates the Image Viewer to the correct collection, and an error with be thrown if you do not have a matching anchor tag in your page.

However, for the most part it works nearly identically as the original ImageViewer. I tried to change as little as possible, so that it would be easy to convert original ImageViewer pages to this new one.

posted by Matt Snider at 6:50 pm  

Saturday, July 26, 2008

Object Functions

While it is always best practice to never extend the ‘prototype’ of Object, it is fine to extend Object directly with your own static methods. There are 4 methods that I find critical to working with JavaScript objects: forEach, getIgnoreKeys, is, and toQueryString. The ‘forEach’ method iterates on the Object using the “for … in” loop, but calls ‘getIgnoreKeys’ to determine which keys are methods attached to the Object prototype, and ignores those values. You can also use the ‘forEach’ method to iterate on an Array, even though it would be more efficient to use the ‘forEach’ method we attached to Array in my last post. Lastly, the ‘is’ Function is simply a type detection method and ‘toQueryString’ calls ‘forEach’ so that we can construct a query string, “key1=value1&key2=value2&key3=value3…”.

Check out object.js here and the unit test here.

posted by Matt Snider at 11:40 am  

Tuesday, July 22, 2008

Extending Array

I have not written code extending Array yet, as I have had concerns about breaking the web while extending Array. The problem is that any changes to the prototype of Array breaks “for … in” loops. After some discussion with other JavaScript architects and further thought, it became apparent that extending Array doesn’t really break the web. First of all, the only issue is that after modifying the “Array.prototype”, using “for … in” loops on an Array returns each attached Function, as well as each member of the Array, so you would have to do what YUI does and ignore theses keys. However, you would only need to use “for … in” loops if you were creating associative arrays, and an associative Array can be represented instead with an Object, so there is no need to use them. Therefore, it is good practice to use Object in place of any associative Arrays. If you follow this practice, then one only needs to iterate numerically on an Array, never needing the “for … in” loop; allowing us to safely extend “Array.prototype” with useful methods.

The most important method is probably the “Array.get” Function attached statically to Array. This method converts Array-like objects such as ‘arguments’ and ‘collection’ to an Array, so that you get all the syntactic sugar of “array.js” on non-arrays. This is most useful in conjunction with “document.getElementsByTagName” or simply ‘arguments’.

I have also added the concept of a pointer to Array so that you can iterate through the Array with functions like: ‘next’, ‘prev’, and ‘current’, which point to the appropriate index in the Array. Some other useful short-hand methods like: ‘first’ and ‘last’ have also been added.

Beyond those, there is a slew of other useful functions, that allow for easy Array management. It is all well documented, check “array.js” out here. And I have written an array unit test here.

posted by Matt Snider at 8:49 pm  

Sunday, July 20, 2008

Extending String, Revisited

In the past, I wrote an article extending the native JavaScript String Object with useful methods and also about a RegExp escape method:

String Functions
RegExp Escape

I have revisited those methods and also added several other methods to String.prototype and static methods to String that I find useful. Check out the new String.js here and the unit test here if you want to double check that it all works well on your favorite browser.

The first noteworthy change is that I have migrated the “RegExp.esc” method to “String.prototype.escapeRx”. The old “RegExp.esc” method always required that a String be passed in, so it made sense to extend String with this functionality, instead of always calling a static method and passing a string. This allowed me to also not have a dependancy on the “string.js” utility file in my “regexp.js” utility file.

The second noteworthy change, is “String.prototype.decode” Function. This was written by Jason Yiin at Mint.com and unescapes HTML and XML entities. I have had numerous issues with server-side view technologies over escaping my HTML and/or XML code, and this Function has allowed me to correct these indiscretions.

Otherwise, I believe the files are similar to the older versions, but dependencies on other utility Functions have been removed (except Core.js) and they fail more gracefully, when bad parameters are provided.

posted by Matt Snider at 1:41 pm  
« Previous PageNext Page »

Powered by WordPress