
How true hackers write JavaScript - damir
https://news.ycombinator.com/hn.js
======
fuzzy2
Not all that impressive, considering non-consistent code style and the like.

Also not a fan of unnecessarily cryptic variable names.

~~~
kowdermeister
I think the title is ironic.

------
saagarjha
Hacker News also works pretty well without JavaScript at all, which is really
nice. Not something that I use often, but it comes in handy when you want to
post a comment from your potato device that can't handle the modern web.

~~~
Tade0
I once managed to open HN on a Nokia 3110 classic. Scrolling hanged and
eventually rebooted the phone, but nevertheless the signature "HN orange"
could be seen.

------
perfunctory
In most places this code would never pass a code review and would be
considered plain unacceptable. I guess I'd prefer some place where this code
style is the norm.

~~~
noobermin
Go into academia. You'll receive _no_ code review, and you'll deal with pretty
bad code, but you'll be free to write however you want as long as it works and
can be published.

~~~
plopilop
"pretty bad code"? That's an euphemism.

~~~
noobermin
yes

------
fb03
Way better than loading megabytes of .js just to display stupid animations and
other useless gimmicky stuff.

This file is just in consonance with this website's style: concise, terse, to-
the-point and with minimal bs.

Of all of my currently in-rotation news websites, this loads and reacts the
fastest, no matter where I am or how crippled my current connection is. I am
sure they are factoring load times and speed over 'shiny new features'.

Keep going, YC!

~~~
davnicwil
Animations being badly/over used aside, this is a bit of a straw man argument.

If you load megabytes of JS to do animations, as opposed to using CSS, then
yes you are just doing it wrong.

Things aren't either exactly right and done one way, or else terrible. You can
of course have bells and whistles like animations if your usecase requires
them (HN doesn't!) and still have a fast loading site with clean code, if you
do it right.

~~~
fb03
I agree with you. And the animations example was simply one of the bad
offenders (merely one of the symptoms of the disease).

The disease is overutilizing buckloads of js scaffolding which is blatant in
the majority of the websites right now. Even a small blog now "has to have"
the latest xzibit framework which pulls 32 foobar dependencies, all gulped
into one huge blob of minified compressed clusterfuck-pack.

~~~
davnicwil
Yeah, I guess it is a tradeoff between engineering productivity and
performance. Both are features. Past a certain level of complexity it _is_
usually the correct decision to choose the framework and toolchain so that you
can deliver and maintain features _at all_. "Make it work -> make it good ->
make it fast" is very relevant here.

Though I agree about what you're saying for blogs and other content sites,
where the tradeoff is clearly inappropriate in a lot of cases. With that said,
things like [https://www.gatsbyjs.org/](https://www.gatsbyjs.org/) offer an
interesting '3rd way' here, allowing you to keep the productive toolchain and
framework stuff but still serve a blazing fast static content site.

------
mr_vile
This style is very similar to one which is used by expert C programmers, it
seems that most people that write like this have had experience writing
parsers and compilers too. It's not their fault that most people don't just
think purely in terms of function composition :P

~~~
olavk
I'm sure the code works very well for its intended purpose, but I'm kind of
wary of the idea that using obscure abbreviations like 'posf', 'arem', 'vis',
'ind' etc. makes you "expert hacker".

I don't agree that code should always be designed to be perfectly readable by
outsiders. If such abbreviations works for the people who have to maintain the
code, more power to them. But I disagree than this is somehow proof of
superior skill.

------
olalonde
If there's one take away from this, it is that programmers have widely
differing opinions on what constitutes good code.

~~~
userbinator
The spectrum seems to span from APL at one extreme, and Enterprise Java/C# at
the other. This is closer to the former but still not that extreme (I
personally lean toward that direction too, mostly working with Asm and C.)

------
sbr464
I feel like this was written in this particular style assuming it was not
going to be minified. I think it's ok to use this style if only maintained by
a couple of people, or for a small project like this. I do like it's
conciseness, but I don't feel the title is appropriate.

------
dang
Maybe I'll respond in a general way to what's come up here. This code isn't
unreadable, or accidentally readable—it was written specifically for
readability. Similarly, it isn't unmaintainable or accidentally
maintainable—it is written specifically for maintainability. Unlike in most
programming debates, we can actually prove this. Here is the proof: I've
talked to everyone whose job it has ever been to read this code, and all agree
that it is easy to read. I've also talked to everyone whose job it has ever
been to maintain this code, and all agree that it is easy to maintain. The
point is that readability and maintainability are determined by _who_ is
reading or maintaining, and the 'who' that matters for determining that is the
team responsible for a system.

If that sounds strange or obviously wrong, it may be that you're taking
certain widely-held beliefs about programming as if they were truths about
programming. Also, the conventions of the larger codebase which this small
program is drawn from carry a lot of information, and free us to work with
concision and simplicity that aren't so common in production codebases.

I know this sounds weird, but hope that it will stir curiosity in one or two
of you. By far the biggest mistake I made as a young programmer was staying
within what one might call the shallowculture for longer than I needed to. The
outstanding properties of the shallowculture are that it 'knows' how to
program and 'knows' to dismiss uncommon approaches to programming as wrong and
inferior. If you notice yourself reacting this way, my grizzled advice is to
pause and start questioning. Look into it deeply, and you'll find that this
seeming 'knowledge' is spurious. That will open up for you more satisfying
ways of making software. For example, you'll find ways of avoiding the
complexity bloat that is routinely lamented in Hacker News threads as a plague
on our industry.

~~~
olavk
I don't like criticizing the code since it clearly works as intended and HN
works very well, and I am a happy user. But since you argue that the code is
very deliberately maintainable I will challenge that, because I am not
convinced. The code is tightly coupled to the HTML in a way that makes it
difficult to change the HTML without breaking the code. For example it
_should_ (if the code is so maintainable) be trivial to change the use of
tables for presentation into semantic HTML like say UL/LI. Since this would
improve accessibility this is a net win with no drawbacks, right?

~~~
dang
This is hard to discuss clearly without the rest of the code being visible
(sorry—I know that would be interesting but we just can't). But the general
reason why that argument doesn't apply is that maintainability is relative to
the goals of a project, and making it "trivial to change the use of tables for
presentation into semantic HTML" is not a main goal of this project [1].
Meanwhile the cost would be high: it would take a lot of code to give HN's
software the property that its markup could easily be switched between two
dissimilar structures. High cost and low benefit adds up to not worth doing
for this program. (Of course it could be different for other programs.)

And that's merely the best-case scenario; systems that go for that rarely make
it to 100%. What usually happens is they find abstractions that give 90% of
the desired flexibility, but break down at the problem's edges and require
special-casing and workarounds to sort of hobble their way to the finish line.
The complexity of that special-casing means you end up giving back a lot of
the maintainability you hoped to win with such a design. It's like grabbing a
handful of sand: a lot slips through your fingers.

Since we're not going for full decoupling and flexibility, what should we do?
Here we differ from what most developers would consider best practice. We
don't adopt half-measures, we try to avoid them. Since we're not trying to
fully abstract away the structure of the markup, we just let that structure be
naked in the code. For example, since 'hide' needs to remove 3 nodes, it just
removes 3 nodes. We do it this way not because we don't know the risks of
dependencies, but because we know the risks of abstractions: most cost more
than they're worth. Programmers have been so steeped in 'best practices' that
they tend to incur those costs without being sufficiently aware of them. Alice
does it for her feature, Bob does it for his, neither's top priority is the
system as a whole, and the costs build up like mercury in the blood. To be
fair, most programmers have little choice, given the project they're on and
the culture of the profession. You can see how deeply engrained this culture
is by the bulk of the responses in this thread. In my experience, one can't
fight it. One can only look for a project with different thinking (which btw
is why sctb and I are here).

Our profession has a large blind spot about the downside of adding code.
Obvious abstractions that have a best-practicey feel (e.g. assigning names to
the nodes that 'hide' has to remove, or putting the nodes into a collection so
'hide' need only remove one thing instead of three) are like that old adage
"no one ever got fired for buying IBM". You make a suboptimal choice because
it feels like you're supposed to and it feels safer. That dooms software. All
those little choices add complexity, complexity compounds rapidly, and soon
you are paralyzed with bloat. To avoid this you need heightened, even paranoid
awareness of the downside, which unfortunately runs opposite to most software
culture. I'm actually not very good at it; others are much better [2]. pg
never adds a line of code unless he's forced to. Me, I add 10 lines of code,
then feel embarrassed and and remove 7 or 8 of them. But at least I feel
embarrassed.

So that little number 3 in 'hide' is not there because we're naive and made a
rookie error, waiting to be uncovered by the internet—it's because we
considered the code that various options would cost, considered what they'd
buy us, and decided that _in the context of this system_ the simpler approach
was best. That's what I meant about the code being written for
maintainability. If you do that in every case _and_ you're lucky _and_ the
moon is in Sagittarius, maybe you evade the rigor mortis of complexity and
live to hack another day.

[1] In case that sounds like dissing accessibility, I'm not, but it would take
time to explain why and I don't think those reasons are in the critical path
of this discussion.

[2] If anyone wants more grizzled advice, mine is to search out a programmer
who is better at this than you are and make it a career goal to work with
them.

~~~
olavk
Thanks for an interesting answer. I really appreciate to hear the thinking
behind the design of the code. But when it comes down to it, the argument seem
to be that this piece of code is not _supposed_ to be maintainable because you
don't find it important to be able to easily change the HTML. Which again is
totally fine if that is a deliberate decision, but there is a difference
between arguing that some code is designed for maintainability and arguing
that it it does not _need_ to be maintainable.

As for adding code, note that my suggestion would _remove_ code - removing a
single node in code is obviously simpler than removing a fixed number of nodes
in a for loop. But you are correct it introduces an abstraction. I appreciate
the cost you assign to abstraction, but I personally think this should be
weighed against the cost of more complex and fragile code as here.

Again, I'm not criticizing the code itself, just the assertion that the code
is super maintainable. The proof of the pudding is in the eating. If it is too
costly to accommodate users using screen readers (how few and unimportant they
may be) because of the complexity and risk of changing the HTML, then you have
a maintainability issue.

~~~
dang
It sounds like we mean different things by maintainability. What I mean is
ease of keeping the existing system aligned with its current goals. What
you're talking about, I would probably call extensibility, and indeed that is
not what our code is written for.

In my experience the way to get extensibility is _not_ to try for it, which
leads to bloat, but rather to have the smallest system you can. A smaller
system is easier to make a major change to than a larger system, and you don't
need to anticipate the change in advance.

~~~
olavk
I'm sure nobody decided that it was an explicit goal of the site to have
accessibility issues. It has those issues for historical reasons.
Maintainability to me is being able to fix such relatively minor issues
without everything coming crashing down. If it is too difficult to fix then
the code is not maintainable whether or not the code is super small and
compact. (Note I'm not arguing HN _should_ be redesigned to use semantic
markup, just using it as an example of reasonable maintenance.)

I would say _simplicity_ is a really important part of maintainability, but
that is not the same as _smallness_. For example, the for-loop mentioned is
quite short and compact, no doubt about that. But to me it it not _simple_ ,
because it is hard to figure out what it does and what happens when you change
it. It is tightly coupled to the particulars of the HTML (even including the
whitespace) so you have to dive into the HTML to see what nodes is removed and
figure out yourself what is the implicit relationship between these three
nodes. So is difficult to change without accidentally breaking something.

Making code simpler often leads to it also being shorter, which is good. But
making code shorter without making it simpler just leads to unmaintainable
cryptic code.

But I acknowledge maintainability also depends a lot on the people who are
supposed to do the maintenance. I personal have limited memory, and I would
forget what exactly those three nodes represented within hours of writing the
code. I use abstraction and simplicity not because of some lofty ideal, but
because I simply do not have the mental capacity to keep this amount of
accidental complexity in my head. Your mileage may vary.

------
iamleppert
But where is Webpack??? Why aren't they using ES7 decorators and lambda
syntax? Each one of those functions should be broken out into its own separate
source file and they should be using classes, not functions. For the love of
god, how do they trust this code will do the right thing without the
obligatory type safety of a shoe-horned type system?

And what is with this code returning false for everything? Everyone knows
you're supposed to return JSX wrapper in another DIV in order to talk to
browsers!

Lastly, how could they ever expect this solution to scale? I bet this is only
slightly worse than okay for a hack-a-thon but hell would surely freeze over
the minute a single user was forced to use it!

It's a maintenance nightmare. It's far better to start with some webpack +
react boilerplate. Each one of the dependencies means that someone else is
maintaining the code, so you can wash your hands of that responsibility and
sleep well knowing internet elves are maintaining your web app!

~~~
masswerk
Also, there's an inexcusable lack of modal popups and dialogs on this site!
They simply do not care about users! :-)

~~~
reitoei
It looks like you're using an adblocker. Click here to upgrade to HackerNews
Gold for just $1 a week!

------
timrichard
There might be more appreciation for "how future maintainers write
JavaScript", but that's not cool and it takes a couple of decades more
experience.

Looks a lot like "read-only" web code I've encountered on projects, where
waves of people decided it's easier to work around what's already there than
amend it (and that's also how you end up with 20,000 line CSS files).

------
jek0
The authors is quite lazy or cannot type fast enough, so he has to create all
those cryptic abbreviations: `rks` for `ranks`, `unv` for `unvote`, etc. Which
makes it really hard to read.

But it's especially the inconsistent use of camelcase in function names than
makes me think it's pretty amateurish.

~~~
richrichardsson
Hard to read? The sum total of the script fits on 2-3 pages at most, no single
function exceeds about 10 lines, if you have trouble reading and reasoning
about that code then something is wrong.

~~~
paulintrognon
functions will not be any line longer by writing ranks instead of rks, but it
will greatly improve readability.

You can have the exact same code wihtout abbreviations, and I can assure you
that it will still be 2-3 pages long, with very short functions.

~~~
dang
You can't evaluate readability without knowing the conventions that a system
is working with. (That means knowing something about the people as well, btw.)

In this system, 'rks' communicates something that 'ranks' does not: that this
is a local variable denoting a collection, and that it is a collection of the
things that 'rk' denotes one of. So actually it adds quite a bit to
readability.

If I were going to edit that code to make it more readable, the one change I'd
consider is using 'r' (or maybe 'n', because these are integers) instead of
'rk'. One can argue either way whether the k adds information or just noise.

------
lucisferre
I'm not sure if this was linked fatously, but this is actually pretty good JS.

\- All functions

\- The functions are simple and decomposed into smaller functions

\- No usages of "this" (except one necessary one in an event handler)

\- No ham fisted attempts at doing OOP with JS

Only complaints really are naming and code style is overly compact which would
potentially make it harder to understand, but in this context I think that is
ok. There is nothing overly complex going on here. The code seems reasonably
easy to understand.

If there are criticism I'd like to hear them.

~~~
rs86
JS is OOP, although does not have classes. It has prototypes

~~~
lucisferre
Javascript works half-decently as a functional language but is a pretty bad
OOP one. Pick your poison. Add to that the general consensus that
functionally-oriented programs are easier to test, debug and reason about and
you have my reasoning.

------
habosa
The code style is fine, it's practical.

But how about what it does? Has anyone ever tried to collapse or expand a 100+
comment thread here? It can lag for 1-2s. So all the talk of "it's simple code
that serves its purpose" is not really true.

That said, please don't solve my complaint by adding a bunch of SPA stuff to
HN. I love how fast the base content loads here and how there's nothing
dynamic.

------
Aardwolf
Um, is this supposed to be a criticism?

Because this is MUCH better than sites loading megabytes worth of scripts to
do nothing more than render text, have dozens of floating elements everywhere,
auto playing videos that follow you, "use our app" buttons, etc...

JS is the assembly of the web, do you also criticize games written in
assembly?

~~~
krapp
>JS is the assembly of the web, do you also criticize games written in
assembly?

No it isn't, any more than C++ is the assembly of your operating system.

Assembly has the terseness that it does because of constraints that javascript
doesn't have -- this javascript looks the way it does because the author
wanted it to look like lisp code, not because it has to.

~~~
erikpukinskis
Assembly, used this way, just means “the words that create action at the layer
below which you cannot go”. Also referred to as “metal”.

Depending on where you are starting that could be JavaScript, or it could be
Basic, or any of a number of things. In the browser it’s JavaScript.

This only makes sense if you can see that there are machines within the
machines, within the machines.

~~~
krapp
Fair enough, it's a metaphor, but a fragile one that breaks down easily, as I
believe it has here. Using actual assembly to defend terse coding in
javascript because javascript is "assembly for the web" doesn't work.

~~~
erikpukinskis
That’s true.

------
giancarlostoro
While everyone's freaking out over the code I'm remembering the old code that
was only 2 functions[0], which leaves me to believe this code was changed not
too long ago. I wish we had a github or something of some of these front-end
changes to HN just to see how things change over time. I figure it wouldn't be
too much, but still.

The original JS was inlined and contained two functions: hide and vote.

[0]
[https://news.ycombinator.com/item?id=11307758](https://news.ycombinator.com/item?id=11307758)

~~~
dang
We expanded it for collapsible comments a few years ago.

~~~
giancarlostoro
Been here a little minute, feels like it was just yesterday, but if I had to
guess at least after 2016? Since that's when that comment is from. I guess
it's my fault for not looking once collapsible comments were added though :)

------
oh-kumudo
The code is very much readable, without suffering from being concise. Love it.

------
noncoml

        function hidestory (ev, el, id) {
          for (var i=0; i < 3; i++) {
    

That 3 there is the problem with “simple” JS. It’s tightly coupled to the HTML
but they live in completely different places.

On a side note this 3 should be at least assigned to a variable with a
meaningful name.

~~~
paulintrognon
hidestory() is not even used anywhere ; why is it here for? Debugging?

~~~
fenomas
It's called by the "hide" link under each story on the top page.

------
damir
Love it. Looks like lisp (can almost tell pg or rtm wrote it?), functions all
over. No bloat...

------
boubiyeah
Looks like pretty average code to me.

------
krapp
It works... every criticism beyond that is just an argument about taste,
although personally I think it's unnecessarily terse.

I've written uglier code and gotten paid for it. ¯\\_(ツ)_/¯

~~~
DanHulton
Not necessarily. Sure, all code has to work, but unless you're going to ship a
product and then never, ever return to it, it has to be maintainable as well.

There are a few arguments in this thread about poor maintainability of this
code, when coupled to the HTML it affects (which always must be considered,
otherwise this code is pointless), and I tend to agree with them.

Personally, I wouldn't mind some less terse names and some more semantic
groupings of HTML nodes.

Thankfully, it _is_ simple. I'm pretty sure doing that would be the task of an
afternoon, though I would definitely want to accomplish that before trying to
change anything else on the page.

~~~
krapp
>but unless you're going to ship a product and then never, ever return to it,
it has to be maintainable as well.

This is Hacker News... there's a good chance it will go more or less unchanged
for a while.

------
michalstanko
This is very similar to what I'd do for my small projects, I really love the
small size and the brevity of it.

On the contrary, it's not what I'd do at work for one of our bigger web apps.

No need to criticize, it does what it's supposed to.

------
TheAceOfHearts
This code would be a bit shorter and simpler if it dropped support for old
browsers (specifically, Internet Explorer).

Some examples:

The functions hasClass, addClass, and remClass could be replaced by
Element.classList [0].

Another one is remEl, which can be replaced with a direct call to
ChildNode.remove() [1].

I was going to give more examples, but I don't feel like rewriting the whole
script.

[0] [https://developer.mozilla.org/en-
US/docs/Web/API/Element/cla...](https://developer.mozilla.org/en-
US/docs/Web/API/Element/classList)

[1] [https://developer.mozilla.org/en-
US/docs/Web/API/ChildNode/r...](https://developer.mozilla.org/en-
US/docs/Web/API/ChildNode/remove)

~~~
masswerk
Why should anyone invest lifetime to remove compatibility from a perfectly
working application? Just to prove that not everyone is using an evergreen
browser?

------
pygy_
This is neat (how the up/downvote onclick handler sends the info to the
server).

    
    
        new Image().src = el.href;
    

Where href looks like this:

    
    
        vote?id=xxxxxxxx&how=up&auth=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&goto=item%3Fid%3Dzzzzzzzz#wwwwwwww

~~~
perfunctory
Maybe it's neat but this is not how anyone would write it today. This is the
legacy code still left from the time before ajax was invented.

~~~
TekMol
Hacker News was launched 2007.

XMLHttpRequest has been around for about 7 years then and using it was called
Ajax for about 2 years already.

~~~
qbonnard
Not to mention the "ajax" function in the script...

------
kozak
Write it with all the modern "proper" approaches, and good luck even just
recompiling it in 10 years. While this code - however imperfect - remains just
as maintainable today as it was when originally written (which is probably
more than 10 years ago).

~~~
acdha
That's true if you're talking about using React, Vue, etc. but do you really
think ES5 JavaScript is more likely to be supported in the future than ES6?

~~~
kozak
I'm talking about NPM modules and the build toolchain (which consists of even
more NPM modules). This stuff goes stale very quickly (at the 10-year scale).
It's good for a project that is kept continuously alive (i.e. when there is a
team of developers that is continuously making changes to the project), but
not when the project sits on a shelf for 10 years and then needs to be
modified.

~~~
LiterallyDoge
This is a good point, but if you take NPM / Yarn / Bower (RIP) with a grain of
salt and secure your codebase against failures, they work nicely as a
toolchain.

~~~
kozak
Do you mean committing node_modules to the Git repository? Unfortunately,
that's not a panacea with build tools, because some of them contain non-JS
code that is tightly coupled to Node and OS versions.

------
biznickman
Lol this title is literally a recipe for fighting. That said, I love how they
eliminated the need for JQuery with all the functions up top. Super
minimalist.

------
TAForObvReasons
[https://twitter.com/triskweline/status/798443082740023296](https://twitter.com/triskweline/status/798443082740023296)

> Valve's Steam Store renders on the server, uses ancient jQuery 1.8, loads 12
> unminified JavaScripts.

> It moved 3.5 billion dollars in 2015.

~~~
Zealotux
I'm not sure of what it's supposed to prove, who knows how many clients and
how much money Steam lost because of slow loading/painting times or just plain
broken JS. From experience I can tell that I gave up buying a game on Steam at
least once because of broken jQuery.

There are real metrics from top industry leaders[1][2] to prove that loading
time has a clear impact on conversion rates.

>Consider this when you need 5000 npm modules and a team of 10 to compile your
startup's login form.

Unminified JavaScripts files and bloated JS are both wrong, once again: not
sure of what's the point of comparing both.

[1] [https://medium.com/@vikigreen/impact-of-slow-page-load-
time-...](https://medium.com/@vikigreen/impact-of-slow-page-load-time-on-
website-performance-40d5c9ce568a) [2]
[http://glinden.blogspot.com/2006/11/marissa-mayer-at-
web-20....](http://glinden.blogspot.com/2006/11/marissa-mayer-at-web-20.html)

~~~
talltimtom
What it proves is that it’s good enough to make tons of money.

You’re arguing from a hypothetical alternate reality where they could have
been better off, but there’s no point in that. It’s like saying “Sure Usain
bolt can run a sub 10sec 100m.... but if he’d gone into politics perhaps he’d
made even more money and been twice as famous, we have no way of knowing, so
how does that sub 10s 100m really prove anything about whether or not he’s
fast?”

Usain bolt is fast, and that code made money. It’s just facts, accept them.

------
ourmandave
I can eventually see this coming up on SO asking how to, "make this work. I
copied it from the internet."

It will be tagged _javascript_ and _jquery_. =(

------
expertentipp
"var", "function", one explicit "return" at the end of function body, working
directly with the DOM? I still do this sometimes and the
Angular/React/TypeScript kids look at me like at some alien wizard, while
Java/.NET folks still think "it's just a dumb website".

------
nickthemagicman
I actually think this code is nice! Love the one liners. Variable names could
be a little more....explicit.

------
jmull
Here's something maybe productive to take away from this kibitzing thread:

Consider this code and the range of comments on it while doing (or undergoing)
your next non-trivial code review.

(For one thing, it reminds me that a great value of adopting comprehensive
code standards is that it resolves these kinds arguments, letting teams get on
with the important stuff.)

------
olavk
There are some polarized opinions in this thread. Someone wrote _That might be
one of the most readable pieces of code that I 've ever read._ Apparently it
improves readability immensely to rename 'forEach' to 'aeach'.

To be honest the code is not very readable, but it _is_ very simple and self
contained. It would be very easy to dive into to fix a bug because there are
no external dependencies or frameworks you have to understand.

The list of one-liners at the top seem like a very minimal 'jQuery'-like
layer. Perfectly fine except for the l33t identifier names.

But there is also some really fragile coupling to the HTML, e.g.:

    
    
       for (var i=0; i < 3; i++) { remEl($(id).nextSibling) }
    

I wouldn't dare to modify the HTML when the JavaScript looks like this! What
are the three elements which are being removed? Why exactly three? Probably
the author and maintainer of the code knows, but this is the kind of coupling
where nobody except the original author would dare to change anything. Perhaps
this why HN still uses tables for presentation - nobody dares to change it?

~~~
aleksei
Perhaps this why HN still uses tables for presentation - nobody dares to
change it?

There's also simply no reason to change it, it works.

~~~
TeMPOraL
> _There 's also simply no reason to change it, it works._

It works, and it's arguably simpler than the div-soup with extensive styling,
which is the "kosher" way of doing this.

~~~
olavk
Well using divs would at least make you able to group the elements
semantically so you could remove a single node rather than having a for loop
which removes exactly three nodes. This would be significantly simpler in my
opinion. And it would be less fragile since now you can redesign everything
inside this node without the JavaScript breaking.

~~~
jmiserez
Agreed. But getting divs to do what you want has also gotten considerably
easier in the last 10 years. It used to be a lot more work/boilerplate to get
a consistent div-based layout across all browsers down to IE 6.

As for the redesign, the total amount of code (HTML/CSS/JS) you’d need to
touch is still small. Do web designers still exist that only do HTML/CSS but
no JS?

~~~
TeMPOraL
> _Do web designers still exist that only do HTML /CSS but no JS?_

I hope so, given that most websites (as opposed to web _apps_ ) in existence
should not need JS for core functionality.

------
qbonnard
> function addClass (el, cl) { if (el) { var a = el.className.split(' '); if
> (!afind(cl, a)) { a.unshift(cl); el.className = a.join(' ')}} }

Why does it use `unshift` rather than `push` ?

The only reason I can think of is that the last class added will be faster to
remove when iterating over the array...

------
Tajnymag
That might be one of the most readable pieces of code that I've ever read.

~~~
vorticalbox
Really? It has horrendous naming and it isn't even using es6

function byClass (el, cl) { return el ? el.getElementsByClassName(cl) : [] }

const byClass = (el, cl) => el ? el.getElementsByClassName(cl) : []

~~~
oh-kumudo
Your code is not more readable, it is just shorter.

In fact, using the online babel transpiler, it transpiles the es6 version you
provided precisely into hn's version:

[https://bit.ly/2MPmjK7](https://bit.ly/2MPmjK7)

To use es6 to achieve what this tiny piece of javascript could already do, you
probably need to introduce some tremendous dependency like babel just to be
compatible with all kinds of weird browsers out there. What does es6 give
here? Not very much in my opinion.

~~~
simplysh
It's 2018. Unless you're using Internet Explorer arrow functions are supported
natively.

~~~
lghh
And I bet there are some people here using IE. And old browsers.

~~~
Snd_
I would REALLY love to see the browser usage statistics for HN :)

~~~
yesenadam
Well.. I might be an outlier, I use OmniWeb and Camino as they give the best
results with my 2005 Mac. So I'm still living in the Pre-=> Age.

------
dandare
Am I the only one who thinks this is unreadable?

    
    
        var on = !afind($(id), collapsed());
        (on ? addClass : remClass)($(id), 'coll');
    

where

    
    
        function afind (x, a) { var i = apos(x, a); return (i >= 0) ? a[i] : null; }
    

where

    
    
        function apos (x, a) { return (typeof x == 'function') ? posf(x,a) : Array.prototype.indexOf.call(a,x) }
    

where

    
    
        function posf (f, a) { for (var i=0; i < a.length; i++) { if (f(a[i])) return i; } return -1; }

~~~
crazygringo
+1,000.

The best programming advice I ever got was to write code for other people to
read (and understand at a glance), not for the machine to execute.

This code is the precise opposite of that.

~~~
dang
It is a precise embodiment of that: the code was written specifically for
people to read, and those people find it very readable.

It is a mistake to assume a single standard here. Does a German speaker find a
Sanskrit text readable, or a trombone player find a guitar piece playable?

------
damir
OP here, looking at the vote function, I learned something new today - this
code:

    
    
      new Image().src = el.href;
    

will make image request to server with any params from href so you can process
the voting stuff. Nice one.

------
arendtio
While I appreciate the JS on this page (for usability reasons), I was
surprised by the amount (149 lines) that is used for a page which looks like
plain HTML.

Nevertheless, the code looks kinda beautiful.

------
amelius
To me this looks more like transpiler output ...

~~~
dotancohen
It would if the variable names were all a,b,c. But they're not, they are
almost Polish Notation. There's a all right, but fn, cl, tag for callbacks
classes and tags. Also n and m for integers in a range and x for iterable
integers.

This is the work of someone with a ton of experience. You take 10 minutes to
look at the code and understand it, then you are groking complex ideas and
operations quickly. I'd love to work with whoever wrote that code.

------
JTbane

      function $(id) { return document.getElementById(id); }

Nice! Now I don't need jQuery.

~~~
spiralx
Here's the smallest jQuery replacement I've seen :)

[https://gist.github.com/paulirish/12fb951a8b893a454b32](https://gist.github.com/paulirish/12fb951a8b893a454b32)

It let's you do

    
    
        $('p').on('click', el => /* ... */)
    

which is handy for smaller scripts :)

------
halis

      My opinion of the code is this. 
      It's pretty tight, which I can respect. 
      I haven't run it, but it PROBABLY works.
    
      But it's hard to read. 
      Things are named poorly. 
      Many edge cases are missed (not checking args properly).
    
      These functions are not pure and therefore, harder to test. 
      Maybe true 1337 H@X0Rz don't need to write tests, but some of us have mortgages to pay...
    
      I didn't re-write all of this mess, but check out the few util functions that I re-wrote.
    
      As a developer, which version would you rather work with?
      If you said the original version that's cool with me.
      But don't try to come work on my team.
    
      // isNull :: a -> Boolean
      const isNull = x => x == null
    
      // isNullOrEmpty :: String -> Boolean
      const isNullOrEmpty = string => string == null || !string.length
    
      // $ :: String -> HTMLDocument -> HTMLElement
      const $ = (id, _document = document) => {
        if (isNullOrEmpty(id)) return null
        if (isNull(_document)) return null
        return _document.getElementById(id) || null
      }
    
      // findClassInElement :: HTMLElement -> String -> [HTMLElement]
      const findClassInElement = (el, className) => {
        if (isNull(el)) return []
        if (isNullOrEmpty(tagName)) return []
        return el.getElementsByClassName(className) || []
      }
    
      // findTagInElement :: HTMLElement -> String -> [HTMLElement]
      const findTagInElement = (el, tagName) => {
        if (isNull(el)) return []
        if (isNullOrEmpty(tagName)) return []
        return el.getElementsByTagName(tagName) || []
      }
    
      // findClass :: String -> HTMLDocument -> [HTMLElement]
      const findClass = (className, _document = document) => {
        if (isNullOrEmpty(className)) return []
        if (isNull(_document)) return [] 
        return findClassInElement(_document, className) || []
      }
    
      // elementHasClass :: HTMLElement -> String -> Boolean
      const elementHasClass = (el, className) => {
        if (isNull(el)) return false
        if (isNullOrEmpty(className)) return false
        return el.className.includes(className)
      }
    
      // addClass :: HTMLElement -> String -> Boolean
      const addClass = (el, className) => {
        if (isNull(el) || isNull(el.className)) return false
        if (isNullOrEmpty(className)) return false
        if (el.className.includes(className)) return true
        el.className = `${el.className} ${className}`
        return true
      }

~~~
aprdm
I prefer the original one.

I don't see the value, for this use case, of all those checks. We know what
the input values are, essentially what's in the DOM that we 100% control.

I feel ES6 syntax much less readable than ES5 (probably because I have worked
more in ES5 than ES6), it introduces a bunch of new symbols.

How can this

    
    
      const isNull = x => x == null
    
    

Be more readable than

    
    
      function isNull (x) { return x == null}
    
    

Another example,

    
    
      const addClass = (el, className) => {
      }
    
    

I always have to remember what this construct means and translate / unroll it
in my mind to a regular function.

Also, why would an addClass return a boolean? Doesn't make much sense.

    
    
      // $ :: String -> HTMLDocument -> HTMLElement
        const $ = (id, _document = document) => {
          if (isNullOrEmpty(id)) return null
          if (isNull(_document)) return null
          return _document.getElementById(id) || null
        }
    
    

How can above be more readable than

    
    
      function $(id) { return document.getElementById(id); }
    
    

I bet I can give the original code for Python/C/Java developers and they would
understand / change it easily.

That said, I consider myself a backend / devops person who sometimes needs to
do work in the frontend, the biggest project I did was a medium size app with
around 40 routes that I used react, es5 and bootstrap.

Big fan of Go and it's very readable / limited syntax.

------
linkmotif
Yeah this is nothing bad at all just some code. I’m fact I can grok it on my
phone so I’d say it’s good code.

------
LukaszWiktor
The less lines of code, the less chance for a bug.

------
Ataraxy
Criticisms and bizarre fawning over beauty that a couple of people have made
aside, the one thing that bugs me the most is the inconsistent usage of ;

~~~
dang
I should fix that, since sctb probably agrees with you and is too polite to
say so.

Any suggestions for me as a shitty semicolonist?

~~~
Ataraxy
I'm not some grand arbitrer of the semi colon debate to tell you what is the
best possible thing to use (to the best of my knowledge it's a preference
thing at this point). Personally I prefer to just use none at all these days
in practice, but that's just me.

I just found it weird that they were seemingly arbitrarily used here on
occassion.

------
bstar77
IMO, this code is perfectly fine if you are one guy, never plan/want to change
anything and don't expect your site to grow.

------
Timpy
Seriously though... do you guys declare variables with var or let? I thought
let was the new standard for scoping reasons. I'm not trying to start a holy
war, I'm just a junior looking to learn.

~~~
strken
let and const are good practice in code that will be transpiled, or in
Node.js, but they're newish features that don't have 100% browser support yet,
so they shouldn't be used if your code has to run directly in a browser.

[https://caniuse.com/#feat=let](https://caniuse.com/#feat=let)

------
AltruisticGap
I thought I was smart...

BEFORE

function vis(el, on) { if (el) { on ? remClass(el, 'nosee') : addClass(el,
'nosee') } }

AFTER

function vis(el, on) { if (el) { window[on ? 'remClass' : 'addClass'](el,
'nosee') } }

...sadly we have to add window when using the square brackets so it's not much
shorter. Oh well.

~~~
Arkanosis
Why not:

function vis(el, on) { if (el) { (on ? remClass : addClass)(el, 'nosee') } }

?

~~~
AltruisticGap
Ahh thanks! I forgot about that. I'm guessing I didn't think about it because
I'm used to the following pattern ,which is actually useful from time to time
in my projects. And typically it happens in an object/class:

    
    
      this[expr ? 'method1' : 'method2'](args)
    

So I have to add "this" as the methods are not global. Now if I overlooked
something again I'm definitely getting rusty :)

------
danschumann
I dunno. Is posf really better than indexOf? Why not use more conventional
names for the underscore-ish functions they made?

------
omar12
Simple, straight forward and does the job.

------
stephengillie
It's just a pile of functions; no actual processes are scripted to execute.

------
reitoei
ITT: People losing their shit over 149 lines of basic JavaScript code.

------
hguhghuff
Looks like they are wrapping javascript functions instead of just using the
functions.

~~~
olavk
Someone else points out that older versions of IE didn't support array methods
like forEach on nodelists. So it is basically a thin compatibility layer.

------
LolNoGenerics
No ES2046, is that legacy?

------
JoeCortopassi
Everything has a context. If your site has:

    
    
      - minimal functionality
      - rarely if ever changes
      - is maintained by a very small group (Hacker News)
    

...then something like the example linked is perfect. Most web app developers
don't live in this world. The more common situation is:

    
    
      - large and/or transient teams
      - large quantity of inter-dependent features
      - constant changes
    

...which means that the thing you need to optimize for isn't pretty/fast code.
It's clarity and resiliency in the code. Can a junior and senior dev both work
on the same code base? Can someone new to the project be effective with
minimal ramp up time? Can you work on a feature without accidentally stepping
on another persons current task?

Modern web apps are less about being performant and minimal, and more about
dealing with the complexities of large software teams

~~~
slantyyz
>> Modern web apps are less about being performant and minimal, and more about
dealing with the complexities of large software teams

If that's true, then the app will likely have what I call a "programmer's
interface" (which is especially common in most enterprise web apps I
encounter). Those apps might be solid from a code perspective, but don't tend
to be very user friendly.

I tend to think that modern web apps are more about dealing with the
complexities of balancing functionality with ease of use to the end user
_while_ still dealing with the complexities of large _product_ teams, which
include but are not limited to designers, UX experts and developers.

~~~
too-soon-ago
The fancy-lookin' sites are the ones which hijack basic keyboard
functionality, scroll in weird ways, etc.

I can't be the only person who finds browser default behavior more intuitive
and usable in general.

Why does "UX" so often mean mouse-heavy and _always_ favor the beginner over
the power-user.

~~~
richiebonilla
I agree that following browser default behavior is more intuitive in general.
I'm not a fan of scroll jacking.

I'd say apps skew towards mouse-heavy because controls are on-screen and
therefore discoverable. It takes a good deal of product-market fit before you
can count on your users knowing/caring enough about your app to remember
keyboard shortcuts.

Most apps are grown by adding users, so optimizing for the new user is more
important to the business until a certain level of maturity and market
saturation is reached.

Applications with a dedicated professional user base can go deeper into power-
user territory sooner since they can charge more per user, and new users
expect there to be a learning curve.

------
EamonnMR
"True Hackers" can write LISP in any language?

------
joosters
More garbage ycombinator hero worship. 'True Hackers' ? Grow up!

~~~
smt88
I understood it to be a tongue-in-cheek title

~~~
Dirlewanger
You do things ironically long enough, you end up just doing the thing...

------
fithisux
Is this some Arc-to-JS transpilation?

~~~
TeMPOraL
Probably not yet, but I've heard this might be the case in the future.

~~~
dang
We have Arc to JS working in production now, but only use it for moderation
software. The generated output is inevitably more verbose and we wouldn't want
to inflict that on every page load.

------
dmitriid
It only exists because DOM APIs are crap, and jQuery is too big. So it ends up
reimplementing parts of jQuery needed to run.

~~~
vntok
For the record, jQuery 3.3.1 (production, normal) is at 30kb [1]; if you don't
want effects (production, slim), you can get it for less than 24kb [2]. For
the value it provides, that does not sound "too big" to me.

[1]:
[https://code.jquery.com/jquery-3.3.1.min.js](https://code.jquery.com/jquery-3.3.1.min.js)
[2]:
[https://code.jquery.com/jquery-3.3.1.slim.min.js](https://code.jquery.com/jquery-3.3.1.slim.min.js)

~~~
rootlocus
In contrast, hn.js only weighs 2.1kb and contains all the business logic.

~~~
rmetzler
And it isn't even minified. So if there would be a problem in production it
would be easily debuggable.

------
bovermyer
It works reliably and quickly, so mission accomplished.

------
tobyhinloopen
Neat

------
Capbara
document.querySelector('pre').style.color = '#17c517';

------
manojlds
...for a site as simple as hn

Yes!

~~~
erkin
Most bloated sites don't even have an excuse to be more complicated than this.

~~~
LiterallyDoge
Web apps do, though.

------
anta40
any example on how to use this JS code?

~~~
jenscow
Yes, the page you are currently reading.

------
winrid
Barely readable but I think there's a lot of optimization going on here... I
don't think that optimization is done correctly but that is why it's hard to
read.

~~~
zbentley
There's minimal optimization (as in tricks), but lots of optimization (as in
readability and common sense).

It's also incredibly readable; I'm puzzled as to what the bar for
"readability" is if this doesn't meet it.

------
aaaaaaaaaab
Yep, that's about how much JS your site should contain if you're a sane
person.

------
mullikine
any more javascript than this and you're probably making something that's full
of ads

~~~
vntok
Or something that has features?

~~~
alokdhari
Undocumented features??

------
gcb0
we are way overdue for a source code and architecture evolution history here.
the way this started as a toy and exploded without any noticeable performance
problems shpuld be a good case study.

------
SmellyGeekBoy
Is it September already?

------
anonlastname
I approve of those wrapper functions in the first few lines... those would
have saved me so much time when I was a web dev

I'd say I'm going to copy them next time but there is no license header...

~~~
spiralx
If you really need a shortcut then

    
    
        const $ = (sel, elem = document) => [ ...elem.querySelectorAll(sel) ]
    

is nicer as you can use CSS selectors.

------
adamjc
Either run it through a minifier or have the whole thing unminified, these
single lined functions look horrific, and are saving characters for the sake
of it.

Terrible naming, e.g. 'vis' -> reading the function it means toggle
visibility, so it should be called 'toggleVisibility', you shouldn't have to
read what the function is doing to understand what it will do.

You ever get a bug in your code, It's not fun to look back at badly maintained
code and go 'oh, shit, what does this do again?'.

~~~
tzs
> Terrible naming, e.g. 'vis' -> reading the function it means toggle
> visibility, so it should be called 'toggleVisibility'

But it doesn't toggle visibility. It sets visibility.

~~~
adamjc
Yeah, you're right, I misread the code. It should be called 'setVisibility'
then.

A problem that wouldn't exist if it was named correctly in the first place ;)

------
Jaruzel
I don't understand why people are dissing this.

It does what it's designed to do, and fills a specific need for _one_ website.
It's not there as a teaching aid, nor is it meant to be shared for other
people to use elsewhere.

Not everything has to be gold-standard code full of perfect variable names,
extensive comments and good whitespacing. If you have a day job that isn't
primarily writing code, and/or you are likely to be the only person ever
working on that code, who cares if it's not up to the standard that people
around here seem to expect of every project?

As long as the code works, anything else is just gravy. If you were to peel
back the layers on all the major websites out there, I'm sure you'd find less
than stellar code everywhere.

~~~
pacifika
>If you have a day job that isn't primarily writing code, and/or you are
likely to be the only person ever working on that code, who cares if it's not
up to the standard that people around here seem to expect of every project?

Future you that has to decipher the code. Why make it hard on yourself?
Software is a living thing eventually a decision will need to be made about it
and without understanding what it does it's easier to make suboptimal
assumptions.

~~~
watwut
> Future you that has to decipher the code. Why make it hard on yourself?
> Software is a living thing eventually a decision will need to be made about
> it and without understanding what it does it's easier to make suboptimal
> assumptions.

To the defense of the title, in my experience, whenever label "hacker" was
used on programmer or code, it meant "difficult to read, full of hard to
maintain shortcuts and tricks" kind of code. Really always, I don't ever
remember it to be used in any other sense.

~~~
yani
True this

------
onion2k

        function onready () {
          recoll();
        }
    
        document.addEventListener("DOMContentLoaded", onready);
    

Just attach recoll() directly. There's no benefit of going via onready().

~~~
gpvos
You don't have to make every piece of code 100% tight. onready is actually the
kind of function that you very likely want to add more lines to in the future,
so why not leave it in? (I'm willing to bet it had more lines at some point in
the past.)

~~~
onion2k
_onready is actually the kind of function that you very likely want to add
more lines to in the future, so why not leave it in?_

I'm a fan of writing the code you need when you need it. Adding or leaving
things things there 'just in case' leads to code that's harder to reason about
in the future because it's full of things that may or may not be used.

------
sonnyblarney
Thanks for sharing this nice bit of code; we always learn from examples, we
should see more of this.

I feel the naming is too short, things are just a little too specifically
compact and slightly cryptic ... almost like the author is trying to say
something ...

I've never said this before about code ... but I think that code is 'smug'!

Like odd facial hair, or one of those valley-specific t-shirts ... the code
trying to project how cool it thinks it is!

That code is 'humble-bragging' ...

~~~
SmellyGeekBoy
Really? It just looks compact and efficient to me.

~~~
smt88
"Compact" (abbreviated) names are negligibly more efficient when machines read
them, and they're wildly less efficient when humans read them. Code is read
many more times than it's written, so it doesn't make sense to optimize for
fewer keystrokes when typing the code.

This is especially true with modern IDEs that autocomplete everything.

~~~
eadmund
> Code is read many more times than it's written, so it doesn't make sense to
> optimize for fewer keystrokes when typing the code.

That's actually probably _not_ the case with JavaScript, so it may indeed make
more sense to write it to be more efficient to execute.

~~~
smt88
Post-processors do that much better than a human and can also tree-shake.
There's no reason for a human to do it.

------
AltruisticGap
He/she could have put much of the function in the top under the $() to make a
super mini jquery. That's close to what I use with Vue. It's made even easier
to write considering I almost never find a need for jquery's default behaviour
of running its methods on multiple nodes. 99% of the time in my projects I
just need to do something with one node.

~~~
acdha
I’ve replaced jQuery on several projects with $ = querySelector, $$ =
querySelectorAll (wrapped in Array.from if you need to support IE11 so you can
use all of the new array methods which their NodeList implementation didn’t
get until Edge), classList and fetch.

Beyond the code size savings, I find the newer standard methods are generally
easier to understand when scanning code.

~~~
AltruisticGap
I like what this guy did, talking about native methods. However it's _too_
flexible for me, I prefer to have the most minimal functionality in order to
ensure consistency in my code. But maybe that's just me. If the library opens
up too many possibilities, then I have both the "problem of choice" when
writing code, and also it's more difficult to refactor down the line if you
want to change lib. Whereas using your own super simple solution (as long as
it's sufficient), you can also do a simple abstract layer when including some
vendor API.

[https://github.com/eorroe/NodeList.js/](https://github.com/eorroe/NodeList.js/)

~~~
acdha
I've been using this for years – it's not the end of web development but an
awful lot of projects don't need more framework than can fit in a tweet:

    
    
        let $ = (selector, scope=document) => {
            return scope.querySelector(selector);
        };
    
        let $$ = (selector, scope=document) => {
            return Array.from(scope.querySelectorAll(selector));
        };

~~~
schindlabua
My personal microframework:

    
    
        const node = (tag = 'div', attributes = {}, inner) => {
            const e = document.createElement(tag);
    
            for(const [key, value] of Object.entries(atrributes))
                e.setAttribute(key, value);
    
            if(inner) e.innerHTML = inner;
    
            return e;
        };
    

The premium version contains an additional:

    
    
        e.child = (a,b,c) => { const f = node(a,b,c); e.appendChild(f); return f; };

------
TekMol

        function aeach (fn, a) { return Array.prototype.forEach.call(a, fn) }
    

Does this win anything over using forEach directly?

~~~
braythwayt
A lot of things like this will work on both arrays (thta have a .forEach
method) and things that behave enough like arrays such that
Array.prototype.forEach works for them.

The downside of this approach (if you subscribe to the ideology of OO) is that
if you make something completely unlike an array internally, like a linked
list, you can’t use this function on it. Whereas, if you wrote a .forEach
method for it, you could use it interchangeably with arrays anywhere the code
calls .forEach.

Without trying it, I’ll guess that this is used on the arguments magic
parameter. It’s notorious for being array-like but not an array.

~~~
Flenser
DOM functions like getElementsByClassName return a nodelist, which as you say
behaves like an array.

