We’ve actually hired a couple of people recently, so I’m not sure if I’ll be able to bust this one out any time soon. Instead, I’ll share it with you.
This is another one that I feel works for a lot of different experience and knowledge levels. A good, junior developer should be able to give me a satisfactory answer and a senior developer could take the same problem, decide to be clever, and give me a great answer.
Let’s take a look at it.
The Markup, a Task and a Twist
I start with the following markup:
<div>div 1</div> <div>div 2</div> <div>div 3</div> <div>div 4</div> <div>div 5</div>
The task is simple. Using plain JavaScript, add a 1px border to each of those divs. The only catch is, the for
loop is forbidden.
Depending on the level of the candidate, there are several answers that will bring a smile to my face.
While Loop
This is a while loop that basically takes the place of a for
loop.
var divs = document.getElementsByTagName("div"), i = 0; while (i < divs.length) { divs[i].style.border= "1px solid blue" i++; }
This is a standard “right” answer for a junior developer. A junior dev who thinks of using a while loop and can actually craft one is not a phony. This is especially true now since so many people just use $(selector).each without even thinking.
This can be improved.
Faster While Loop
This isn’t much different, but it caches the divs.length
to save the lookup each time.
var divs = document.getElementsByTagName("div"), i = 0, test=divs.length; while (i < test) { divs[i].style.border="1px solid blue" i++; }
This would be a great answer for a junior developer and a satisfactory answer for someone more senior.
With a senior developer I would ask for another alternative.
Get Wacky With a Do… While
Here’s one potential alternative. I would be surprised to get this as a primary answer, but as a secondary answer it’d be great.
var divs = document.getElementsByTagName("div"), i = 0, test=divs.length; do { divs[i].style.border= "1px solid purple" i++; } while (i < test)
Silver Medal- a Fast While Loop
This one indicates attention to detail and an eye towards performance.
var divs = document.getElementsByTagName("div"), i = divs.length; while (i--) { divs[i].style.border= "1px solid grey" }
This reverse while loop made rounds a few years ago as the outcome of the incredible survey: What’s the Fastest Way to Code a Loop in JavaScript? Someone who either read that or had the results filter down to them is okay in my book.
The Golden Ticket
If a more senior developer busts out the below solution, they get the Gold Star.
[ ].forEach.call(divs, function(obj){obj.style.border="1px solid red"})
This shows someone who’s poked at the guts of JavaScript in a meaningful way. They also get bonus points for pointing out that forEach
is slower than a for
loop.
Bonus: Ignore Me For Fun and Profit
In addition to any of the above solutions, if a candidate of any level says “but really, just use CSS,” even though the instructions say “use JavaScript,” they get bonus points because, really, why do it like this?
JavaScript Ninja/Pirate/Jedi Section
Feel free to follow Adam‘s lead and propose other, interesting solutions in the comments.
This not good enough for you?
document.getElementsByTagName(‘body’)[0].innerHTML =
document.getElementsByTagName(‘body’)[0].innerHTML.replace(/(<div>/g,'<div style=”border:1px solid red;”>’);
The (sorta recursive) winner: http://jsfiddle.net/au2Qm/3/.
var x = document.getElementsByTagName(‘div’);
var els = Array.prototype.slice.call(x);
function drawBorders(arr){
arr[0].style.border = “1px solid red”;
arr.shift();
if(arr.length >= 1){
drawBorders(arr);
}
}
drawBorders(els);
I love that. Will anyone top it?
Your fastest solution might not actually be the fastest. I actually was doing some perf tuning for underscore.js a couple months ago for use in node and the reverse loop was 25% slower in v8 than the traditional forward loop. I re-ran the benchmark several times and it was consistent, so my hypothesis is that the forward loop tripped some fast path that reverse looping did not. This might have changed in the past couple months, but old benchmark interpreted rules of thumb do not necessarily apply to modern js engines.
https://github.com/grayrest/underscore/wiki/Node-conversion-informal-hand-benchmark-notes
That said, the loop construct you use doesn’t particularly matter in this case since adding a border triggers reflow in every browser (you’re modifying the box dimensions). If you cared about perf you’d copy to a document fragment first. 😉
Interesting result. As I was writing this up, I was curious about how well the results still stood up since the post I referenced was a few years old. That’s a lifetime in the development of JS engines.
I wonder what the result would be across the other engines/browsers these days.
I would go with this one (is it plain javascript?):
var arr = document.getElementsByTagName(‘head’);
if(arr.length == 1) {
var css = document.createElement(‘style’);
css.type = ‘text/css’;
css.textContent = ‘div {border:1px solid red;}’;
arr[0].appendChild(css);
}
Why not use for(a;b;c){…} loop?
The last variant will not work in IE. There aren’t any [].forEach.call in it and the result of is not an Array, but HTMLCollection so it can be accessed by [], but can’t be enumerated.
Hey, this is for an interview, so the candidate only has to make these solutions work in the browser they’re doing the examples in- usually Chrome. If they mentioned that “this won’t work in browsers without a native foreach” I would simply ask them to add it to Array.prototype for extra credit.
Secondly, it WOULD work in IE9.
http://jsfiddle.net/2A25p/
Also, the POINT of it is that HTMLCollections are not Arrays, but that you can borrow an Array method (forEach) to do this task.
I would love to hear a candidate mention the following from the ES5 spec: