Monday, March 12, 2012

Find the jQuery Bug #6: Traversing Trouble

Introduction


In this open-ended series I'll be showcasing a snippet of buggy jQuery code that you might encounter, explain what the problem is, and then identify how you can easily resolve the issue.

You can view other posts in this series...

The Desired Feature


We want to take the following list of jQuery board and team members and then hide only those that are team members, leaving only the board members showing.


The Buggy Code


The following code snippet is our first attempt at solving the problem, but there is a subtle error. Do you see it?


You can view, run, and edit the above code sample from jsFiddle.

The results that we expected was to only view a subset of the total list, but instead we ended up seeing all the items in the list!



The Underlying Problem


At the root of the problem is that the .find() method is used for finding elements that are descendants of the current jQuery collection.

.find( selector )
Returns: jQuery
Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.

-- http://api.jquery.com/find/

The above code snippet already starts with a jQuery collection of 22 items referenced by the $items variable. When calling the $item.find( ".team" ) method jQuery looks for all elements containing the team class that are children of it's internal collection. In this case, the list items do not have any children, so the result is an empty jQuery collection.

It is important to note that jQuery allows you to call methods off of any jQuery collection even if it is empty. The thing is that it just doesn't do anything, it silently fails. What we really need to solve this problem is to have some way to narrow down the internal jQuery collection based on a specified criteria. Thankfully, there is an easy way to do this.

A Solution


The solution to fix this problem is really simple and straightforward. The main problem is that we were using the wrong method.

We should have been using the .filter() method instead, which takes the current jQuery collection and filters them by matching against a provided selector. It doesn't traverse the children at all, but it's only purpose is to reduce the number of top level elements currently captured in the jQuery collection.

.filter( selector )
Returns: jQuery
Reduce the set of matched elements to those that match the selector or pass the function's test.

-- http://api.jquery.com/filter/

All you really need to do is to use the .filter() method instead of the .find() as we used in the previous example.


You can view, run, and edit the above code sample from JSBin.

If you test out the code again below you'll notice that the list items with class of team are targeted and hidden like we wanted!



Conclusion


The key concept to remember here is that the .find() method is for traversing into the DOM and locating descendants that match a criteria and the .filter() method is used to reduced the elements that are already selected that match a criteria.

This may seem like a trivial concept to grasp by some, but I've seen this common confusion of the two methods numerous times. I find that many developers expect that the .find() method will perform both filter and find, but it doesn't.

Until next time...

Tuesday, March 06, 2012

Find the jQuery Bug #5: Defective Data

Introduction


In this open-ended series I'll be showcasing a snippet of buggy jQuery code that you might encounter, explain what the problem is, and then identify how you can easily resolve the issue.

You can view other posts in this series...

The Desired Feature


We want to build a simple jQuery Plugin that will add a confirm message to a simple button or anchor.


The Buggy Code


The following code snippet is our first attempt at building the plugin, but there is a subtle error. Do you see it?


You can view, run, and edit the above code sample from jsFiddle.

The developer initialized the buttons on the page and then wanted to update the prompt for the first button. He removed all the event handlers added by the plugin, updated the attribute, and then re-initialized the button with the plugin. Unfortunately, this technique didn't work as intended.

The Underlying Problem


At the root of the problem is an issue when trying to use the .attr() method after already using the .data() method.

"Only the .data() API reads HTML5 data-* attributes, and it does so once." -- Dave Methvin http://www.learningjquery.com/...

The jQuery plugin reads the HTML5 data-* attribute first, and the developer tried to update the data-* attribute later with the .attr() method. Once the jQuery plugin is re-initialized on the DOM element it will not read the data-* attribute again, but instead use the data it retrieved the first time.

A Solution


The solution to fix this problem is really simple and straightforward. All you really need to do is to change the code where the data-* attribute was updated to use the .data() method instead of the .attr() method.


You can view, run, and edit the above code sample from jsFiddle.

If you test out the code again below you'll notice that it behaves as expected. When you click on the button it will use the updated text that was provided before the plugin was re-initialized.

You may notice that I also changed the key parameter to the .data() method to camelCase instead of the dashed version I had previously. As of jQuery 1.6 the library has changed direction in how they deal with HTML5 data-* attributes.

"The treatment of attributes with embedded dashes was changed in jQuery 1.6 to conform to the W3C HTML5 specification." -- http://api.jquery.com/data...

Now, you can still reference that HTML5 data-* attributes using their dashed keys, but jQuery will first attempt to use the key "as is" and only attempt to convert that to a camelCased version if it failed. See the following comment from the Dave's article I referenced above.

"Because many people will use .data( "camel-case" ) instead, we convert that to camelCase as well, but only if no data item named camel-case is found so it's faster to use the first form." -- Dave Methvin http://www.learningjquery.com/...

Other Things You Might Want to Know


Something that you also may not know about the $.data() method is that it will attempt to convert the contents of a HTML5 data-* attributes into the appropriate type.

"Every attempt is made to convert the string to a JavaScript value (this includes booleans, numbers, objects, arrays, and null) otherwise it is left as a string. To retrieve the value's attribute as a string without any attempt to convert it, use the .attr() method." -- http://api.jquery.com/data...

