I Promise this was a bad idea

Promises are being added to everything. Browser API’s, the latest cool frameworks and libraries, browsers, and soon, maybe even node core.

In theory, I’m all for it. In theory, if we had a clean, simple way to describe asynchronous interactions, it’d be a great idea. In reality, Promises fall short of the mark, and their relationship with async-await is one for concern.

In theory…

The best parts of JavaScript have been the result of goat tracks, a path beaten by thorough usage. Promises started getting used in some form of anger in libraries like jQuery, and in the cases for which they were being used, they pretty much did everything developers needed. Want to load a file and then parse it? Easy. Want to load a file, then load a few files based on the first? No problem. Throw an error? Cool, just show an alert(). For trivial use cases, Promises look great.

But there are downsides that make promises not very pleasant to use.

Bother.

They catch thrown errors and handle them the same way as rejections. This is a massive pain if you want your server to crash when you’ve made a coding mistake, as you have to try and detect ‘good’ errors, like 404s to external services, or DB lookup misses, from ‘bad’ errors like “TypeError: 555t5tt55555555 is not a function” – something that occurs frequently when you own cats. There is discussion around modifying how try-catch works in JavaScript to work around this issue, but that should be a massive red-flag in its own right.

Promises also don’t interop with existing API’s nicely, you have to write boilerplate to interact with the normal ‘error, result’ pattern.

Promises run eagerly. By its self this isn’t an issue, there are cases where this is good, but often, lazily requesting asynchronous dependencies is preferable. Creating a lazy dependency graph also allows for easier debugging and description.

What then?

There is nothing special about Promises, they were just big first. There are many alternatives that do a better job. righto and pull-streams do everything Promises do, with fewer pitfalls. Righto interacts with err-backs, and promises, with no boilerplate, won’t catch thrown errors, and can even print a trace that shows the dependency graph for a flow, and which exact task rejected.

debug

But Mah Async-Await!

The biggest issue with not using Promises that get’s raised is that they are functionally required for async-await, but they aren’t. There is nothing specific about promises that makes them necessary for async-await to work; any async task wrapper could be used just as well. Async-await supporting Promises exclusively is a conflation of concerns.

If async-await was more generic, it would be possible to use it with a wide array of third party libraries that expose the right API. pull-streams, righto my-cool-lib could ALL be used with async-await.

GOAT TRACKS

This restriction is a major concern for me, as it lays down an iron path that restricts the formation of goat tracks. It enforces the usage of Promises, even though there may be better options out there. It restricts experimentation with alternatives.

I’m personally not a fan of the async-await pattern, as it attempts to hide asynchronicity with synchronous looking code, deterring understanding. If it is going to be used widely, it should be at least given the opportunity to be used in a variety of ways, to see what works best.

I’m anxious of the day we all look back and think “Oh no, that was a huge mistake.”

– Contains opinions.

Advertisement

The frustrations of opensource

I spend a lot of time “re-inventing the wheel”, usually due to being blocked or frustrated by the wheel I’m trying to use.

Frustrations and roadblocks that occur when dealing with opensource software can often get in the way of writing software, and when that’s your job, you can’t just wait a few months for an issue to be resolved.

A great example of how something minor can become a major frustration has been happening for a while now. clone, a nice little lib that lets you pass pretty much anything in, and get a cloned version of it out. I use it in many of my modules, but it has an issue:

https://github.com/pvorb/node-clone/pull/46

clone checks if the input is a Buffer, and if it is, it will clone the buffer. This is required functionality of the module. The issue is, even if you never mention Buffer anywhere else in any of your code, or your dependencies, the fact that clone *can* deal with Buffer, means browserify *will* include it.

Programmatically, this  is fine, but the browserify Buffer implementation is reasonably large (40kB!), and adds a significant weight to any browserify bundle that contains it.

I realised that trying to obscure Buffer from browserify wasn’t going to cut it, and decided that a better solution would be to solve this in browserify.

