Sunday, July 27, 2014

This blog is oficially closed

This blog is officially closed


It's been quite some years since I last posted on this blog. At some point the amount of daily inquires in the comments became overwhelming for the time I had available so I locked them.

If someone has a doubt or an issue with a plugin I'd advice you to ask on sites like StackOverflow.
If you think you found a bug or have a feature request feel free to create an issue on the appropriate project on Github. I try to reply fast but there's no guarantee.

Sunday, June 14, 2009

jQuery.SerialScroll 1.2.2 released

Enhancements

  • Removed the check that avoids re-scrolling to the same index, this might be desirable sometimes.
  • The settings object is exposed in the onBefore as the this.

Links

Downloads

Saturday, June 6, 2009

Moving my plugins to Google Code

Google Code

I decided to move my plugins from my private svn to Google Code. This means the code being developed is now public and can be viewed and reviewed by anyone.

I'm also trying to automate the building process with a makefile in order to make deploys less tedious (thus more frequent :)).

Everyone's invited to check the code, review it, comment and report bugs. The repository has a few plugins that I actually never released. It doesn't contain non-jQuery scripts yet. I haven't moved them yet and won't do yet.


Links

Saturday, May 30, 2009

2 Ajax plugins for jQuery +1.3

Introduction

I never actually posted about 2 tiny plugins I made, once jQuery 1.3 was nearing.

Both are somehow related, they are related to Ajax (jQuery.ajax) and they provide sort of a manager to allow multiple implementations of the same feature.

Needless to say, both plugins only work under jQuery 1.3 or higher. The packages(zips) include jQuery 1.2.7pre because that's the version I used to test them when I made them. You obviously don't need to use that version.


XHR

Since jQuery 1.3. The default XHR "factory" function can be overriden by passing an 'xhr' setting to jQuery.ajax with a new function

This tiny plugin provides a simple registry for different xhr implementations to co-exist.

To provide a new implementation, you need to do something like this:

jQuery.xhr.register( 'my_xhr', function( settings ){
  return new MyXhrImplementation( settings );
});

The argument settings is the object passed to jQuery.ajax.

To use it, you do:

jQuery.ajax({
    url:'...',
    transport:'my_xhr',
    // ...
});