The following example shows a DOM element using various types of HTML5 data-* attributes, and then shows that when you call the .data() method to retrieve a value it will parse and convert it into a boolean, number, object, array, or null.



You can view, run, and edit the above code sample from jsFiddle.

If for some reason you didn't want jQuery to convert the HTML data-* attributes when using the .data() method, you could use the .attr() method instead. The .attr() method will always return the string version of the attribute.

Conclusion


The key concept to remember here is that once you call the .data() getter method on a element then it will read the HTML5 data-* atributes only once. If you want to update the data from there on, then you'll need to use the .data() setter method. Also, it is important to know that when you read these HTML5 data-* attributes jQuery will convert those values into it's appropriate type.

I hope you found this helpful. I recommend you reading Dave Methvin's article entitled Using jQuery’s Data APIs.

Please provide any feedback in the comments below. Until next time...

Monday, February 27, 2012

TextMate-like ⌘T & ⇧⌘T in Chrome Dev Tools & Other New Features

Introduction


For the past year or so I've used Google Chrome Canary as my primary web browser, however, about a month ago I temporarily switched to the stable build of Chrome because of some instability in the dev tools. I just recently switched back to Canary and was pleasantly surprised to see numerous new features in the dev tools there were very welcome indeed!

This post only covers the extreme latest in Chrome Dev Tools. There are so many more rich features that can be found. If you are interested about more helpful features you might be interested in a post I did last year entitled 7 Chrome Tips Developers & Designers May Not Know

If you are not familiar with the Canary build of Chrome, it is a extremely dev version of the browser. You should be cautious because it does update almost every day, but at the same time you get first in line to see all the new features. Unlike the dev or beta builds of Chrome you can actually install Canary side-by-side next to your Stable or Beta build of Chrome. This makes switching between versions very convenient.

The features I am about to describe are currently only available in the dev or canary builds of Chrome. If you have the Beta or Stable builds then you will not see these yet, but hopefully they'll make their way to those builds sooner than later. At least this will wet your appetite ;)

Scripts Explorer


A welcome change to the Scripts tab is a new Scripts Explorer (as seen in the below image). The previous way to navigate through the scripts of a webpage was to open a HUGE drop down containing tons of script files. Chrome kept reorganizing that huge drop-down list to make it more usable, but in the end it wasn't optimal when dealing with a site with lots of JavaScript fiels.

The new Scripts Explorer nicely separates the Scripts used on the webpage from the Content Scripts used in all of the Chrome Extensions you have installed in your browser. Historically, it was slightly annoying to see all those Content Scripts alongside your main JavaScript files in that huge drop down mentioned above. Again, over time Chrome moved those Content Scripts to the bottom of that drop down instead of intermingled, but this move to the Scripts Explorer is head over heals WAY better... YAY!

In addition, you can also choose to dock the Scripts Explorer to the left side of the dev tools (by clicking the little icon in the upper right) or have it auto-close when you are done finding what you need.


TextMate's ⌘T "Go to File" Feature


I don't know about you, but I'm one of those keyboard shortcut junkies, so I am always on the lookout on how to do my job without using the mouse. So, I was even more excited to see support for quickly finding a JavaScript file by only using the keyboard! Chrome call's this feature "Go to Script" and it is similar to what you may have experience in TextMate using ⌘T or maybe in Sublime Text 2 with ⌘P.

Since ⌘T is already reserved for creating a new tab in Chrome, they have chosen ⌘O for this feature. As soon as you type ⌘O you will see the following dialog displaying JavaScript files. As you type the list will filter to only the files that match what you are typing. You can arrow up or down to narrow the selection even more and then click "enter" to open that script file.


TextMate's ⇧⌘T "Go to Symbol" Feature


Now, if the "Go to Script" feature wasn't enough, there is also the "Go to Function", which is similar to the "Go to Symbol" feature of TextMate using ⇧⌘T or ⌘R in Sublime Text 2.

Once you are in a JavaScript file you can press ⇧⌘O to bring up the "Go to Function" dialog. This works in a very similar way as the "Go to Script" dialog as we saw previously, but this time instead of looking for files, it helps you track down functions! Navigating through your JavaScript files to find a specific function has now become a breeze!


Dock Dev Tools to the Right


The last feature that stood out to me as really cool, was the new setting to "Dock to right". Many people these days have a wide-screen monitor and having the dev tools docked at the bottom of the screen sometimes feels a little scrunched. If I have my browser maximized there is usually tons of room to the left or right of the website I am viewing.

Thankfully, with this new feature I can now choose to dock the dev tool to the right of my screen to give some breathing room for development!

Note: I undrestand some like to undock the dev tools to solve this issue, but I usually tend to like keeping them docked so this new feature is very handy for me.


In order to turn on this feature, you'll need to go into the Settings dialog. You can access this by clicking on the gear icon located at the bottom right of your dev tools. Once the dialog has been launched you should see a General section with the "Doc to right" option listed.


Conclusion


I find the the above JavaScript enhancements to the dev tools have really made navigating through scripts to be much more enjoyable. If you have noticed any other new features that I have missed please let me know. They keep cropping up all the time... which is AWESOME!

If you have Chrome Canary then you can start using the above features right way, otherwise they should make it in a Beta or Stable build in the near future.