find a location for property in a new city

Wednesday 18 April 2012

jQuery - Don't use bind and definitely don't use live!

If you use jQuery you have almost certainly used the .click(fn) event. You may know that just calls .bind('click', fn). You may have used live(). You may know that live() is weird and buggy and inefficient. You may have heard you should not use it in favour of .delegate(). You may have heard of the new .on(). But which is better? bind vs live vs delegate vs on?

Take the following html:

<div id="myContainer">
    <input type="button" id="myBtn" value="Click me!" />
<script type="text/javascript>

I have a button that has a javascript function called doSomething attached to it. This works fine until I dynamically replace the inner HTML of the div myContainer with a new button (with same ID etc). doSomething will no longer be attached to the new myBtn button so it will not be called when the button is clicked.

FYI, $('#myBtn').click(doSomething); is just shorthand for writing $('#myBtn').bind('click', doSomething);

So you can change the JavaScript code above to $('#myBtn').live('click', doSomething); and it will still work even when the button is brought in or generated dynamically. Up until now I have found that the live function uses magic to just make it all work even after ajax abuse and therefore it is better.

However, recently (in a more complex implementation) I found it was causing some undesired behaviour so I had a dig into it. Turns out live() is buggy and badly performing and has actually been deprecated as of jQuery v1.7 so do not use live! What should you be using for this type of functionality then?

Well, delegate has been the popular replacement since it attaches the method once to a selected container but still targets the inner element as bind or click would. The importance is two fold:

  1. The functionality is not wastefully attached to each element matched and instead just once to the containing element
  2. Dynamically added items that match the selector will still fire the event since the event is not attached to this element, it is attached to the container that has remained static

In fact live does the same as delegate but it's containing element cannot be defined, it is the whole document which, if you have a deep DOM, finding the originating element using the selector could cause it to search a long way. Lame... In fact, I found bugs with it and there are other details which mean you should use delegate instead. I say should because there is a new cool kid on the block.

So what is better than bind, live and delegate?

You should use .on(). All the cool kids are doing it. Want me to name drop users of on? Well, ME! Perhaps more famous in the JavaScript world is jQuery, and they use it! If you look at the jQuery library bind, live and delegate all just call on as of jQuery v1.7+

So my JavaScript code above should change to:

Bind and click are still fine in my opinion as nice little shortcuts but only when using it them to attach a function to one specific element and only when you are not attaching to dynamic objects. If you are attaching to, say, multiple <li>’s in a <ul> you should use on() instead to attach the event to the <ul> and target the event to the <li>’s. This way there is only one function and multiple references to it rather than creating the function once for each of the <li>’s.

Follow britishdev on Twitter

Tuesday 17 April 2012

What to do with LastIndexOf unexpected ArgumentOutOfRangeException behaviour

Using string.LastIndexOf(char value, int startIndex, int count); started giving me some behaviour I wasn't expecting when it started giving me an ArgumentOutOfRangeException Exception with a description of: "Count must be positive and count must refer to a location within the string/array/collection."

Let me give you an example:

var message = ".NET is unusually faultless!";
var maxSearch = 16;
var count = message.LastIndexOf('l', 0, maxSearch);
var newMsg = message.Substring(0, count);

This should work right? What could be wrong with this? Well according to IntelliSense "Reports the index position of the last occurrence of the specified Unicode character in a substring within this instance. The search starts at a specified character position and examines a specified number of character positions." Hmm.. Okay. And the count parameter that I am getting wrong? "The number of character positions to examine." Seems legit.

I decided to disassemble .NET to have a peep at what's going on internally... Have I found a bug in .NET?! Can't tell because the method is extern, so I cannot look at it. I instead did some experimenting and worked it out.

How to properly use LastIndexOf

Turns out I was just using it incorrectly and blaming my tools. Although I am convinced some blame lies with IntelliSense for forgetting to mention that LastIndexOf searches backwards through the string! Should I have known that? I don't know. Anyway this is what I should have written:

var message = ".NET is unusually faultless!";
var maxSearch = 16;
var count = message.LastIndexOf('l', maxSearch, maxSearch);
var newMsg = message.Substring(0, count);

The startIndex parameter should be at the last point you want to search backwards from. The count then specifies the amount of chars for your search to go back through.

Maybe I'm just being stupid or maybe it is just that Console.WriteLine(newMsg);

Follow britishdev on Twitter