The built-in implementation is used by default (unless it's overriden with jQuery.ajaxSetup) and it's called 'xhr'.

Links & Downloads

AjaxFilter

This plugin lets you store multiple functions to sanitize ajax responses.

If you want to provide a new implementation, you need to do something like this:

jQuery.ajaxFilter.register('js_in_href','html',
 function( data ){
  return data.replace(/href="javascript:[^"]+"/g, '');
});

or

jQuery.ajaxFilter.register('eval','script html json',
 function( data ){
   return data.replace(/eval\(.+?\);?/g, '');
});

Arguments for jQuery.ajaxFilter.register() are:

  1. Name for the filter, used as 'filter' when calling jQuery.ajax.
  2. One or more dataTypes to handle. Can be any combination of ajax, html, json and xml separated by spaces.
  3. The filter function. Will receive 2 arguments: the data and the type. The 'this' will reference the settings object.

To use it, you do:

jQuery.ajax({
    url:'foo.html',
    filter:'js_in_href',
    // ...
});

The filter can also parse the response.
That means it can (for example) provide an alternative way of eval'ing json. This is specially useful for Adobe AIR apps. If the filter returns something else than a string, it's assumed to be the final response.

Links & Downloads

Monday, May 25, 2009

jQuery.ScrollTo 1.4.2 released

Notice

I've pretty much stopped updating this blog, but the plugin development is still on-going. You can find the link to the Github project page at the bottom of the article.

Fixes

  • Fixing max calculations for regular DOM elements.

Features

  • The plugin support percentages as target ('50%' or {top:'50%', left:'45%'}).
  • Exposed the max() calculation as $.scrollTo.max.

Enhancements

  • Renamed $.fn.scrollable to $.fn._scrollable to avoid conflicts with other plugins.

Links

Wednesday, March 11, 2009

jQuery.LocalScroll 1.2.7 released

Notice

I've pretty much stopped updating this blog, but the plugin development is still on-going. You can find the link to the Github project page at the bottom of the article.

In this release I cleaned up the code a little and added some features. Also removed a few old (aka deprecated) stuff that were still hanging around.

Enhancements

  • Added some misc enhancements and cleanup the code.
  • Updated the plugin to take advantage of recent scrollTo additions.

Changes

  • The element that triggered the scroll cannot be accessed anymore from within the onBefore, you can bind your own click (or w/e) to them in order to add a class or things like that.
  • settings.persistent is no longer supported (was deprecated).

Features

  • The set of settings can be accessed from within the onBefore as the 'this'.
  • The hash (#foo) is set to the URL before scrolling, so the back button works accurately (when scrolling the window).
  • The option 'hash' doesn't make the window jump when scrolling overflown elements
  • $.localScroll.hash now resets the element scroll to (0,0) by default. You can set the setting 'reset' to false to avoid this.
Enjoy!

Links

Monday, March 9, 2009

jQuery.ScrollTo 1.4.1 released

Fixes

  • The plugin accepts floating numbers.
  • Using jQuery.nodeName where neccessary so that this works on xml+xhtml.
  • The max() internal function wasn't completely accurrate, now it is 98% (except for IE on quirks mode but it's not too noticeable).

Features

  • The target can be 'max' to scroll to the end while keeping it elegant.
  • The plugin works on quirks mode.

Enhancements

  • Default duration is 0 for jquery +1.3. Means sync animations.
  • Rewrote $.fn.scrollable() again. The plugin works on all major browsers (FF, IE, Safari, Opera, Chrome), on all versions, compat & quirks modes, even for iframes.
  • In addition to window/document, if html or body are received, the plugin will choose the right one.

Links

Monday, November 17, 2008

Haskell Functions for Javascript

Introduction

I've been doing a small Haskell project for the Uni and got really interested in this language and the Functional paradigm in general.

I suddenly felt like porting some of this cool functionality to Javascript.

Currying

I started by allowing functions to be curried. Here's a demo showing the script I made.

This is quite a fun way of making functions that derive from other functions.

Haskell functions

I then felt like porting Haskell's functions. Seemed doable considering Javascript has Higher Order Functions and we now can curry them.

I ported many Prelude's methods as well as Char methods.

Special features

Apart from currying, these functions can also work on strings, the same way as they do for lists (arrays). You don't need to worry about splitting(before) or joining(after). I added this because that's how Haskell's functions work.

These functions also work on maps/object/associative arrays. This might not be true for some functions where this was too complicated. This was the best adaptation of Haskell's Tuples that I could think of. A couple of functions like fst and snd don't have a concrete application for now.

In case I didn't make myself clear on this one, all the functions I made can be automatically curried. They're all contained in one namespace, that is, window.hs. I added a method called: hs.global() that will import all the functions to the global namespace. The magic currying function is available as hs.curry.

Operators

Haskell's operators are regular functions. I did imitate this adding most operators as methods. The only problem is that to reference them, you need to use something like hs['+'] instead of hs.+ as one would like.

To defeat this, I made hs a function, it provides a small shortcut for these cases. As an example: hs['+'](1,2) == hs('+',1,2).

I think the second option does look better. These functions can also be curried. hs._ will work the same as hs, but inverting the (2) arguments.

I added the dot operator for function composition, it's quite a powerful function.

 

That's it, the script is licensed under BSD, check the demo and play with it!

Links

Downloads

Monday, November 3, 2008

Fast Trim Function for Javascript

Introduction

Following Steven Levithan's old post about string trimming.

I decided to give it another try after my first proposal.

I made a trim version, based on my aforementioned first attempt. It seems to be much faster on any browser and with different (string) lengths and whitespaces.


First of all...

I want to make clear that this version doesn't trim the exact same characters that other versions do.

In my first version, I simply tried to improve Steven's version without really giving it a deep thought.

This time I thought:Are all those characters really relevant?.

After thinking about this for a while I decided that, whatever /\s/ matches, should be trimmed.

Why ? because most major js libraries use the regex version. If their users are content, then those are the needed characters. Also, if my function would actually do better than others, it could go directly to jQuery's core (there's a ticket for that).

So I created a test page. The results weren't equal for all browsers, IE and Safari 3 yielded far less charCodes. I decided these are the ones I want to trim.


My Trim function

So, the basic modification I made to my previous version, was to check whether the charCode is lower than 33 instead of the whole map/object.

This one takes much less code and is quite faster, I humbly named it "myBestTrim", here it is:

// Licensed under BSD
function myBestTrim( str ){
 var start = -1,
  end = str.length;
 while( str.charCodeAt(--end) < 33 );
 while( str.charCodeAt(++start) < 33 );
 return str.slice( start, end + 1 );
};

The Benchmark

If you want to try the benchmark, get in here. The number should you care about (in my opinion) is the minimum. That's probably the one that ran a lower CPU level.

That test uses a string with 10K characters. Sounds like a lot but this blog's homepage has 55K of html. As oposed to a regex-based trim, this one should scale pretty well, because it doesn't need to check all the string.

That's actually why I removed any regex-based approach from this test, because they'd take too long.

If you have Firebug, I'd advice you to turn it off before getting into this test.

I made a similar test with small strings. It uses a string of 30 characters with 3 whitespaces on each side. In this case, the difference was smaller but still noticeable.

The only situation where another function was faster was on IE, with a small string WITH whitespaces. In this situation, jQuery's trim (the typical regex-based trim) was slightly faster, my trim got 2nd place.

Most situations don't really require much trimming and critical situations have very large strings, so I think this function scales well on those 2.


Conclusion

If all goes well, I'll put this function into jQuery's core. I'd like though, to get some results from other users, to verify my trim is really fast and effective.

Here's the ticket requesting a faster trim for jQuery: #2279.

11/6/08
Removed a dispensable check, thanks Andrea Giammarchi.

Thursday, September 11, 2008

jQuery.ScrollTo 1.4 released!

Fixes

  • Fixed the problem when scrolling the window to absolute positioned elements on Safari.
  • Fixed the problem on Opera 9.5 when scrolling the window. That it always scrolls to 0.

Features

  • Added the settings object as 2nd argument to the onAfter callback.
  • The 3rd argument of scrollTo can be just a function and it's used as the onAfter.
  • Added full support for iframes (even max scroll calculation).
  • Instead of $.scrollTo, $(window).scrollTo() and $(document).scrollTo() can be used(even for iframes).
  • Added $().scrollable() that returns the real element to scroll, f.e: $(window).scrollable() == [body|html], works for iframes too.

I rewrote the scroll limit calculations part, in order to fix the 2 bugs. If you notice any problem, please report.

This version should be perfectly compatible with the latest versions of SerialScroll and LocalScroll.

I tested this only with jQuery 1.2.6 (from trunk) but it should work well even with pretty old versions of jQuery.

Tested my demos and some iframe experiments on IE 6/7, FF 2/3, Safari 3 and Opera 9.2/5. All worked as expected. Enjoy!

Links

Saturday, July 26, 2008

Moving demos to demos.flesler.com

New host

I just wanted to point out that I'm slowly migrating the demos from freewebs.com to demos.flesler.com.
I bought the domain a week ago or so, but didn't have the time to move them all.
I'm also "PHPizing" them a bit, so it takes some extra time.

Moved Demos

I'll update existing links on the blog and will redirect the demos from freewebs(eventually).
If you want to know which ones are ready, here's a list
jQuery Plugins
Regular Scripts

What else ?

I also plan to have the blog on blog.flesler.com.
I tried for 5 minutes, but something went wrong in the middle. The redirection didn't work, so I reverted. I will try soon, after I do some research.

I'll try to be very careful not to break the demos while I add some more to the mix, if you see a failure please let me know. You can fall back to freewebs for now.
Don't forget to update your bookmarks :D .

Tuesday, June 17, 2008

jsClass - Object Oriented Javascript

Introduction

jsClass is an extensible Javascript Base Class that enables you to do clean Object Oriented Programming (OOP) with Javascript.

Why Object Oriented Programming ?

OOP is one of the multiple approaches you can take to structure your code.
It doesn't fit any situation, it's proven to be an outstanding approach to handle large/complex applications.
It will help you keep your code clear, extensible, decoupled and maintainable.
For further information on OOP, just check some of the links on this article.

Extensions and Design Patterns

jsClass accepts extensions, that is, custom modifications that you can attach to classes.
Some are tightly related to Design Patterns.
These are general reusable solutions to commonly occurring problems.

Each extension is very well documented on the code, specifying the different ways yo use them. Also, check the demo to see each in action.
This is the list of built-in extensions:
  • abstract
    Create abstract classes that cannot be instantiated without extending.
  • extend
    Use inheritance, make child classes extend base classes.
  • getset
    Generate getters and setters for private members of your class.
  • implement
    Create Interfaces that define methods that need to be defined, in order to implement them.
  • members
    Add members to the instances of your class. It's a shortcut for SomeClass.members(...).
  • module
    Save your class into a Module. If required, all the namespace is created before saving the class.
  • properties
    Generate properties to control the access to private members of your class.
  • singleton
    Create Singleton classes that can only be instantiated once.
  • statics
    Add static methods or data to your class. It's a shortcut for SomeClass.statics(...).

How to use ?

The best way is to see it in action. Check the demo (link below) to see different uses of jsClass and its extensions.

Links

Monday, June 9, 2008

jQuery is on fire!

The jQuery team and the UI team have been working very hard lately to bring you these new outstanding releases.

jQuery 1.2.6

This release adds a ton of outstanding changes.
  • Many many incredible speed improvements.
  • A bunch of improvements to existing methods.
  • A complete rewriting of the .attr() method, making it much more versatile and useful.
  • Dimensions, by Brandon Aaron has been included into the core.
  • Me!
Go see the official Blog post to see them all.

jQuery UI 1.5

The excellent jQuery UI Module got a new release.
It brings a ton of features and improvements, as well as a standarized API to manipulate all the widgets.
Go check their official Blog Post to learn all about it!

UI Enchant
UI has a set of +15 effects to be used along with the jQuery animations.

ThemeRoller
This widget, provided by the Filament Group, allows you to fine tune all your UI components and achieve a unique look for your work.
To read more about it, check the ThemeRoller Documentation.

Thursday, May 15, 2008

jsDump - Pretty dump of any Javascript data

Introduction

This script can dump any type of Javascript data (or most), thus, generating a string out of the received information.
It's specially useful when creating some kind of console or logger.
Also, it can be used for the development stage, when you need to inspect things on browsers that lack a good console.

How to use

Very simple:
var dumped = jsDump.parse( ... );
Instead of '...', you need to put the data that you want to dump.
That could be an array, a date, the document, an array of arrays, etc.
The variable 'dumped' will now contain a string with the dumped version of your data.

Settings

These are the things you can configure:
  • HTML
    (boolean) If true, the data will be escaped for html output, the default setting is false.
    Note that the script won't escape all the text, only the parts added by the script (indentation, new lines, dumped nodes), it won't alter original strings, etc.
  • indentChar
    (string) This is the unit of indentation, by default it is 4 whitespaces.
    Keep this agnostic from the HTML mode, it will escape spaces and tabs by itself.
  • multiline
    (boolean) Whether to generate multiline code. The default is true. This is specially relevant when dumping collections.
  • DOMAttrs
    (Object) This contains a map of attributes, that need to be dumped from HTML nodes.
    The keys must be the names to be showed. The values, the real name of the attribute contained in the node.
    By default, 'id', 'name' and 'class' are dumped (only if not empty).
To modify these settings, just do (f.e):
jsDump.HTML = true;
This needs to be done before calling .parse().

More methods

jsDump also contains a method, jsDump.typeOf, that is used internally to sniff the type of what you send.
You can use this function for your own code, it'll make an advanced typeof.
To modify the way a certain kind of data is dumped, you must set that using the method 'setParser'.
The 'parser' can be a string, or a function. Use a string if the output is always the same, otherwise, use a function. It will receive the object as argument and must return the parsed string.

You use it like this:(f.e)
jsDump.setParser( 'number', '[number]' );
jsDump.setParser( 'date', function( date ){
return date.toUTCString();
});
To see all the data types, check the source, no gain on listing them all here.
Lastly, you can create your custom types, by just adding them to the list, then, inside a parser function, you can call the method .parse() and pass, as second argument, the custom data type.
This will force the script to use that type, instead of sniffing it from the data.

Links

Downloads

Wednesday, May 7, 2008

Textnode translator for Javascript

Introduction

This is a generic JS Class, that allows you to translate(parse) the content of text nodes, and replace them for the new content.
You only need to specify the parsing function, and the starting (root) node.
All the text nodes inside it, will get parsed.

Modes

The class supports synchronous and asynchronous parsing.
The parsing function will receive the original text as first argument.
If you can parse the text right away, then just return it from the parsing function.
If it needs to be delayed (f.e: AJAX request), then the set the attribute 'sync' of the instance, to false. By doing this, you will get a second argument in the parsing function, which will be a function that you need to call, passing it the parsed text.

Returned data

The parsed data you return can be:
  • a string
    This is the standard, a string replacing the old one.
  • nothing
    new If you don't return data, or the same string, it will just get skipped.
  • a node
    new You can return an html node.
  • an array
    new You can return an array with nodes and/or strings.

Filteringnew

Optionally, you can pass the translator, a filtering function. This way you can exclude elements (and their descendants) from the parsing.
To use it, pass the function as second argument to the constructor.
It will receive the (element) node, and it must return true or false.

How to use

Check the demo to see both modes and filtering in action.
You need to call the method 'traverse' of the instance, passing it the root node.
Remember to call the method after the html document is parsed, so you can access all the nodes in it.

Links

Downloads

Update 5/26/08
Added 1.0.1, updated the docs and demo.

Monday, April 28, 2008

Doctorate on jQuery.SerialScroll

Introduction

After replying to a large amount of comments and emails about jQuery.SerialScroll, I decided to comment some more about this plugin, also to publish some snippets, to achieve commonly desired effects or behaviors.
This should save you (and me :)) some time. It might also show you some additions, that you haven't considered.
I'll also try to show some more model calls to the plugin, so you can unattach yourself from those in the demo.

A little bit of theory

Before the snippets, I'll go through the basics, to refresh your mind.
Calls to the plugin
It can be done on two different kind of elements
  • Element to be scrolled: This is what you normally do when you want one single instance of SerialScroll in a page.
    $('#pane').serialScroll({
    //...
    });
    
  • Container: You might want to create many "SerialScrolls" in the page, you don't need to call it many times, just put the option 'target' to work.
    $('div.container').serialScroll({
    //...
    target:'div.pane',
    //...
    });
    
    This will allow you to have many divs with class container, which contain a scrollable div with class pane. They don't need to be divs.
    When doing this, the selectos for the arrows also become relative to the container, instead of absolute.
  • Global call: If by chance, you want to scroll the window, you'll need to use this approach.
    $.serialScroll({
    //...
    });
    
    If, for some reason, you need to retrieve this implicit element, call:
    $.scrollTo.window();
    This will return you the element, don't use window or document.
onBefore
This setting, which is a callback, will empower some snippets, so you better learn about it.
$('div.container').serialScroll({
//...
onBefore:function( e, elem, $pane, $items, pos ){
//...
},
//...
});
Note that:
  • The 'this' is the element that triggered the event.
  • e is the event object.
  • elem is the element we'll be scrolling to.
  • $pane is the element being scrolled.
  • $items is the items collection at this moment.
  • pos is the position of elem in the collection.
  • if it returns false, the event will be ignored.
  • Those arguments with $ are jqueryfied.
The onAfter, only receives 'elem' as the first argument, and the 'this' is the element being scrolled ($pane but not jqueryfied).

The snippets

Now, what you really want, the code required to do all those fancy things, that the plugin doesn't do natively.
One note, all the snippets are wrapped with a document ready and they use symbolic ids. Needless to say, you don't need to copy the ids, or even USE ids.
Only the relevant settings are specified, so you will see "..." reminding that it's incomplete code.
You configure the selectors and the rest of the settings, according to your needs.

  • Use constant auto-scrolling. getnew
  • Hide the arrows when the limits are met. get
  • Stop the auto scrolling on hover. get
  • Scroll from right to left (or bottom to top). get
  • Manipulate the widget using the keyboard. get
  • Generate a pager based on the items. get

Concluding

I hope this helped and saved you some time, I also hope you learnt something new.
I plan to add more snippets as they come up from requests.
If you have any doubt, don't hesitate to ask.

Update 6/25/08
Added the constant scrolling snippet

Thursday, April 17, 2008

jQuery.Modularize

Introduction

This small plugin(673 bytes!) enables you to have modular methods on jQuery.
It allows the developer to apply the module pattern, to the functions of jQuery.fn.
They can be used as namespace/module for more functions.
This helps keeping the rule of only claiming one name from the jQuery namespace.

Example

It takes a module declared like this:
$.fn.foo = function(){ ... };

$.fn.foo.bar = function(){ ... };

$.fn.foo.baz = function(){ ... };
And enables you to use it like this:
$(...).foo().bar( ... );

$(...).foo( .... );

$(...).foo().baz( ... ).foo().bar( ... ).foo( ... );
As showed above, the method that acts as module, can also be used as function.
To avoid using it as module, the function must receive arguments.

How to use

Basic call
$.modularize( 'foo' );
If $.fn.foo already exists, it will be used when $(...).foo(...) is called with arguments.
If $(...).foo(...) won't be used as method, then you don't need to declare it, just call the plugin before adding the sub-functions.

Providing a default method
$.modularize( 'foo', function(){ ... } );
The given function will be used when $(...).foo(...) is called with arguments.
This is a shortcut for declaring it, and then calling the plugin.

Nested calls
$.modularize( 'baz', null, $.fn.foo );
This allows:
$(...).foo().baz().foobar( ... )
Instead of null, you could send the default method.
Before doing this call, make sure you created $.fn.foo.

Miscellaneous
The 'this' (scope) of the methods, will always be pointing to the calling jQuery object, $(...).
The methods will be gathered on the first call to the function.
Thus, you can "modularize" before adding the sub-methods to the module, or after, it doesn't matter.

If you need to call the module and then add methods, or you just want the methods to be gathered each time, set
$.fn.foo.lazy = true;
You can set this to true, call the methods, and then reset to false.

Links

Downloads

Monday, April 7, 2008

Benchmarking Javascript variables and function scopes

Introduction

The other day, I was doing a quick templating plugin for jQuery. It would split long strings into arrays of tokens, and then parse these, based on a map(associative array) of handlers for the different "commands" in the tokens.

The Background

The thing is, that the tokenization was done on the first call, and only the actual parsing was executed each time the template was applied.
Surprisingly, the parsing function of the plugin was always taking around 2.5 seconds!
This was a recursive function, called around 10 times. It had a short loop of 3-4 iterations per call.
This shouldn't be SUCH a heavy task, certainly not enough to take that long.
I was really confused so I started experimenting.

The Situation

There were 3 types of tokens in the template:
  • Tokens that open/start a block, f.e: <?if $foo?>.
  • Tokens that close/end a block, f.e: </if?>.
  • Tokens that are their own block, f.e: <?elseif $bar/>.