The actual problem

The root of the issue is that browserify should NOT add Buffer on clone’s behalf. Clone does NOT need Buffer unless some OTHER module is using it.

I opened an issue in browserify explaining the issue, and suggesting a flag be added to solve the issue:

https://github.com/substack/node-browserify/issues/1228

The solution proposed involved adding a field to the “browserify” object in a modules package.json, that would tell browserify what builtins this module uses, but should not be bundled because of it.

The discussion finally petered out with basically “That sounds hard”. I had a dig through browserify, looking to add this myself. I was able to figure  out that it was actaully going to be insert-module-globals that needed some work, but it didn’t have access to the package.json, so the option would need to be passed, and the code was so unreadable that I eventually just gave up.

The TL;DR:

some modules, like clone need Buffer to create Buffers, but only if another module passes it in.

  • if no other module in the bundle references Buffer, clone does not need it, so it should be omitted.
  • if another module in the bundle references Buffer, clone may need it, and it will be available, because the other module will have caused it to be added.
  • Browserify needs a way to be told when a module shouldn’t cause a builtin to be bundled, just like peerDependencies.

 

Make stylus more nodey

Today I wanted to break some stylus files out into their own module, and I wanted to be able to require a stylus module in the same way I would require a node module.

I tryed:

npm i my-styles-module

And then:

@require my my-styles-module

Which of course didn’t work, but there is a way.

As explained here, you can tell stylus other directories to look in for imports and requires, if it can’t find it in its working directory.

I’m using gulp to build my stylus, so all that was required was to add:

...
.pipe(stylus({
include: ['../node_modules']
})
...

And now stylus works just like node.

How to check if something is an array.

Every once in a while, I see someone talking about how to check if a thing is an array. I’ve been through a few stages of how to write an array detection utility myself.

Stage 1; “instanceof is the best!”

I’ll just check if object instanceof Array! Woohoo!

And then I came across this: http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/

Huh, arrays from other frames are not instances of the Array constructor in this frame. I guess that makes sense, since it isn’t an instance of this frames Array.

Stage 2; “instanceof is the worst!”

Righto, guess I’ll use the solution provided.

One problem:

The suggested solution relies on checking a string that gets spit out from Object.prototype.toString.call().

That’s great, except for this:

Object.prototype.toString.call(Array.prototype);
// -> [object Array]

Cool so now your array detection function will detect Array.prototype as an array, which it isn’t.

http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4

Shudup spec, Array.prototype isn’t an array, that would be fucking stupid. Anyway Array.prototype.instanceof Array returns false, as you would expect, because it literally isn’t an instance of Array. If you make your own constructor, you wouldn’t expect it’s prototype to be an instance of it, would you?

Really, if you’re checking that something is a thing based on some random string you pulled from a function on a different constructor… you’ve gotta expect problems.

Stage 3; “Array-detecting libraries are stupid”.

There is a huge amount of noise on the internet around this topic, and it is all noise. You will never make a perfect one-size-fits-all .isArray, Even Array.isArray() won’t work for array-like objects that people intend to be used in place of arrays. In the end, to quote greenday…

It doesn’t even matter.

Think about what you are actually trying to check for. If you have an API that takes an array, just use instanceof Array. Who cares if it doesn’t work across frames? Thats a rare case, and if you need to talk across frames, you will probably know that you’re doing it; Convert the array to a local instance before you give it to your API. Sure it won’t work with that Hipster new library that gives you SuperArrays, but that isn’t important, because the new hotness will come and go. Your library should support JavaScript.

Don’t be afraid to use instanceof, for arrays or any other object. It is an extremely robust way of ensuring an object will look and behave how you expect. It isn’t perfect, but it’s how JavaScript was meant to be used.

The takeaway:

  • instanceof Array is NOT harmful.
  • array detection libraries are stupid, think about what you actually need to check, and check for that.

Samsung Series 9 Touchpad Fixes

Simply put, Samsung software is shit. The touch pad hardware is excellent, and even the drivers for it are great, but the software that ships to configure it is extremely limited.

Have you ever wished you could do crazy things like 3-finger-touch right click? Or maybe use the touch-pad while pressing keys? Probably not right? That all sounds WAYYY too hard and crazy for a user, or at least that’s what Samsung thinks.

Luckily you can change pretty much everything, the Elan driver is really feature-rich and configurable, if you know what to do.

So, to convert your touch-pad from usable to awesome:

1. Open regedit

  • windows+r
  • type ‘regedit’, press enter, 
  • Accept the prompt

2. Go to the honey pot of touchpad settings

  • HKEY_CURRENT_USER
  • Software
  • Elantech
  • Smartpad

3. CHANGE ALL THE THINGS!

Want three finger right-click?

  • change Tap_Three_Finger to 1

Want two finger middle-click?

  • change Tap_Two_Finger to 2

(You can obviously swap 2 finger and 3 finger by swapping their values if you’re weird)

Want to be able to use the touch-pad while typing?

  • Change DisableWhenType_Enable to 0

Just got windows 8.1 and lost the ability to have normal scrolling?

  • Change SC_Reverse_Enable to 0

As you can see there’s a whole ton of settings that can be tweaked. I’m yet to play with everything I’ll want to tweak.

 

Gedi, an absurdly powerful model API

logo

Firstly, what’s gedi?

git: https://github.com/gaffa-tape/gedi
npm: https://npmjs.org/package/gedi

Gedi is model API wrapper. It Allows you to get and set, and remove to/from an object, and bind to changes that occur due to sets/removes.

Simple usage:

    // Set up the models data
    var model = new Gedi({
        thing: 2,
        lifeforms:{
            dogs:[
                {
                    name: "scruffy",
                    age:8
                },
                {
                    name: "spot",
                    age:4
                }
            ],
            users:[
                {
                    name: "bob",
                    age: 20
                },
                {
                    name: "john",
                    age:45
                }
            ]
        }
    });

    // get
    model.get('[thing]'); // --> 2

    // set
    model.set('[thing]', 5);

    // get after set
    model.get('[thing]'); // --> 5

    // bind to [thing]
    model.bind('[thing]', function(event){
        // this callback is called when [thing] changes.
        // event.getValue() returns the value of [thing]
    });
    

You can throw a parent path on the end of get/set/etc.. to make all bindings relative to it.

    model.get('[dogs]'); // --> undefined

    model.get('[lifeforms/dogs]'); // --> Array of dogs.

    // here, [dogs] will be relative to [lifeforms]
    model.get('[dogs]', '[lifeforms]'); // --> Array of dogs.
    

Why square braces?

Because gedi is more powerfull than just an object watcher.

you can pull data out via expressions…

    model.get('(last [lifeforms/dogs]).name'); // --> "spot"
    

And even bind callbacks to expressions…

    model.bind('(last [lifeforms/dogs]).name', function(event){
        // this callback is called when [lifeforms/dogs] changes.
        // event.getValue() returns the value of (last [lifeforms/dogs]).name
    });
    

However there was one thing I’d always thought would be awesome, and i’ve recently made some pretty big changes to how gedi works under the covers to allow.

    // Set the last item in [lifeforms/dogs] to a different dog
    model.set('(last [lifeforms/dogs])', {name: "ellie", age: 2});
    

Gedi can now keep track of paths throughout the execution of an expression, and perform useful opperations using the resulting path.

This is pretty exciting to me, expecially for how it can be used in gaffa.

You can also perform opperations on the result of a filter/map/slice/concat/etc.. eg.

(fyi, the syntax for an anonymous function in gedi is {param1 param2 paramN functionBody})

remove all items in an array that match an expression.

    model.remove('(filter [dogs] {dog (> dog.age 7)})', '[lifeforms]');
    

set properties on all items across multiple arrays that match an expression. (TO RIDICULOUS, AND BEYOND!)

    model.set('(map (filter (concat [dogs] [users]) {item (> item.age 5)}) {item item.label})', 'old!', '[lifeforms]');
    

And of course, any changes made using expressions cause model events to fire at the right paths.

How to do cross-domain async iFrame POST’s the easy/robust way

iFrames, Everyone loves them.

Haha! OK seriously now.

Every seasoned web developer has come up against the need to POST data asynchronously  to a different domain that does not accept cross-domain headers, and the usual solution is an iFrame to post into.

The code is usually something like this:

var myForm = crel('form', {'class':'braintreeForm', method : 'post', target:'myDodgyIframe'});
var myFrame = crel('iframe', {name:'myDodgyIframe'});

And then some code to make this disgusting pile of crap work:

var firstLoad;

myFrame.addEventListener('load', function(event){
    // the load event that gets fired on insertion
    if(!firstLoad){
        firstLoad = true;
        return;
    }

    var result;

    // the actual load event, probably.
    try{
        result = JSON.parse(event.target.contentDocument.documentElement.textContent);
    }catch(jsonError){
        //handle the error somehow
    }

    // use result if it parsed well.
});

An issue that you may encounter is that, if you are inserting the iFrame into the DOM arbitrarily, and not necessarily submitting it, the load event triggers every time it is inserted, not just once.

A solution to this came to me today:

myFrame.addEventListener('load', function(event){
    if(event.target.contentWindow.location.toString() === 'about:blank'){
        // ignorable load, return.
        return;
    }

    // useful load, use.
    var result;

    // the actual load event, probably.
    try{
        result = JSON.parse(event.target.contentDocument.documentElement.textContent);
    }catch(jsonError){
        //handle the error somehow
    }

    // use result if it parsed well.
    
    //cleanup
    event.target.contentWindow.location = 'about:blank';
});

This ignores any useless load events the frame might fire, without dodgy statefull ignore-the-first-load code.

The Blackberry Z10, So much right, so much wrong…

I’ve been using a blackberry Z10 as my main phone for about 2 weeks now, having used the Dev Alpha B for a few months before that, and I’ve pretty much made up my mind about it. My previous phone was a Galaxy Nexus, so that’s what I’ll be comparing it to mostly.

Hardware:

The good:

  • The button-less face is excellent. I’ve already forgotten how to unlock other phones.
  • Nice high res screen.
  • Decent performance (only ever really noticeable when playing 3D games, which run well).
  • Decent battery life, I get a day’s heavy use out of it without issue.
  • The back cover is nice and grippy, you can stick it on a dash and it won’t slide around (if you drive carefully).
  • The camera is excellent.

The bad:

  • The screen is kinda small.. Not iPhone small, that would be ridiculous, but it’s smaller than my GNex, even though the phone its self isn’t any smaller.
  • Looks like an iPhone. While this will help BB steal iPhone users, it makes me slightly embarrassed to be seen with it. I don’t see how they could not get sued by Apple, it actually is very similar, unlike previous phones that were a ‘copy’. It’s quite boring to look at.
  • The volume buttons feel cheap, and I don’t really know what the middle button is for.
  • The plugs are on the side. This is sometimes a good thing, but mostly it looks weird.
  • It has pulg*s*. It doesn’t need a HDMI out, the phone isn’t being pushed as a media device, so why dedicate an ugly port to it?
  • The ports are positioned weirdly, mostly on the body of the phone, but partly into the back cover.

Software:

the good:

  • The browser. It is the best. Ever. Literally nothing else is as good. Chome for desktop? Excellent, still not as good as the BB browser. I’m not kidding, it is better than Chrome, for both viewing and developing web applications.
  • Cascades (or whatever) is awesome. Once you swipe things around on this phone, you can’t help but accidentally swipe other phones, usually resulting in Google now. Something came in to the phone? Slide up, hold, slide to the side if you want a preview of the notification.
  • App-specific notifications. OMG they are awesome. Something happened in an app? Red icon. Something happened in an app in a folder? Red icon. Give the engineer who came up with this a raise, I love it.
  • the Hub is great, no matter what happened, it can always be found in the hub.
  • The app switcher is really fluid for switching between 2 apps.
  • The word flicking keyboard is a good idea.

the bad:

  • The apps. There aren’t enough. This should seem like an obvious, unavoidable issue, as with any new phone, but this phone should be an exception. Blackberry does not leverage their browser enough. Why isn’t there a way to turn any website into a chrome-less app?
  • Maps. They suck. Practically unusable. The app lags all the time. Why the hell would they not just use Google maps?
  • The phone has crashed probably 30 times since I’ve had it. At least once a day, but I have a feeling its probably more often than I realise.
  • Apps cannot run in the background. This is a big issue as it stops apps like llama (for android) working, which is a big issue because the phone is never in the right profile.
  • The phone is never in the right profile (like I just said). It is constantly going off when you don’t want it to, and never ringing when you need it to. This is because I don’t manually change the profile, because I am a human and any ‘smart’ phone should do this for me.
  • The keyboard needs the long-press-for-symbols feature that android has. Things like question marks shouldn’t be difficult to input.
  • It has a shit lock-screen. The only option is a password. Patterns people!

Development:

the good:

  • The browser, as mentioned, is amazing. The development experience is not just bearable, but actually BETTER than Chrome for desktop! The profile tab has better info than Chrome. Apps run fast, every feature you could want is available.
  • Native development: who cares, this is 2013.

the bad:

  • Java. Seriously? you need Java to create a web-app for this phone? Why? BB really needs to improve the tooling around turning web-apps into BB apps.

In summary, I really like some of the ideas that went into this phone, but I don’t think I can live with it. The few apps it does have are either crippled by the lack of OS support, or are just not very good. An example: Google talk lets you have only 1 account, and it notifies you of messages, even when you are using another device to converse with your contact. There is no Google latitude.

I realise that some will say “Well just make the missing apps yourself!” which is true, I could, and probably should do this, but I just don’t have the time.

Should you buy it?

If you are a previous BB user you will probably love it.

If you are a previous iPhone user you will probably hate it for the wrong reasons

If you are a previous Android user you will probably love some of the features, then get annoyed with the lack of applications.

Personally, I’m torn, If i go back to android, I will miss some of the amazingly good features like swipe to  unlock, swipe to notifications, and swipe to do pretty much anything. I will miss the excellent browser. That said the maps, lack of apps, Google talk, and frequent crashes are really getting to me..

Basically…. I lied; I’m not sure yet.

A BB/Android blend would be the perfect phone.

Deploying a Web app in 14 days, No HTML.

Recently, at Sidelab, we developed and deployed signforms.com, an application for signing documents online, in 14 days. The application runs on a node.js back-end, using amazon web services. One interesting thing about the application is that it uses effectively no HTML.

There are a few static html files for the lander site, but once logged in, there is only one .html file. Other than script tags to load the application for the very first time, the file looks like this:

</head>
<body>
</body>
</html>

After that, no HTML is ever sent.

Don’t believe me? Sign up (its free currently), log in, then view source. You will see nothing but JSON.

Signforms uses no html

A typical page in SignForms.com

WAT!?

The front end of SignForms is build using an opensource framework called Gaffa. Once the application has been loaded, all navigation is achieved via requests for JSON resources.

Oh, so it’s client-side templating.

No, there are no templates, ever. When we work on SignForms, we never write HTML, or HAML, or any kind of ML. We write JavaScript ‘pages’ that are made up of ‘view items’ such as containers, textboxes, buttons etc. For cache-ability we serialize these pages to JSON for the application to consume.

In your browser, Gaffa takes the serialized page, inflates it into objects, then generates the DOM required to display the page, and adds all other functionality such as event handlers, page load, and autosave behaviours that are required.

Why?

I’m not a fan of HTML, it’s not something humans should write. One thing that people usually seem to forget is that HTML is a sibling to XML. The majority of people would prefer not to write XML, but have no issues writing HTML. This seems strange to me. The other thing people seem to forget is that HTML is a serialization format for DOM, it is verbose and inflexible.

One commonly seen flow of data for a web application is as follows:

Developer:

  • Write ‘Views’ in HTML
  • Add magic strings to tell the server where to put more strings

Server

  • Scan massive string, stringify data and concatenate the extra strings to the original string
  • Send massive string across the wire
  • Don’t cache it because it will change next time.

Client

  • Parse massive string
  • Correct any invalid HTML, take a best guess as to what it was supposed to be
  • Convert strings into DOM
  • Render the DOM

The way Gaffa handles navigation is different:

Developer:

  • Write code
  • File watcher auto-serializes to JSON

Server:

  • Serve JSON Page, cache for next time.
  • Serve DATA.

Client:

  • Parse JSON into ‘viewItems’.
  • Apply Data to ‘viewItems’.
  • render DOM (Or Canvas, or webGL, or google map markers, or any other logical view).

There are a few big advantages:

  • Only one HTML page is ever sent, once, and it’s tiny (2KB uncompressed)
  • Developers write code, not an XML derivative.
  • Application pages are cached indefinitely, and are tiny (17 KB uncompressed and unminified for a feature rich page)
  • The server only serves data, reducing load.
  • Page-weight is microscopic. (<0.5 MB per page for everything, including all images, css, and data)
  • Pages render stupid-fast (~1s for a complicated, data-bound page).

Gaffa achieves this by creating a declarative programming abstraction for the UI. If you want to display a textbox, and bind it’s value to the model, you would create a textbox viewItem:

var myTextbox = new textbox();

myTextbox.value.binding = '[myValue]';

Other views can bind to the same value in the model:

var myLabel = new label();
myLabel.text.binding = '[myValue]';

And the data can be manipulated before displaying it:

myLabel.text.binding = '(join " " "hello person, your value is" [myValue] "which is" [myValue].length "characters long")';

Then you add the viewItems to gaffa:

gaffa.views.add([myTextbox, myLabel]);

And you’re done.

Because viewItems are rendered as DOM, you can use CSS to style them as you normally would, and end up with a fast, feature rich web application in a very short period of time.

As to be expected from my previous post, this application forgoes the usual “Don’t think, include” librarys such as jQuery, bootstrap, etc, and instead uses small components that do exactly the job required.

A few libraries/technologies used:

JS:

CSS:

The DOM isn’t slow, you are.

Edited post-HN: Wow, big response! WARNING: May contain traces of opinion, and naughty words like ‘shit’ and ‘internet explorer’.

This blog post was written in 2013, and many things have progressed. The predictions made have generally come true.

TL;DR:

  • Use jQuery for what it is meant for, not everything.
  • If you do stupid shit with the DOM, it will perform poorly.
  • If your web-app is slow, it’s because of your code, not the DOM.

Whenever the native versus web argument arises, the biggest issue people usually have with web is performance. They quote figures like ‘60fps‘ and ‘scroll speed’, and, back when browsers were just getting good, i would say “OK, there’s work to be done, but they will get there”, which was met with anything from “Yeah maybe” to “No it will never happen!”.

ORLY?

Really? You don’t think it can be as fast? You think that your proficiency as a developer outstrips the devs working under the hood of chrome? firefox? Interne..wait yeah you’re probably better than IE devs… But for decent browsers, it is a ridiculous assumption that your average iOS or Android developer is going to be able to custom implement a function that a Chrome or Firefox developer provides to you via an API. Not to mention the fact that these abstractions are constantly being tweaked and reviewed by thousands of developers  every day.

But really, this point is redundant anyway, because when you get down to it, even C is an abstraction on assembly, and if you are an Android developer, you’re coding against Dalvik… which is just as much of an abstraction as JavaScript is anyway! Abstraction is more likely to increase speed, because someone smarter than you has written the bit that needs to be fast.

But again, this isn’t the topic of the post.

People often throw around the statement “The DOM is slow”. This is a completely false statement. Utterly stupid. Look: http://jsfiddle.net/YNxEK/. Ten THOUSAND divs in about 200 milliseconds. For comparison, there are about 3000 elements on a Facebook wall, and you could, using JavaScript, render the whole thing in about 100 milliseconds. In a recent project, we needed a list of groups of items that would filter based on a users input. There were about 140 items, and it had to be fast on a phone. Currently, the implementation is as follows:

On filter change:

  1. Delete everything.
  2. Render everything from scratch.

And it has absolutely no perceivable lag what so ever on a mobile phone. None. This is pretty much the worst implementation possible, but it was all that was needed because it was easily fast enough. Why then, do people say that the DOM is slow?

I blame jQuery.

These days, when hiring for a front end developer position, there is a very simple way to determine how good a candidate is. “What are your thoughts on jQuery?”. This simple question will cull about 80% of applicants off the bat. Some cull-able answers (which I have actually seen):

  • “I love JavaScript”. – Help them find the door, they will probably struggle on their own.
  • “It’s a really helpful framework” – A victim of group ignorance.
  • “It’s good but not as fast as HTML5” – Haha… get out.

I’m dead serious when I say these are all real answers from real people.

Yes, jQuery is the compatibility tool of choice these days, and for good reason; it solves the tedious problems it aims to solve in a really easy way. The problem is, most, and I do mean, the majority of ‘web developers’ think it is the only way to work with the DOM. It isn’t even that jQuery is slow; for what it does, it is quite fast. The issue is that a huge number of developers use it to do quite literally everything.

Actually, I blame developers…

This whole ‘DOM is slow’ is really just “I’m too stupid to know that what I’m doing is stupid”, and practically no one is immune. I once worked for a large ‘software’ company that came to me one day with a laggy screen in a ‘web app’ and said “fix this”. The bespoke JavaScript for this one screen was around 400 lines long, and had nearly 500 jQuery selections in it. Worse, many selections were being made within a loop, for no apparent reason! I ended up being able to delete about 90% of the selectors, by just assigning the selection to a variable, which resulted in a 1000% performance increase! Whenever you select an element (or elements) with jQuery, you’re instantiating a very feature-rich wrapper.

If all you are doing is making HTML elements, DO NOT USE JQUERY. About the worst possible way to create HTML is like this:

$(‘<div class=”imAMoron”><span>derp</span></span>’);

Not only will this be insanely slow in comparison to using the DOM method (document.createElement), it also leaves your code looking like a steaming pile of shit. If you need to make a lot of DOM in JavaScript, use a tool like laconic, or crel. And I do recommend creating lots of DOM in JavaScript, it is definitely faster than requesting the same structure from a server, then letting the browser parse the HTML to DOM, and then selecting the elements back out of the DOM to manipulate them.

What now?

First: ignore pretty much anything Facebook has to say about DOM performance. They really have no idea what they are talking about. Sencha was able to make a version of the Facebook mobile app with their framework, and it was FASTER than the official, native app, and Sencha isn’t even particularly fast.

Second: Stop using DOM libraries out of habit! If your target browser is IE8 or above, jQuery hardly provides any features that aren’t shipped on the host object anyway. document.querySelectorAll will pretty much do everything you need. jQuery is a tool, not a framework. Use it as such.

Third: Use document fragments. If you are changing the state of the UI in a loop, don’t change it while that DOM is in the document, because every change you make will cause a re-draw or re-flow.

Fourth: USE THE GODDAMN PROFILER! In almost every case, the bottleneck will end up being your code, not the DOM.