DEV Community

Cover image for JavaScript is not the problem
Massimo Artizzu
Massimo Artizzu

Posted on

JavaScript is not the problem

The other day I was asked to examine a performance issue on an Angular 11 app. Simply typing in an <input> field was very sluggish, and CPU load skyrocketed at 100% from the moment a key is pressed to ~3 seconds after the last is released.

Angular should be the culprit, right? A silly crafted module that caused a loop that gets broken by a "Maximum call stack size exceeded" error or similar.

But the main thread wasn't occupied by script execution. It was a rendering issue.

It turns out that this CSS rule was the cause instead:

.form-control-extra.is-invalid,
.form-control-extra:invalid::placeholder,
.form-control.is-invalid,
.was-validated .form-control-extra:invalid,
.was-validated .form-control:invalid {
  border-color: rgb(43, 239, 117);
  color: rgb(43, 239, 117);
  padding-right: 4px;
  background-image: none;
  background-repeat: no-repeat;
  background-position: right calc(0.1875rem + 0.375em) center;
  background-size: calc(0.375rem + 0.75em) calc(0.375rem + 0.75em);
}
Enter fullscreen mode Exit fullscreen mode

Simply removing it from its stylesheet would iron things out. And that's not even the plot twist! In fact, that rule wasn't even applied on that page!

...

🦗🦗🦗

...

To this day, I still have no idea about the real cause of the problem. It was tested on Chrome only, as it's an app for internal use, and Chrome is the only supported browser. The problem isn't easily reproducible, but I suspect it's a bug on the Blink engine.

My conclusion was to not ship a 200+ KB blanket CSS file with 2000+ rules when only 2% of them are actually used, and partially.

What's this about?

On May 14th, Chris Ferdinandi published his article The wrong way to do Web Components on his blog. Chris is incredibly knowledgeable about web development, and an excellent teacher that gives a lot of great tips.

But indeed he has peculiar ideas about how the web should be developed.

The article above starts with a clarification:

There’s not really a “wrong” way to build Web Components. But there are some approaches that are, in my opinion, antithetical to the spirit of them, and maybe miss the point of what makes them so great.

From my experience, I've always struggled to precisely outline what's the "spirit" of Web Components. Since their original conception in 2011 by Alex Russell, many things have changed. I've used them for the following goals:

  • building UI Kits (probably their best use case);
  • creating easily transportable widgets;
  • wrapping branches of legacy applications;
  • developing "framework-less" web applications;
  • encapsulating micro-frontends.

But if Chris has a clearer idea, it's worth listening to him.

The article continues mentioning Web Awesome, the "next iteration" of Shoelace by the same author @claviska Cory LaViska. Then it highlights:

But here’s the problem: Shoelace is not a progressive enhancement library.

... No, it's not indeed. But what is the problem, exactly?

Far too much of the UI renders nothing at all without JavaScript. Other stuff starts out as useless junk.

That <sl-button>? Without JS, it’s just text. You can focus on it. You can’t interact with it. Why do I need JavaScript to render a button!?!

So the problem is... that it uses JavaScript, then?

Of course, a button is one of the basic tiles of the Web. We don't need JavaScript to have a working button. We can very well choose to use native <button>s.

You need some JavaScript if you want that button to:

  • have a consistent, encapsulated and controlled style;
  • have a compact, declarative interface like <sl-button loading> to express complex states;
  • emit custom meaningful events.

