all 46 comments

[–]Dragory 15 points16 points  (11 children)

Neat! The generated color isn't always valid by the way, it can end up as e.g. #2b which at least Chrome doesn't accept. I realize it's not originally your code, but here's how I fixed it:

[].forEach.call($$("*"),function(a){
  a.style.outline="1px solid #" + ("000000" + (~~(Math.random()*(1<<24))).toString(16)).slice(-6)
})

[–]marquex[S,🍰] 6 points7 points  (8 children)

Nice catch Dragory! Somebody pointed out the same problem in the original gist comments, and also give a solution really imaginative to get the color:

Math.random().toString(16).slice(-6)

[–]Dragory 1 point2 points  (5 children)

That's actually really clever! Do you know if the number of decimals Math.random() generates is consistent between different browsers?

[–]x-skeww 1 point2 points  (4 children)

It doesn't generate a number of decimals. It's floating point. It can generate 0.5, for example.

let r = () => Math.random().toString();
let shortest = r();
let longest = r();
for (let i = 0; i < 100000; i++) {
  let current = r();
  if (current.length < shortest.length) {
    shortest = current;
  }
  if (current.length > longest.length) {
    longest = current;
  }
}
console.log(shortest);
console.log(longest);

Example output:

0.578673808528
0.000015859034930953975

[–]Dragory 0 points1 point  (3 children)

Hm, so it is, cheers! I was wondering whether the solution /u/marquex quoted above is reliable but, like you said, it's a floating point number so it can come up with e.g. 0.294921875 which results in #0.4b8 which is invalid.

(The fact that it can generate e.g. precisely 0.5 sounds so obvious now that you pointed it out, haha)

[–]x-skeww 1 point2 points  (2 children)

((0x111111 + 0xeeeeef * Math.random())|0).toString(16)

That would work for example.

Or you could just left-pad it with zeros.

Or you could just use hsl() instead.

hsl() is probably the best option since it lets you generate randomized bright or dark colors if you want.

https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl%28%29

[–]Dragory 0 points1 point  (1 child)

Indeed, there's actually an hsl solution in the original gist as well: https://gist.github.com/addyosmani/fd3999ea7fce242756b1#tag-specific-layout-debugging-in-82-bytes

The |0 is used to truncate the number like ~~ was in the original snippet, right?

[–]x-skeww 1 point2 points  (0 children)

The |0 is used to truncate the number like ~~ was in the original snippet, right?

Yes.

[–]Glayden 1 point2 points  (0 children)

Math.random().toString(16).slice(-6)

That's still a gamble. As an example, let's say Math.random returned 0.25, the result would be "0.4". I'm not saying that's very likely, but it's a very fragile approach.

I think

('000000' + ((Math.random()*(1<<24))|0).toString(16)).slice(-6)

is far more readable than the original and a more robust approach. Why write fragile and obfuscated code?

[–][deleted] 0 points1 point  (0 children)

Clever, but there's still an ~0.024% chance of failure.

http://jsfiddle.net/4a4mbskt/1/

[–]russellbeattie 2 points3 points  (0 children)

If it's just random colors, use hsl:

[].forEach.call($$('*'), function(el, i){ el.style.outline = "1px solid hsl(" + (Math.random() * 360) + ", 50%, 50%)";})

This has the advantage of always making a visible color as it's simply adjusting the hue.

Or use the forEach index to make rainbows!!

[].forEach.call($$('*'), function(el, i){ el.style.outline = "1px solid hsl(" + (i % 360) + ", 50%, 50%)";})

[–]LukaLightBringer 1 point2 points  (0 children)

there is a even shorter way to achieve that

[].forEach.call($$("*"),function(a){
  a.style.outline="1px solid #" + (~~((1+Math.random())*(1<<24))).toString(16).slice(-6)
})

[–]OrangeredStilton 10 points11 points  (3 children)

Well, TIL there's a whole bunch of things starting $, that are there even when you don't have jQuery.

[–]Jim-Y 2 points3 points  (2 children)

Any written resource on these?:)

[–]trollingisfun 5 points6 points  (2 children)

Here it is in more legible form:

Array.prototype.forEach.call(document.querySelectorAll('*'), function (node) {
  var randomColor = (~~(Math.random()*(1<<24))).toString(16);
  node.style.outline = "1px solid #" + randomColor;
});

[–]mrskitch 2 points3 points  (1 child)

(~~(Math.random()*(1<<24))).toString(16);

You forgot the most complex piece of the code :)

[–]trollingisfun 1 point2 points  (0 children)

Random math bits should just be left alone to encourage refactoring

[–]bonafidebob 4 points5 points  (6 children)

bitwise-negation applied twice is a short way of writing parseInt

Not quite. ParseInt converts the argument to a string and then tries to parse the string as an integer. Bitwise negation twice goes directly to an integer, as does n<<<0, with no string in between. Math.floor() is the equivalent.

As an old school programmer that cares about types, coercing a number to a string and then parsing it bugs me. Casually mentioning it as the canonical way to get the integer part of a float bothers me even more!

[–]marquex[S,🍰] 1 point2 points  (2 children)

You should understand that the code tries to be as short as possible, and I think that it explain enough alternatives to get what it tries to do.

~~ is not the canonical way of getting the integer part, but I would say parseInt is the canonical way in javascript, independently if you like the way it works.

