A short snippet for detecting versions of IE in JavaScript without resorting to user-agent sniffing... (»)
Posted in 'General, JavaScript' by James on July 26th, 2010
A letter to the .NET magazine
Staring keenly at a stack of magazines, I hazard a gaze towards the computer
section and lay my eyes upon the newest cover of my sworn enemy, the .NET
magazine. Its luring front-page convinces me that it deserves my consideration.
I buy it without looking inside. I want to reserve the surprise for later.
Now sitting in the lounge at London Gatwick Airport, I whip out the magazine
and rush to a JavaScript article. I can’t wait. It’s about jQuery and I just know
that they’ve gotten their act together since previous encounters. I’m sure this
article will contain the best in jQuery techniques and JavaScript best practices.
Well, .NET, now I’m sitting here with the unfortunate duty to inform you of my
unsurprising disappointment.
The truth is I expected nothing more from you.
Please turn to page 80 of issue 205 and pay special consideration to the
following reservations of mine:
The author doesn’t mention the possibility nor the applicability of creating a
jQuery plugin instead of the single globally defined function ‘drawerToggler’.
What’s wrong with this? Well, the function happens to contain functionality that
could be abstracted to deal with arbitrary sets of nodes and is therefore suited
to jQuery’s plugin mechanism. I would understand if at least a mention surfaced,
but nothing.
This is certainly not the worst of it though.
The function itself:
function drawerToggler() {
$('#theme-drawer').slideToggle("normal", function(){
if ($('#theme-drawer').is(":visible")) {
$('#wrapper').css("margin-bottom", "150px");
} else {
$('#wrapper').css("margin-bottom", "20px");
}
});
}
Where it’s being called:
$(document).ready(function(){
$('.drawer-toggler').click(function(){
drawerToggler();
});
});
This function is called within the click event handler for any element with a
className containing drawer-toggler. Since it’s getting called on every click,
and the elements selected within the drawerToggler do not change between calls,
it would be logical to select and “cache” the DOM object references beforehand,
and then the function could use these DOM references instead of continually
(and redundantly) re-querying the DOM on every single call!
The function itself contains some redundant code too. The ‘theme-wrapper’ to
which the slideToggle method is applied, is then re-queried within the callback.
Apparently unbeknown to the author, this very element can be accessed via the
‘this’ keyword within the callback function — there is absolutely no need to
re-query the DOM for #theme-wrapper!
This isn’t just a case of my preferences versus the author. There are established
best practices in the jQuery community and as one of the industry’s top
publications you should be providing not only good advice but near enough the
best advice according to current industry practices and standards.
Posted in 'General, My Life' by James on July 13th, 2010
Okay. For once, I’m going to say what I actually think on this site of mine! I’ve done it to a degree before but I got frequently hushed by the few people that see anything but neutrality as a weakness or a threat. I’m only human, and I have some damned opinions. So here they are!
Be prepared for mildly offensive rhetoric and inappropriate usage of the ellipsis and exclamation mark!!!
-
I think this universe is simply awesome. We’re quite tiny when everything is put into perspective. Heck, we’re just one form of life, on one planet, in one star system of hundreds of billions of star systems in this galaxy alone (which itself is accompanied by hundreds of billions of other galaxies), and yet some people still have the arrogance to feel that the human race is at the centre of the whole universe! Our wonderful yet miniscule existence is aptly covered in this beautifully poetic video from the late Carl Sagan.
- I think inline documentation, like that made possible with JSDoc, makes code ugly and unreadable. I want to see the code, and that’s all!
- I love the outdoors. I envy people that have jobs outside. And I don’t mean outside on the street cleaning up human sewage. I mean outside in the real world — the places that we haven’t yet destroyed.
- I think heavy UI abstractions like ExtJS totally take away all of the fun, excitement and challenges that should form a central part in any programmer’s role. To me, it looks like a product built for business people, and business people rarely understand the motives and desires of programmers — they only understand a couple of things: profit margins and economic viability.
- I’ve yet to sit in an office chair that won’t make my back hurt like hell after eight hours.
- I don’t believe in a God. I also don’t believe in fairies or unicorns.
- I get annoyed when people don’t realise the difference between believing that something doesn’t exist and rejecting the belief that something does exist, for it should be apparent that there is a huge difference.
- My favourite comedians of all time are Ricky Gervais, George Carlin, Frankie Boyle and Bill Hicks (watch all the videos!).
- I hate running, but I absolutely love cycling!
- I don’t understand Node.js. I really did try, but it just ended up confusing me. I’m not a Linux wizz so installing it was trouble enough.
- I think modern day advertising and marketing techniques are the bane of western society. Oh, and politicians!
- I didn’t care for the World Cup, and I always find it amusing how people shout at the TV and put flags on their cars when it won’t have the slightest effect on the game itself.
- I’m trying to become a vegetarian. Watch the documentary, Earthlings, and you’ll understand why.
- Pixar made me cry. I don’t know how they did it…
- I’d love to know how to play a musical instrument properly. Preferably the piano.
- I think humans have a responsibility to stop using non-renewable resources, but I don’t think playing the blame-game is helpful. I also don’t think change will happen given the current political and economic powers that be.
- I have a love-hate relationship with PHP. Doing most things is dead simple (which is great), but it’s such an ugly language!
- Even after all this time, jQuery is still my favourite JavaScript library. It’s small, intuitive and doesn’t require me to adopt a new programming paradigm!
- Other movies that have made me weep like a baby include The Stoning of Soraya M, Forrest Gump, The Champ and Pay It Forward.
Okay, that’s enough for today. Comment if you want. If you disagree with me on any of these points, then, GOOD! This world would be such a dull place without such varied opinions and people!
Posted in 'JavaScript' by James on July 7th, 2010
Finding and replacing strings of text in a DOM document is very tricky, especially if you’re looking to do it properly and entirely unobtrusively.
In this context, “unobtrusive” means affecting the page in a minimally invasive manner — minimal DOM destruction, no un-called-for normalizing of text nodes (i.e. joining separate text-nodes together) etc.
Essentially, what I’m talking about is finding a specific piece of text in a page or a piece of text that matches a pattern and then doing something with it — possibly wrapping it in an element or changing it in some other way.
Imagine that we have the following paragraph:
<p>This order's reference number is RF83297.</p>
We want to locate the reference number and wrap it in an anchor that leads to the order’s page (pretend that we’re making a bookmarklet to enhance the admin panel of some online store). Given what we know about the HTML structure this can be quite easy:
(Using jQuery)
jQuery('p').each(function(){
var p = $(this);
p.html(
p.text().replace(
/\bRF\d{5}/g,
'<a href="/order/$&">$&</a>'
)
)
});
The problem with this approach is that it breaks once you add any elements to any of the paragraphs on the page. It sets the content of the the element to the (replaced) text of that very element, by definition destroying any non-text nodes in the process, or, to put it poetically, it flattens the DOM structure of any given paragraph.
Additionally, the above technique does not care to avoid paragraphs that do not contain a match for the pattern — it’ll blindly flatten all elements within the paragraphs…
Posted in 'Cool Stuff, JavaScript' by James on June 1st, 2010
I wanted to give a brief overview of the techniques involved in creating this. It’s slightly pointless but still quite fun and I think there are a few notable aspects of its implementation that are worth writing about.
It is a “Fairy” generator that creates small pulsating elements and moves them around the viewport in random directions, but not too eratically. You can load some fairies on this page by clicking this bookmarklet!
The original problem was that I had been mentally locked-in to a specific type of animation on the client-side and I would sit there thinking, “Okay, how do I animate this element from ‘a’ to ‘b’ while taking random detours and going in random directions?”… I was still in the conventional client-side animation mindset, where you take an element and call an animation function to mutate certain CSS properties over a specified duration. The prospect of doing it via manual calls to setInterval, with delta-x/y values never crossed my mind.
Eventually it became clear that this would be the only way to make the fairies fly as I wanted — i.e. not to simply go from ‘a’ to ‘b’, but to continually fly around in all directions.
I started with an environment singleton instance, named “fairyEnvironment”, containing the various bounds and rules attributed to the current document:
var fairyEnvironment = new function() {
this.bounds = {
left: 0,
top: 0,
right: 0,
bottom: 0
};
this.speedLimit = {
high: 6,
low: 2
};
this.spawn = function(amount, postCreation, config) {
var ret = [], i = -1;
amount = ~~amount
while (++i < amount) {
postCreation &&
postCreation.call(
ret[i] = new fairyEnvironment.Fairy(config)
);
}
return ret;
};
};
The generated fairies would have to stay within a specific area, defined by the bounds, “top”, “left”, “right” and “bottom”. I also specified the upper and lower speed limit and a function to spawn multiple Fairy instances at once. You can see that I’ve referenced fairyEnvironment.Fairy, which is defined as follows:
Posted in 'General, JavaScript' by James on April 29th, 2010
Without UI events we’d be totally lost on the client-side — it is only via User Interface Events that we can know what the user wants to do. User-initiated events such as “click”, “mouseover” and “mousemove” are absolutely essential to upholding a user-centred experience and I want to make it clear that my quarrel is not with these types of events. Any event that gives us information about users’ intentions is a good event.
I feel that there are bad events too though — events that we feel we must utilise just because they exist — and we feel we must build into our APIs just because event-driven design dictates absolute submission to this way of thinking.
DOM mutation events
Every single time I’ve ever considered using a DOM-mutation event I’ve subsequently discovered that the problem can be better solved by re-architecting the codebase. The DOM2 mutation events aren’t well supported in less capable browsers but offer an interesting opportunity — to listen for document mutation such as attribute changes, node insertion and node removal.
This sounds cool and I’m sure your mind was buzzing with ideas when you first heard about these events, but have you actually encountered a situation where utilising these events is the only viable solution? If so, I’d love to hear it.
Posted in 'JavaScript' by James on April 27th, 2010
After attempting multiple JavaScript quizzes I thought it would be fun to have a go creating one for my readers. It doesn’t involve any DOM nonsense or browser quirks — this is just plain ECMAScript (3rd edition). It tests your knowledge of the language and specification.
Within each textbox enter what you think the piece of code above it returns.
- The pieces of code are executed as statements.
- Enter your answer as if you were typing the code literally. So, if the answer is the string "foo", then type "foo" (including the quotes — double or single), and if it’s an array like
[1,2,3] then type it out just like that.
- If you think an error is thrown then enter ERROR in the appropriate textbox.
- When there are multiple statements, type what the last statement returns.
Please access this post directly in order to take the quiz (note: JavaScript must be enabled).
Posted in 'JavaScript' by James on March 31st, 2010
When jQuery fires a callback function, whether it is an event handler, an each iterator, or a filter function, it will normally give you a DOM element as the function’s context, accessible via the this keyword. It’s common practice to subsequently wrap this in jQuery(...) resulting in a newly constructed jQuery instance.
This is no fault of jQuery’s but this practice of redundant “wrapping” generally sucks.
I “tweeted” a while ago, complaining of this:
Constructing a new jq obj on each iteration just to access some text seems wrong. jQuery(‘a’).map(function(){ return $(this).text(); });
Paul Irish suggested that I use jQuery.text instead of jQuery.fn.text, meaning that I wouldn’t have to bother with constructing a new jQuery object every single time my function was called. This was a good suggestion but unfortunately not all of jQuery’s methods have corresponding single-node functions, and it would mean not being able to chain methods.
This is a growing problem, and is only worsened by developers’ insistence on constructing multiple jQuery objects with the same element! -
jQuery('a').click(function(){
if (jQuery(this).attr('href') === 'blah') {
jQuery(this).next('.whatever').slideToggle();
}
jQuery(this).fadeTo('slow', 0.4);
return false;
});
Eew! Not only are there multiple pointless constructors, but Mr. Uber Cool jQuery Developer isn’t accustomed to the DOM, so has absolutely no idea that, in most situations, this.href would suffice for getting the href property value. This kind of misuse has been discussed in step #3 here: http://encosia.com/2010/03/30/5-steps-toward-jquery-mastery/.
The real problem remains that there are three jQuery objects being constructed, — I feel that it is a library’s obligation to protect against misuse like this. Even if you’re only constructing one jQuery object in a callback it’s still somewhat pointless, in that you’re only doing so to call a couple of methods…
Posted in 'JavaScript' by James on March 23rd, 2010
I was recently playing around with labels and blocks (as one does) and found that they could be used as a way to annotate/contain chunks of JavaScript without using ugly comments (or abstraction?). I can see its appeal – it offers a sense of containment that is hard to get with just comments.
I’ll use the example of a really simple “getJSONP” function:
// BEFORE
function getJSONP(url, success) {
var ud = '_' + +new Date,
script = document.createElement('script'),
head = document.head || document.getElementsByTagName('head')[0]
|| document.documentElement;
window[ud] = function(data) {
head.removeChild(script);
delete window[ud];
success && success(data);
};
script.src = url.replace('callback=?', 'callback=' + ud);
head.appendChild(script);
}
// AFTER
function getJSONP(url, success) {
declareVars: {
var ud = '_' + +new Date,
script = document.createElement('script'),
head = document.head || document.getElementsByTagName('head')[0]
|| document.documentElement;
}
defineCallback: {
window[ud] = function(data) {
head.removeChild(script);
delete window[ud];
success && success(data);
};
}
addScriptToDocument: {
script.src = url.replace('callback=?', 'callback=' + ud);
head.appendChild(script);
}
}
You may think that doing this borders on totally pointless and in this instance I’d probably agree, but you must admit it could potentially be useful in other situations (none come to mind though).
We’re normally used to { and } being used to delimit an object literal, but they can also be used to create blocks (or “compound statements”). A compound statement is a statement that contains any number of sub-statements. We use them all the time:
while (true) normalStatement;
while (true) {
iAmInACompoundStatement;
soAmI;
ermICallItABlock;
}
We use them everywhere, if statements, for statements, while statements, switch statements, try/catch statements …