(Worth mentioning: Shoelace's buttons could have been a progressive enhancement over native buttons - in the form of <button is="sl-button"> - if built-in extensions were supported in all browsers instead of facing the obstinate opposition from Safari's team, but that's another story.)

Moreover, there are very common UI patterns that require JavaScript to work correctly, at least if you want to follow to WCAG's directions to create accessible components. You might also notice that all WCAG's examples use vanilla HTML, CSS and JavaScript, no frameworks, no libraries whatsoever.

You want tabs? You need JavaScript. You want a combobox? Only with JavaScript. A menu button? JavaScript.

It's great that Chris' blog can work without JavaScript. But he surely had to avoid those patterns. He probably didn't need them anyway, but not every web page is Chris' blog.

Must all the web work without JavaScript?

If we apply that concept, it's clear that we should make without all those patterns, many of which are pretty common to web users. But that's not just that.

Many UI patterns emerged from decades of web UI evolution. Take accordions, for example. Or modal dialogs. Or tooltips. All of them have been implemented with JavaScript libraries for a long time, and only recently new standard elements have been introduced and supported by browsers to reproduce the most common use cases.

The matter here is that, if the whole world developed their web sites and applications in a manner that they could always work without JavaScript, they could have chosen one of the following:

  • implement new UI patterns, but always providing alternative fallbacks that could work without JavaScript;
  • forget about new patterns and use the JavaScript-less alternative right away.

I'm inclined to think that most customers would have chosen the second alternative, as it would have meant paying less. This would have meant that those new patterns would have had a much, much harder time emerging, so maybe today we wouldn't even have <dialog> as we have today. And probably not even XHRHttpRequest, let alone fetch. It would have been a completely different web.

Ok, but now we have all we need, right?

Not really. Several patterns still require JavaScript, as there's no native equivalent yet. Think about carousels, for example. Or, yet again, tabbed content. Or tooltips (we may be close to achieve them, thanks to Popover API and Anchor Positioning API).

So, yeah, we probably still need JavaScript. Once the JavaScript usage has been legitimized, the next step is to use it wisely.

With this outcome, flat-out suggesting that everything we do with JavaScript should be progressive enhancement, relegating the role of Web Components as cute wrappers around native elements, sounds frankly narrow minded to me.

What's the fuss about JavaScript anyway?

The JavaScript part of a web page is a something that often blamed for poor site performances. And with a reason, too!

Chart that shows the growth of the the median total data transferred for web pages, from 0.5 MB in 2011 to 2.5 MB in 2024. Source: httparchive.org

While discussing with Chris on Mastodon, he linked this scheme by Stuart Langridge. It highlights all the possible problems that originates from having some JavaScript in the page.

Most of those points are valid indeed, but also apply to other content: CSS, fonts, images, even the HTML that you have to load right at the start. Depending on the role, the page could end up broken as much.

Some other points are indeed specific to JavaScript and, most of all, are mostly avoidable. Because, after you optimize and lazy-load them you can't take away much more from images and fonts, but there's a lot to be done for JavaScript. It's not rare to download something like 5-6 MB of script behemoths nowadays.

And I'm not saying that most of the code isn't used (even though it very well could be!), just that loading, parsing and running it can be deferred at a later time, when it's actually necessary.

So, what about Shoelace? Does it load a multi-megabyte script for its components? Does it block the main thread while doing so?

Not at all. Shoelace is comprised by several small chunks, the result of a finely grained code splitting. An autoloader loads them only when necessary (it uses a MutationObserver internally), but you can opt for a full-library loaded or even cherry-pick only the components you need. (The full library is ~300 kB compressed with Brotli, by the way.)

Screenshot of the network waterfall of Shoelace's components, from Shoelace's site. Several .js chunks are loaded, and most of them are under 1 kB

In short, Shoelace would be the least of my concerns when it comes to performance issues.

So what are the real concerns?

The usual answer when it comes to development: it depends.

Optimizing static assets is trivial nowadays and fundamental to modern web development. I don't want to see a 3 MB animated .gif when I could do much better with a 60 kB .webm video, thank you.

JavaScript bundles would probably be my next target. There are so many things to look into. Major performance offenders include: analytics libraries, monolithic bundles, lack of code splitting, minification, compression.

I like JavaScript as a language and an ecosystem, but I try to write as little JavaScript as possible, delegating the work to HTML and CSS (when working on the front-end) when possible.

But I'd never dream to impose to use JavaScript for progressive enhancement only. There are applications that far too complex to rely on what HTML and CSS can offer alone, and you don't always have the luxury to dictate the back-end to do everything that's needed.

Moreover, as we've seen at the beginning of this article, the problems could be caused by something completely different and unrelated. But, as much as CSS Naked Day exists, you won't hear many asking that every website should work without CSS either.

(And yes, that would raise accessibility concerns as well.)

In the end: JavaScript, per se, is not the problem. Its excessive use is.

Addendum: on Shadow DOM

The next day, Chris clarified even further w.r.t. Web Components and the Shadow DOM:

[...] the shadow DOM, in my opinion, is an anti-pattern.

It provides little benefit, with lots of cost. It’s harder to work with. It’s harder to style. It can break accessibility, depending on how it’s implemented. It requires JavaScript to work.

The point on accessibility is, unfortunately, correct. The main problem nowadays is cross-root ARIA element referencing, which is currently discussed but still not possible. So everything should be carefully engineered in order to not break accessibility.

But if Chris cares about accessibility, he should alleviate his resentment against JavaScript as well.

Is Shadow DOM harder to work with? Sure. Just like any new API, it brings several new concepts to the table as well, especially when it comes to encapsulation.

You're not forced to use the Shadow DOM, if you don't feel confident. But it's not like it's been conceived on a whim, without any real reason, like Chris seems to suggest.

I also find much easier to style custom elements with a Shadow DOM. It's basically a miniature DOM where the cascade is much more manageable, and I rarely even use classes there. The real challenge, in my experience, is creating a way to permit style variations from the consumer without allowing to mangle the component's layout and overall meaning, which is usually a thing you want e.g. for a design system.

Chris Ferdinandi's approach to Web Components and web development in general is legitimate and based on concrete motivations, which are weighted by his own preferences. On the other hand, his usage of terms like "wrong way" or "anti-pattern" expresses way more than his personal opinions and leans towards unwarranted absolutism.

In short: always take your context and use cases into consideration before rejecting a pattern, library, framework or any other technology.

Top comments (3)

Collapse
 
jonomacd profile image
Jono MacDougall

I largely agree with you but I will say that there is almost always a way to screw things up with whatever tools are given. When people say "anti-pattern" or "wrong way" to me that aligns with methodologies that aren't inherently bad but that make it easier to screw things up. Those methods can be useful and powerful but they open the door to problems if not done right.

I can understand why people think JS and shadow DOMs fall into that camp. The more they are relied on the more we increase complexity and provide a lot of avenues for disaster and bloat creep.

Of course, nothing is immune from this problem. But some methods are less susceptable than others.

Collapse
 
maxart2501 profile image
Massimo Artizzu

That indeed is the core of the matter: if something makes you easily fall into bad patterns, then it's basically an anti-pattern, right?

I think that, at this point, it's a matter of defining what "easily" means. The gist would be that, basically, a scripting language on the web frontend shouldn't exist at all. Would that be acceptable?

Maybe there's a way so that we can render its usage sensible and not a bad pattern.

Collapse
 
dannyengelman profile image
Danny Engelman

If a pattern works it can never be an anti-pattern. Only developers who have never explored all options will tell others not to use them.

Image description