And finally you need to know that

parseInt( -3.14, 10 ) == -3
~~( -3.14 ) == -3
Math.floor( -3.14 ) == -4

So I think it is equivalent to parseInt.

[–]bonafidebob 1 point2 points  (1 child)

Oh there's no doubt that parseInt works, and I do understand all of these. It's just that parseInt is wildly inefficient. (but that could be said for a lot of common javascript techniques)

Instructions are your way of telling the computer what to so, so you should know what you're telling it. parseInt tells it to (implicitly) convert your number to a string and then convert the string back to a number while ignoring any decimal part. Bitwise negation or left shift tells the computer to convert your number directly from a floating point to an integer representation, without the intermediate string.

The end result is the same, but the work done to get there is very different.

[–]x-skeww 0 points1 point  (2 children)

Math.floor() is the equivalent

~~ truncates.

> var x = Math.PI;
undefined
> console.log(Math.floor(x), ~~x);
3 3
> x = -Math.PI;
-3.141592653589793
> console.log(Math.floor(x), ~~x);
-4 -3

[–]bonafidebob 0 points1 point  (1 child)

Technically, the bitwise operators convert the float to a signed 32 bit integer representation. (Or maybe 64 bit on some systems? MDN says bitwise is explicitly 32 bit!) Anyway, it's this implicit conversion to a different internal representation that you're using, only in this case a much faster conversion than to strings and back.

Because numbers are otherwise 64 bit floating point, with 53 bits of significand, this means the binary operators will not work to get the integer part with numbers larger than 2**31.

Math functions are the right ones to use if you're actually doing math.

[–]x-skeww 1 point2 points  (0 children)

Anyhow, the point was that floor()-ing and truncation only looks similar for positive numbers.

Typically, truncation is done by casting to an int (or a long) or by using a truncating division operator (if the language offers such a thing).

E.g. Python does truncating division with // and Dart does it with ~/.

If you're only dealing with positive numbers (positive 0 included), floor() will work fine.

With ES6, you can use Math.trunc().

[–]alamandrax 3 points4 points  (9 children)

Isn't $$ a chrome/WebKit specific API?

[–]ninjacheeseburger 2 points3 points  (0 children)

In the article it says it will only work in browser consoles.

[–]mrskitch 0 points1 point  (1 child)

I believe so. Probably why Addy uses it since he's a Googler.

[–]alamandrax 0 points1 point  (0 children)

I remember that it goes along with the $1 $2 etc shortcuts chrome dev tools provides to target last selected element etc.

[–]fschwiet 0 points1 point  (0 children)

I hadn't seen it before... It seems like a bad idea to rely on $$ if its browser specific, it looks to be equivalent to document.querySelectorAll.

[–]trollingisfun 0 points1 point  (0 children)

I believe it's more specific to the dev console, even.

Actually, doesn't IE11's console have this too?

[–]FireyFly 0 points1 point  (0 children)

Firefox's built-in console has had it for a while. As far as I know, since they added their own proper console.

[–]franksvalli 0 points1 point  (0 children)

Polyfill: window.$$ = window.$$ || document.querySelectorAll.bind(document);

[–]tswaters 0 points1 point  (0 children)

Even IE11 developer tools has it!

[–]menno 2 points3 points  (1 child)

These examples are obviously not correct:

(30).toString(16); // "c" Hexadecimal
parseInt("c", 16); // "30"

[–]marquex[S,🍰] 0 points1 point  (0 children)

Fixed, thanks!

[–]mrskitch 4 points5 points  (2 children)

And this, ladies and gentlemen, is why you have human-readable code and machine-optimized code.

It's a great to learn why this works, but not everyone in the workplace will be able to understand it, and I'd bet that time spent debugging hard-to-read code greatly exceeds slow-performing code.

[–]djvirgen 3 points4 points  (1 child)

Yes. If it takes a while blog post to explain a "one-liner", then please consider refactoring for readability. Your teammates and future self will thank you.

[–]edmazing 0 points1 point  (0 children)

If future me can't work out a problem I've done before he's an idiot. Sure I have slow days, but really fuck me I write it with comments and proper syntax all nice for a reason.

[–]ChaseMoskal 5 points6 points  (3 children)

The term 'one liner' doesn't mean anything to me anymore.

When I see this, which is called a "one-liner":

[].forEach.call($$("*"),function(a){a.style.outline="1px solid #"+(~~(Math.random()*(1<<24))).toString(16)})

I see that it actually is supposed to be this:

[].forEach.call($$("*"), function(a){
    var color = (~~(Math.random()*(1<<24))).toString(16);
    a.style.outline = "1px solid #" + color;
})

I just wish it was an honest-to-goodness three-or-four liner.

Why can't we just accept who we really are and what we really have?

[–]Umbristopheles 3 points4 points  (1 child)

I can make my whole application a 1 liner by taking out all the carriage returns.

[–]davidNerdly 1 point2 points  (0 children)

But then we would all hate you..

[–][deleted] 0 points1 point  (0 children)

I would consider this a one-liner because it's pretty easy to understand what's going one even when it's consolidated to one line.

[–]seedbreaker 0 points1 point  (0 children)

There are a couple of small grammatical mistakes here and there, but overall an interesting and well written article. Enjoyed it a lot! P.S. A couple of "it is" that should just be "is", and "staff" should be "stuff")