I called this, the mode of the token. So I had 3 symbolic constants declared. All the code was wrapped with a self-executing function, to create a local context. It looked like this:
(function( $ ){
 var
     OPEN = 1,
     CLOSE = 2,
     CLOSED = 3;
 
 /* the rest of the code */
 
})( jQuery );
These constants, were compared to a 'mode' attribute of the token. This caused 1 reference to each of them, on every loop in the parse function.

A Small Change

I tried tried any possible optimization, but Firebug's stopwatch was stuck at 2500ms.
I was nearly desperate, so I started changing things at random.
One of those changes, was modifying the way I was saving the mode of a token. Instead of storing the integer in a 'mode' attribute of the token, I used 'open','close' and 'closed' as boolean attributes.

There's no way to describe my happiness, when Firebug's stopwatch showed ~230ms instead of ~2500ms.
I must say I was really surprised and confused, this was such a small change, and one that I'd never had guessed.
After this, I decided to clear my doubts with a consistent benchmark.

The Benchmark

The first thing I did, was to make my "own Firebug". I created a script with a stopwatch, a logging method and an easy way to benchmark many sequential calls to a function, with many attempts, generating an average and finding the lowest.
After that, I set up the environment. Declared a global variable, and a function similar to the one showed above. Then nested another equal function inside it, and nested another one inside the latter.
In the end, it looked like this:
var global = 1;

(function(){
   var local1 = 1;

   (function(){
      var local2 = 1;  

      (function(){
         var local3 = 1;

         /* here I executed the benchmark */  
  
      })();  
   })(); 
})();
This created 4 variables, 1 global, and 3 local. All of them could be accessed from where the benchmark was being ran, but they belonged to different scope chains. I benchmarked 4 functions, each of them accessed one of these variables 400,000 times with a loop.
Each benchmark was tested 5 times, in order to find an average and the minimum. The latter should be the one that really matters. I tested on all 4 major browsers, on my Windows XP.

The Results

The numbers were surprising, but not in all browsers. Opera 9 seemed to be really indifferent to the scope chain, all took the same time. Firefox 2 was really the slowest. Safari 3 and IE6 were in the middle.
These are the numbers, I'll only mention the lowest from the 5 attempts.
Firefox 2
* global: 969ms * local1: 672ms * local2: 359ms * local3: 78ms
IE 6(=7)
* global: 187ms * local1: 171ms * local2: 125ms * local3: 94ms
Safari 3
* global: 125ms * local1: 93ms * local2: 78ms * local3: 62ms
Firefox 3 beta 5
* global: 41ms * local1: 48ms * local2: 48ms * local3: 37ms
Opera 9
* global: 31ms * local1: 31ms * local2: 31ms * local3: 31ms
Opera is the clear champion, for some reason, the scope doesn't affect the timing at all.
It does affect the rest of the browsers. It will be specially noticeable in Firefox. Note that Firebug was turned off while benchmarking Firefox, it did affect the perfomance but not THAT much.
UPDATE:Added IE7 and Firefox 3 beta, congratz Mozilla, much better!

The Conclusions

Referencing might take time.
Using variables that were declared on different scope chains, takes longer than those that are local to the executing function.
If you are going to use one very often inside a deeply nested function, you should save it in a local variable first.
One common situation is when using the global variables window and document.
It does matter, but it's not always crucial.
Although it does make a difference, you shouldn't become paranoid about this.
These numbers were produced by 400,000 iterations. You probably don't access a variable that many times. Optimizing won't yield a noticeable improvement most of the time.
There are some situations where you should be careful though
  • Within loops, if you have a long loop, caching the variable could improve the perfomance greatly.
  • Inside frequently called functions. This is similar to loops.
  • Inside functions, that are deeply nested inside others. If these reference a distant variable, many times, it could hit on perfomance too.
Benchmarking is fun!
I always love running benchmarks, feel free to try this yourself. You'll find the link below.
The tool used for the benchmark (benchmark.js) can be taken for your own experiments.
If you try other browsers/OS, feel free to post the results, I'll include them so others can see.

I tried to find links about this, but none showed up. I'll update if I get to find any. Same for you, reader. If you can find articles about this, I'll add the links to this post.

Thanks for reading.

Links

Downloads

Saturday, April 5, 2008

jQuery.FastTrigger

What is it

This small plugin includes a clean and simplified version of jQuery.trigger, that will perform better and will specially scale better.

How is this achieved

Many useless steps are skipped when programatically triggering an event, this step that jQuery does no matter what, are not really necessary for triggered events, and some of them hit on perfomance badly. Most of them are not optimized for speed and scalability, but for reusability and code size.

Differences with trigger

This method supports array of data (can include an event object), namespaced events and exclusive triggering. The only known difference with jQuery.trigger is that this method doesn't trigger native handlers, so it's actually comparable to .triggerHandler().

Perfomance

This is method is at least 4 times faster than the regular trigger, and will scale much better as you call it more elements at once.

Global triggering

The equivalent for jQuery.event.trigger, is $.fastTrigger it accepts the same arguments as its prototype's counter-part, but triggers the event on all the elements in the page. This is a dangerous method to use, as it may hang the browser if the page has many elements, using $.fastTrigger won't solve the problem, but will surely perform better. This case should show off the previously mentioned (improved) scalability.

A few examples

$('ul li').fastTrigger('mouseover');//regular
$('h1').fastTrigger('click.foo');//with namespace
$('li p').fastTrigger('focus!');//exclusive
$('ul.list').fastTrigger('collapse');//custom event
$('#bar').fastTrigger('click', ['foo']);//data array
$.fastTrigger('keypress');//global triggering
$('a').fastTrigger('blur',[obj]]);//your event object

Links

Downloads

Saturday, March 29, 2008

String Tokenizer for Javascript

Introduction

This small class can easily parse a string, and generate different kind of tokens. It's very simple and straight-forward. It can perform as a base for other string parsing scripts, like templating engines, custom language interpreters, and many more.

jQuery plugin vs standalone

When called, the script will generate the class, and if jQuery is detected, it will be saved at $.tokenizer.
Otherwise, the class is saved at (window.)Tokenizer.
Note that this script doesn't need jQuery at all, this option is added to ease on jQuery developers.

How to use

The constructor of the class takes 2 arguments, 1 is optional.
  • tokenizers
    This is a collection of strings/regexes that match the tokens.
    The Regexes don't need to include back-references, they can though, but the whole match will be considered a token.
    If you use regex, it's important that you DON'T make it global.
    You can send an array of tokenizers, or just one.
  • build
    This is a parsing function, it will get called for each token found, and also for the string between tokens. It should return the parsed token, note this doesn't need to be a string, the returned token can be an array, an object, etc.
    If no function is given, the tokens are the matched strings.
    The function receives 3 arguments:
    1. The string token that was matched.
    2. Whether it is a matched token, or the string between 2 tokens (true means real token, false, plain string).
    3. The tokenizer that matched this string, or the one that skipped over this slice in the case of plain strings.
As mentioned, build won't just get called for each token found, but also for the strings between tokens. Use the second argument to know which one it is. After you create the tokenizer, you call the method .parse() passing the string, and it will return the array of tokens. You might want to actually do what you need, inside the build method, and just ignore the returned array.

Examples

Templating

var values = { name:'Joe', age:32, surname:'Smith' };
var tokenizer = new Tokenizer([
    /<%(\w+)%>/, /\$(\w+)/
 ],function( src, real, re ){
    return real ? src.replace(re,function(all,name){
       return values[name];
    }) : src;
  }
);
var tpl = '<%name%> $surname is $age years old.';
var tokens = tokenizer.parse(tpl);
document.body.innerHTML = tokens.join('');
CSV parser

var rows = [ ], row = rows[0] = [ ]; 
var csv = new Tokenizer( [',',';'],
  function( text, isSeparator ){
     if( isSeparator ){
         if( text == ';' ){//new row
             row = [ ];
             rows.push(row);
         }
     }else{   
         row.push(text);
     } 
  }
);
csv.parse('Joe,Smith,32;Jane,Doe,26;Mike,Bowel,54');


Downloads