Most of us JavaScript developers can’t work without using a JavaScript Library/Framework today. JavaScript is a very nice language, but it requires for you to write a lot of code before you can implement some interesting animation, drag and drop feature, or Ajax request/polling.
This is in part due to the fact that every line of code we write must be browser compatible, and that adds lots of “ifs” to our code.
So I guess it’s understandable to think that choosing a framework that can abstract these kind of things for us is good. By using a framework we can concentrate on other kind of problems, more like high-level-usability-pattern problems.
Some frameworks stick with JavaScript as their main language (like MooTools or JQuery). Other frameworks simply let you type another kind of language that then gets compiled into JavaScript. I guess that having a language with built-in classical inheritance syntax and a nice IDE to support it are good reasons to develop these kind of libraries that translate some code into another.
Frameworks are undeniably good, but most of the things I learn about the JavaScript programming language were learn while hacking some pure JavaScript code.
When hacking pure JavaScript code you find yourself hacking common language idioms that are usually abstracted by frameworks. And it’s nice to understand how these things work. When you get how a specific JavaScript pattern/idiom works you get to understand lots of things about the language itself.
Most of the people don’t do this today. You can see posts about call and apply, closures and private members patterns very often in Reddit and Ajaxian, and each time that post appears lots of other people upmod it. So that means that most of the people today probably use the bind Function method without really knowing what it does.
The worst part is that JavaScript is a very beginner-friendly language, a good start for people not having a computer sicence related background, but at the same time there are lots of things about the language itself that are advanced features (object mutability, booleans as defaults, functions as first class citizen, prototypal inheritance) and most of the users are never aware of these features, most of the time due to the abstraction of the frameworks they’re using.
An Example
This is a very simple example (and interesting interview question also).
If you’re dealing with the dom when hacking JavaScript then you might often use the hasClass and removeClass methods from JQuery, MooTools, or whatever.
So, how would you write them?
function hasClass(domElement, className) {
//code here...
}
function removeClass(domElement, className) {
//code here...
}
Please, if you’re reading this, take five minutes of your time and write these functions out before reading the answers. Believe me, it’s worth the effort. I mean, how much time can it take?
The Answers
click here to show the answers
Conclusion
These methods could have probably been written differently if disk space and performance weren’t such an important issue in JavaScript, but with those constraints even the most trivial methods like hasClass and removeClass can be interesting to read.
So this is my recommendation: try to understand how things you normally use are implemented. There are lots of interesting JavaScript libraries that make excellent code that’s performant and save disk space (:P) so check them out, you might learn about lots of things, even the most simple functions can have interesting concepts.
Hello there, I'm Nicolas Garcia Belmonte, a Computer Science Engineer from the Buenos Aires Institute of Technology, in Argentina. I live in France now.
Nicolás, all good points. The “basic” nature of Javascript is both a blessing and a curse to beginners and experienced practitioners alike. It is perhaps inevitable, certainly expected to see homegrown utility libraries become full-fledged “products”.
With respect to hasClass(), seeing that you’re not loathe to use regular expressions for removeClass(), I’d prefer using a word-boundary matching adverb instead of padding with spaces:
# function hasClass(domElement, className) {
# return !!domElement.className.match(“\\b” + className + “\\b”);
# }
which if not as fast as using indexOf is to me a bit clearer.
Hi Matias,
Thanks for sharing your hasClass implementation
Actually I don’t know which one is faster, I think that in most cases performance will be browser dependent, but since classNames are usually short names the regexp and indexOf methods might be quite similar in performance in “common” cases.
Hi. What about case insensitivity? You examples don’t seem to care about case insensitivity! In CSS class “kiss” is the same as “KISS”.
Not always apparently:
The trick is that if you write a document using an XML declaration and an XHTML doctype, then the CSS class names will be case sensitive for some browsers.
http://webdesign.about.com/od/css/f/blcssfaqcase.htm
Perhaps it would be better to leave the user the freedom make case sensitive searches for classNames.
I’m not sure I’m understanding this right, but it seems to me that you can put escape sequences in CSS class names (http://www.w3.org/TR/CSS2/grammar.html). Those would interfere with the regexp. I think, to be on the safe side, the class name should be escaped before compiling the regexp:
/**
Returns a version of the string that is suitable for insertion in a regular expression as a literal value
@type String
*/
String.prototype.escapeRegexp = function () {
return(
this.replace(/\\/g,’\\\\’).
replace(/([.\$\^\{\[\|\(\)\]\}\*\+\?])/g,’\\$1′)
);
};
(Hey, great blog and great work, thanks for sharing!!!)
Is there a potential problem with your hasClass implementation when the element has no ‘class’ attribute and returns null. You’d have to be testing for a class called ‘null’.
Interesting exercise. Here’s what I have. I started with addClass and removeClass and then extracted hasClass from addClass. Didn’t know about the .className property so used getAttribute/setAttribute…
function hasClass(element, className) {
var v = element.getAttribute('class');
return new RegExp("\\b" + className + "\\b").test(v);
}
function addClass(element, className) {
var v = element.getAttribute('class');
if (v === null) {
element.setAttribute('class', className);
} else {
if (!hasClass(element, className)) {
element.setAttribute('class', v + ' ' + className);
}
}
}
function removeClass(element, className) {
var v = element.getAttribute('class');
if (v === 'test') {
element.removeAttribute('class');
} else {
newVal = v.replace(new RegExp("[ ]\\b" + className + "\\b"), '');
element.setAttribute('class', newVal);
}
}