My main beef with JS front ends is that they often break normal HTML behavior. Eg, an HTML link is easy to open in a new tab, but JS-powered links often aren’t. I’ve seen a banking site where it’s impossible to open a statement in a new tab or to use the back button after you’ve printed one.
It’s probably possible not to break normal web behavior with a JS front end, but it’s pretty hard to break it without one. I’d gladly lose a few well-done PWAs if I could lose all the half-baked ones.
“dev.to” is garbage - they have a fixed top and bottom bar
What really pisses me off is that they used to only have the top bar (which, since they have infinite scrolling on the home page, does kind of make sense). They added the bottom bar after I registered, making the reading experience worse… The sign up link didn’t use to animate, either. What the hell happened to TPD?! It was always a bit fancy, but it got worse around the time they OSS’ed it.
Why have the UI options for the HTML spec stagnated and left it up to building custom JavaScript-driven elements? I think having a more robust set of standard UI elements is way more important than WebVR, WebBluetooth, or whatever other insanity they’re cooking up these days.
This is the real take-home, in my opinion. All of the things the author mentions are things that should be part of the HTML spec. A “pave the cowpaths” approach to page interactions would make it easy to gradually but eventually completely remove javascript from the web.
Ignoring the annoyance and irony of an article advocating nice JS-less websites on an irritatingly glitzy and over-fancy platform, it’s a nice trick that I should use. Currently I have a site that needs this functionality but semi-abuses the HTML details tag to get it. I’ve found references to the input toggle trick, but none that actually demonstrate it as nicely as speps’s demo.
Original rant below:
Ironic, the pulsing “join dev.to” done in very nicely animated CSS on the right side of the screen… never turns off.
Congrats, you’ve reinvented the blink tag. Now please stop.
Thank Eris for my misspent time doing a little bit of actual web dev. Firefox: right click, select “inspect element”, navigate to the DOM element in the inspector that looks like it covers the right amount of space, delete it. Problem solved. (Works on “I accept cookies to view this site” popups too.)
Edit: Oh, on the main page it has an infinite timeline that loads in as you scroll to the bottom… so I have to go to a different page in the site to actually be able to click the “about” link or such on the bottom. Good job guys. At least they have a bug tracker for me to complain at.
“dev.to” is garbage - they have a fixed top and bottom bar
When I go to any news or blog-style site, I’m generally prepared for the worst. Even with an ad blocker, I’m generally hounded by auto-play html5 video, various widgets flitting around the screen, “subscribe to our newsletter!” pop-ups, and as you note, fixed nav bars.
As a result, these days when I go to sites like dev.to, I find myself clicking on Firefox’s built-in reader view before even scrolling down.
Unfortunately a lot of these CSS-based tricks are pretty terrible in terms of accessibility. Granted, a lot of JS-heavy websites tend to be pretty poor when it comes to accessibility, but that’s largely a result of it being a low priority, rather than any technical limitations of JS-based web apps.
When this topic is brought up, I feel like there are many that will comment about how JSON is faster. Or it’s only slow on first load and browser without reloads and caches everything else is faster, or another excuse I’ve read is that if done right… you wouldn’t have these SPA issues people bring up (the links, tabs, back/forward buttons, refreshes, etc…), but isn’t that basically requiring the SPA developers to re-develop all the very basic, and free, functionality that a browser comes with? Also, it’s so easy for some tech-head to just right click, inspect, and get around your lame client side validations or some other wacky issue that prevents me from clicking submit. (Which I have actually ended up having to do on some sites.)
My favorite way I do a web page, app, whatever the politically correct contemporary term is that won’t offend the full time front end react and vue developers or bootcampers or javascripters, is to just not develop it initialially with javascript mostly. Then go through it, polish perhaps, and do my extra fancy CSS stuff, then go over it again, with a second layer, and throw in my JavaScript (sprinkles?) and make it a little richer. Basically where a page is like an app or in Rails world speak I just render partials on the server and use JavaScript responses and do a simple document.querySelector(‘element’).innerHTML = mypartial.
The problem comes down to complexity: you’re adding another deployment configuration that your app has to support. If you develop a web app that’s supposed to be progressively enhanced, then you have to test it with JavaScript on and with it off. Just as an example: lobste.rs mostly uses server-side template rendering, but some of the buttons don’t work without JavaScript. If @pushcx wanted to fix that, he’d probably end up implementing that functionality twice. You’re basically doubling your test workload.
Worse than that, though, is that the common approaches to enhancement are straight-up spaghetti code. This post gives a pretty good example. And even if you’re smart enough not to take the naive jQuery approach to implement something as complicated as a speculatively-updated Like Button flow, the code that you write when you support JS both on and off has more essential complexity than when you only support the case where JS is on. You’re basically doubling the amount of code paths.
This is why Slimvoice, the JavaScript-free frontend that this lobsters story is about, uses all those complex CSS hacks. Discourse solved the conundrum by making almost all of the functionality JS-only. Slimvoice solved the conundrum by making almost all of the functionality JS-free.
I’m actually working on yet-another-Reddit-clone and trying to figure a good way out of this mess myself. I don’t want to write a SPA; I’ve seen the code under the hood at Discourse, and reimplementing a quarter of the browser’s native functionality myself like they have to do is way too much work. I don’t want buttons that mysteriously do nothing when you go noscript, either. And I certainly don’t want to reload the entire page whenever you star something. You’re not old and moldy, but nothing in your post actually spells out how I can eat my cake without losing it.
I don’t have an answer to everything, but I the following stuck out to me:
The problem comes down to complexity: you’re adding another deployment configuration that your app has to support. If you develop a web app that’s supposed to be progressively enhanced, then you have to test it with JavaScript on and with it off.
I don’t think this is particularly true. If someone’s prepared to ignore all non-JS-users by considering a JS-only site, then I don’t see why they’d have a problem ignoring all non-JS-users of a progressively-enhanced page. It’s similar to a stance like “we don’t support IE”: that doesn’t mean we should sniff user agents and avoid sending pages to IE users. Rather, we send them the page and let them muddle through on their own.
I’ve always done progressive enhancement as a Web dev (I think it’s sloppy and unprofessional not to do so). It’s not hard; it just requires an appropriate mindset and a little experience, then if something works with tables+images+CSS+JS, etc. turned on then it will usually degrade gracefully (similar to the “if it compiles, it works” mantra of statically typed languages: it’s not true in general, we there are approaches we can follow which make it usually true).
I don’t want buttons that mysteriously do nothing when you go noscript, either.
This is a pet peeve of mine: don’t send across HTML that, on its own, is misleading. This applies to buttons which aren’t attached to forms or hyperlinks, but also things like “spinner” gifs which give the impression that something is loading. Anything which requires JS to work, should be added to the page with JS. It’s not hard.
I certainly don’t want to reload the entire page whenever you star something.
That’s the point of progressive enhancement: to enhance, not to avoid implementing. If you want to use JS to implement in-place updates then go ahead. All progressive enhancement would ask is that either you send across a crude non-JS implementation by default (like “reload the entire page”) and “enhance” it with JS on page load; or don’t include that functionality at all in the page that’s sent across. Just don’t send links/buttons that don’t do anything.
Note that this doesn’t require much effort at all. In particular, something like replacing form POSTs with in-place updates doesn’t need two separate implementations of the feature: it’s the sort of thing that JS libraries make trivial, so we just need to e.g. put a certain class and/or data attribute on the button and run a generic function on page load to find/replace any occurrences.
In fact I would claim that “broken button” implementations are probably more complicated and highly coupled than doing it properly, since they spread the “single implementation” across HTML and JS. In contrast the “two implementations” version would have a self-contained HTML version and a self-contained JS version (which may or may not be a single JS library call). The “don’t send any HTML, add it during page load” version is a single self-contained JS implementation.
I don’t think this is particularly true. If someone’s prepared to ignore all non-JS-users by considering a JS-only site, then I don’t see why they’d have a problem ignoring all non-JS-users of a progressively-enhanced page.
If you’re not actually supporting it, you’re doing graceful degradation at best.
Note that this doesn’t require much effort at all. In particular, something like replacing form POSTs with in-place updates doesn’t need two separate implementations of the feature: it’s the sort of thing that JS libraries make trivial, so we just need to e.g. put a certain class and/or data attribute on the button and run a generic function on page load to find/replace any occurrences.
I mean, obviously the database code doesn’t need to be written twice. But most of the frontend logic does.
Going with the like button example, your template pretty much has to do this kind of thing for the non-JS users.
Obviously, I’m using CSS to replace the button text with an image, and after clicking the button, the server sends out a redirect back to the article you voted on.
Now I’m going to deploy a piece of JavaScript, and annotate the button with some info to handle it.
I’m using an imaginary library that takes care of toggling classes, so if star-button-active is present then it gets replaced with star-button-inactive, and vice-versa. It also invokes the form action through XHR, silently swallowing any non-error response that the server gives.
This naive implementation has a severe accessibility bug: the script doesn’t do anything with the alt text. I can fix that bug individually, but you can see how this is a violation of DRY: when the server renders a component that the client then has to update, the two have to be kept in sync. Also, since most users don’t see the server-generated redirect (since the JavaScript hides it), that code can easily bitrot.
As you mentioned, you could just not offer voting for anyone without JavaScript. But if you actually do want to support users with JavaScript disabled, not allowing them to decide which content gets promoted on a news aggregator seems like you’re giving them the middle finger.
My main beef with JS front ends is that they often break normal HTML behavior. Eg, an HTML link is easy to open in a new tab, but JS-powered links often aren’t. I’ve seen a banking site where it’s impossible to open a statement in a new tab or to use the back button after you’ve printed one.
It’s probably possible not to break normal web behavior with a JS front end, but it’s pretty hard to break it without one. I’d gladly lose a few well-done PWAs if I could lose all the half-baked ones.
Finally, an article bashing JavaScript that is actually concrete and interesting!
The input check toggle trick is very nice. Link to demo (from TFA): https://jsfiddle.net/winduptoy/8qfvb1az/
Display: none can break keyboard interaction, but position: absolute -9999px works.
That’s what you think, kiddo.
https://en.wikipedia.org/wiki/Billion_laughs_attack
https://cras.sh/
https://twitter.com/pwnsdx/status/1040944750973595649
What really pisses me off is that they used to only have the top bar (which, since they have infinite scrolling on the home page, does kind of make sense). They added the bottom bar after I registered, making the reading experience worse… The sign up link didn’t use to animate, either. What the hell happened to TPD?! It was always a bit fancy, but it got worse around the time they OSS’ed it.
Those links are fascinating! Thanks for sharing. :)
This is the real take-home, in my opinion. All of the things the author mentions are things that should be part of the HTML spec. A “pave the cowpaths” approach to page interactions would make it easy to gradually but eventually completely remove javascript from the web.
Ignoring the annoyance and irony of an article advocating nice JS-less websites on an irritatingly glitzy and over-fancy platform, it’s a nice trick that I should use. Currently I have a site that needs this functionality but semi-abuses the HTML details tag to get it. I’ve found references to the input toggle trick, but none that actually demonstrate it as nicely as speps’s demo.
Original rant below:
Ironic, the pulsing “join dev.to” done in very nicely animated CSS on the right side of the screen… never turns off.
Congrats, you’ve reinvented the blink tag. Now please stop.
Thank Eris for my misspent time doing a little bit of actual web dev. Firefox: right click, select “inspect element”, navigate to the DOM element in the inspector that looks like it covers the right amount of space, delete it. Problem solved. (Works on “I accept cookies to view this site” popups too.)
Edit: Oh, on the main page it has an infinite timeline that loads in as you scroll to the bottom… so I have to go to a different page in the site to actually be able to click the “about” link or such on the bottom. Good job guys. At least they have a bug tracker for me to complain at.
thank you for all of this!
the input check toggle is nice - but they basically reinvented the “details” tag as you said
“dev.to” is garbage - they have a fixed top and bottom bar
When I go to any news or blog-style site, I’m generally prepared for the worst. Even with an ad blocker, I’m generally hounded by auto-play html5 video, various widgets flitting around the screen, “subscribe to our newsletter!” pop-ups, and as you note, fixed nav bars.
As a result, these days when I go to sites like dev.to, I find myself clicking on Firefox’s built-in reader view before even scrolling down.
I use a 1366x768 screen. The fixed headers and footers on some websites are completely unbearable, esp when mixed with narrow main body text.
Plugging my own key-shortcut-to-disable-fixed-elements FF addon: https://addons.mozilla.org/en-US/firefox/addon/margin_annihilator/
ah yes - i do that too on mobile - sometimes forget on desktop
however in this case it seems “dev.to” even found a way to mess that up
the github twitter icons are giant in reader mode
god that website is just a steaming pile of crap
It’s not my demo, it’s the link from the article as I said.
Unfortunately a lot of these CSS-based tricks are pretty terrible in terms of accessibility. Granted, a lot of JS-heavy websites tend to be pretty poor when it comes to accessibility, but that’s largely a result of it being a low priority, rather than any technical limitations of JS-based web apps.
Noice!!!
When this topic is brought up, I feel like there are many that will comment about how JSON is faster. Or it’s only slow on first load and browser without reloads and caches everything else is faster, or another excuse I’ve read is that if done right… you wouldn’t have these SPA issues people bring up (the links, tabs, back/forward buttons, refreshes, etc…), but isn’t that basically requiring the SPA developers to re-develop all the very basic, and free, functionality that a browser comes with? Also, it’s so easy for some tech-head to just right click, inspect, and get around your lame client side validations or some other wacky issue that prevents me from clicking submit. (Which I have actually ended up having to do on some sites.)
My favorite way I do a web page, app, whatever the politically correct contemporary term is that won’t offend the full time front end react and vue developers or bootcampers or javascripters, is to just not develop it initialially with javascript mostly. Then go through it, polish perhaps, and do my extra fancy CSS stuff, then go over it again, with a second layer, and throw in my JavaScript (sprinkles?) and make it a little richer. Basically where a page is like an app or in Rails world speak I just render partials on the server and use JavaScript responses and do a simple document.querySelector(‘element’).innerHTML = mypartial.
Am I old and moldy?
The problem comes down to complexity: you’re adding another deployment configuration that your app has to support. If you develop a web app that’s supposed to be progressively enhanced, then you have to test it with JavaScript on and with it off. Just as an example: lobste.rs mostly uses server-side template rendering, but some of the buttons don’t work without JavaScript. If @pushcx wanted to fix that, he’d probably end up implementing that functionality twice. You’re basically doubling your test workload.
Worse than that, though, is that the common approaches to enhancement are straight-up spaghetti code. This post gives a pretty good example. And even if you’re smart enough not to take the naive jQuery approach to implement something as complicated as a speculatively-updated Like Button flow, the code that you write when you support JS both on and off has more essential complexity than when you only support the case where JS is on. You’re basically doubling the amount of code paths.
This is why Slimvoice, the JavaScript-free frontend that this lobsters story is about, uses all those complex CSS hacks. Discourse solved the conundrum by making almost all of the functionality JS-only. Slimvoice solved the conundrum by making almost all of the functionality JS-free.
I’m actually working on yet-another-Reddit-clone and trying to figure a good way out of this mess myself. I don’t want to write a SPA; I’ve seen the code under the hood at Discourse, and reimplementing a quarter of the browser’s native functionality myself like they have to do is way too much work. I don’t want buttons that mysteriously do nothing when you go noscript, either. And I certainly don’t want to reload the entire page whenever you star something. You’re not old and moldy, but nothing in your post actually spells out how I can eat my cake without losing it.
I don’t have an answer to everything, but I the following stuck out to me:
I don’t think this is particularly true. If someone’s prepared to ignore all non-JS-users by considering a JS-only site, then I don’t see why they’d have a problem ignoring all non-JS-users of a progressively-enhanced page. It’s similar to a stance like “we don’t support IE”: that doesn’t mean we should sniff user agents and avoid sending pages to IE users. Rather, we send them the page and let them muddle through on their own.
I’ve always done progressive enhancement as a Web dev (I think it’s sloppy and unprofessional not to do so). It’s not hard; it just requires an appropriate mindset and a little experience, then if something works with tables+images+CSS+JS, etc. turned on then it will usually degrade gracefully (similar to the “if it compiles, it works” mantra of statically typed languages: it’s not true in general, we there are approaches we can follow which make it usually true).
This is a pet peeve of mine: don’t send across HTML that, on its own, is misleading. This applies to buttons which aren’t attached to forms or hyperlinks, but also things like “spinner” gifs which give the impression that something is loading. Anything which requires JS to work, should be added to the page with JS. It’s not hard.
That’s the point of progressive enhancement: to enhance, not to avoid implementing. If you want to use JS to implement in-place updates then go ahead. All progressive enhancement would ask is that either you send across a crude non-JS implementation by default (like “reload the entire page”) and “enhance” it with JS on page load; or don’t include that functionality at all in the page that’s sent across. Just don’t send links/buttons that don’t do anything.
Note that this doesn’t require much effort at all. In particular, something like replacing form POSTs with in-place updates doesn’t need two separate implementations of the feature: it’s the sort of thing that JS libraries make trivial, so we just need to e.g. put a certain class and/or data attribute on the button and run a generic function on page load to find/replace any occurrences.
In fact I would claim that “broken button” implementations are probably more complicated and highly coupled than doing it properly, since they spread the “single implementation” across HTML and JS. In contrast the “two implementations” version would have a self-contained HTML version and a self-contained JS version (which may or may not be a single JS library call). The “don’t send any HTML, add it during page load” version is a single self-contained JS implementation.
If you’re not actually supporting it, you’re doing graceful degradation at best.
I mean, obviously the database code doesn’t need to be written twice. But most of the frontend logic does.
Going with the like button example, your template pretty much has to do this kind of thing for the non-JS users.
Obviously, I’m using CSS to replace the button text with an image, and after clicking the button, the server sends out a redirect back to the article you voted on.
Now I’m going to deploy a piece of JavaScript, and annotate the button with some info to handle it.
I’m using an imaginary library that takes care of toggling classes, so if star-button-active is present then it gets replaced with star-button-inactive, and vice-versa. It also invokes the form action through XHR, silently swallowing any non-error response that the server gives.
This naive implementation has a severe accessibility bug: the script doesn’t do anything with the alt text. I can fix that bug individually, but you can see how this is a violation of DRY: when the server renders a component that the client then has to update, the two have to be kept in sync. Also, since most users don’t see the server-generated redirect (since the JavaScript hides it), that code can easily bitrot.
As you mentioned, you could just not offer voting for anyone without JavaScript. But if you actually do want to support users with JavaScript disabled, not allowing them to decide which content gets promoted on a news aggregator seems like you’re giving them the middle finger.
For the filter list request, there is
<input list="id">
which… kinda works, but it isn’t great. As for the rest, oh, I agree completely!An example to take after. Loving it!