Please Tell Users When Your Application “Isn’t For Them”

I usually dislike how people are throwing JavaScript at things that shouldn’t be scripted; railing against its use in places where HTML and CSS are more than capable of handling things on their own. As I’m always saying:
Quality JavaScript enhances an already working page, and should not be the only means of providing functionality.
This notion holds up well anyplace where accessibility matters. If you’re working for government agencies, public utilities, medical, or even retail it is very important for usability and accessibility reasons that scripting off/blocked people can still do things like access records, information, accounts, or make purchases. This is because many non-visual UA’s — user agent, a browser is a UA but a UA isn’t always a browser — don’t provide scripting and/or your scripted functionality might be just plain gibberish. Likewise many workplaces actively block or disable scripting on employee’s machines out of a complete distrust of the technology.
Whether or not that distrust is legacy from ActiveX or just plain paranoid fiction matters not. People do it.
But there are a great many things — calculation tools, code generation,. mapping apps, games — where without JavaScript there is no practical way to implement them. In those cases it’s scripting or nothing. I take no issue with stuff like that being scripting based.
No, the problem is how many such places are too lazy to put a bloody
<noscript>
<p>
This page requires JavaScript to function. Please revisit this site with scripting enabled or in a JavaScript compatible browser.
</p>
</noscript>The simple fact that so many applications feed broken pages, or even just blank white pages of nothing due to this simple omission? That’s just plain lazy and stupid. There are plenty of mechanisms for trapping that your modern fancy scripts aren’t going to work, and to tell the user why. DO IT!
But It’s Not Just JavaScript!
I have written at length about media targets, where using the “media” attribute on your stylesheet tag lets you make things like “style that only applies to screen” only be loaded for screen.
That so many developers do not plan for handling that omitting the attribute can not only result in broken pages, broken navigation (see display:none), and sending screen media concepts at non-screen devices that just don’t give a damn, is just plain horrifying. As I’ve said time and time again, if you see
And yes I realize that means 90% or more of all websites.
But what about your scripted applications where without there being a screen, it’s useless? Mapping applications, paint programs, etc, etm. are not “usable” on print… or speech… sure as shine-ola are a waste of time to even try and use on a braille reader.
HTML miracle of miracles has actually given us an answer to that. Let’s take that noscript from above, and expand it.
<div id="loadErrors">
<noscript>
<p>
This page requires JavaScript to function.
Please revisit this site with scripting enabled
or in a JavaScript compatible browser.
</p>
</noscript>
<p class="nonScreen">
This page is only compatible with screen media targets.
Sorry print, speech, and braille users, but I'm not
sure how you think you'd think this application
would work on your device.
</p>
<!-- #loadErrors -->Then if like a good little doobie you’re loading your CSS properly:
<link
rel="stylesheet"
href="template/screen.css"
media="screen"
>Do this in your stylesheet:
.nonScreen {
display:none;
}Said message will be delivered to everything except screen.
But What About Scripting Itself?
Sadly the <script> tag has no media attribute. I consider that a pretty big failure in the specification, but we do have the window.matchmedia method. What we can do is in a script tag load our scripts based on if windows.matchMedia("screen").matches thus:
<script>
if (matchMedia && matchMedia("screen").matches) {
for (let attr of [
{ src : "firstScript.js" },
{ src : "secondScript.js", async : true }
]) document.body.appendChild(
Object.assign(
document.createElement("script"),
attr
)
);
}
</script>Does window.matchMedia exist? Yes are we in a screen media browser? Yes? Load our scripts via the DOM
Now, there is a problem with matchMedia, it will never be false for “print” when printing from a browser as at the time of script execution you’re on screen. Even though the media=”screen” stylesheet WILL be ignored during print output. Just as it’s ignored for screen readers and braille readers.
It is to that end that your
main { display:block; }Making the dynamic content show for screen, but not print/aural/braille/etc even if your scripting has filled it to the brim.
Boom, if it’s not screen media, those other scripts won’t load or run. I suggest doing this right at the bottom of your HTML before as/where any quality modern scripting should.
For those of you who never heard about it, loading them in your document is slow, blocking, and problematic. A lot of the crazy hoops people jump through like “onload” or modules or IIFE can be avoided by just loading scripts last in the document body, and leveraging let/const’s scoping.
If you’re really concerned about print not showing the contents of MAIN, I would suggest trapping the “onprintstart” event and generating a print specific section to be shown. That goes above/beyond the scope of this article.
Can We Tell Internet Explorer To Kiss Off Too?
Let’s face it. How many of us writing scripting only applications are writing code IE is even capable of running. Lands sake a lot of our code doesn’t even run on browsers less than five years old now as the improvements offered by ECMAScript over the past decade are too advantageous to ignore. Oh noes somebody who won’t update to the newest browser or an OS made within the past DECADE can’t run our web facing crapplet. The horrors…
Thankfully for IE 5 through 10, Microsoft gave us a mechanism we can use to handle this:
Conditional Comments
This provides two mechanisms that if we format right with additional comments, IE can be told to ignore certain HTML, or ignore certain HTML. To ignore:
<!--[if !IE]>-->
code for IE to ignore and all other browsers obey here
<!--<![endif]-->and for IE 5 to 10 only code:
<!--[if IE]>
This will only show in IE 5 to 10, ignored by all other browsers
<![endif]-->So for our CSS we could just:
<!--[if !IE]>-->
<link
rel="stylesheet"
href="template/screen.css"
media="screen"
>
<!--<![endif]--> Note that if you wanted to go the extra mile to style what little content is present for IE, you could:
<!--[if IE]>
<link
rel="stylesheet"
href="template/ieOnly.screen.css"
media="screen"
>
<![endif]-->Then for the scripting:
<!--[if !IE]>-->
<script>
if (matchMedia && matchMedia("screen").matches) {
for (let script of [
{ src : "firstScript.js" },
{ src : "secondScript.js", async : true }
]) document.body.appendChild(
OBject.assign(
document.createElement("script"),
script
)
);
}
</script>
<!--<![endif]-->Then for the error message itself?
<div id="loadErrors">
<noscript>
<p>
This page requires JavaScript to function.
Please revisit this site with scripting enabled
or in a JavaScript compatible browser.
</p>
</noscript>
<p class="nonScreen">
This page is only compatible with screen media targets.
Sorry print, speech, and braille users, but I'm not
sure how you think you'd think this application
would work on your device.
</p>
<!--[if IE]>
<p>
This page is not compatible with Internet Explorer.
Please get a browser made in this century!
</p>
<![endif]-->
<!-- #loadErrors -->This works in IE 5 through 10… but what about 11?
X-UA-Compatible To The Rescue
One feature of this META data is that you can make IE behave like older versions by setting a number. So if we set this in our <head>
<meta http-equiv="X-UA-Compatible" content="IE=9">Internet Explorer 10 and newer will behave like IE9, giving you back your conditional comments.
Sadly this is actually considered invalid HTML 5 because the W3C and WhatWG went and took a steaming dump on that by:
- Making the X-UA-Compatible META part of the specification even though it was browser proprietary and only ever supported by one browser engine, in complete defiance of their own rules about what can go into the specification for validation. THEIR OWN RULES say that to be in the deployment version of the spec it needs to be supported by at least TWO browser engines. As such if you’re not supporting IE, don’t set that
- Declaring the only valid value for it is “edge”, neutering its actual functionality and indeed most of its purpose. Being that it is a browser proprietary HTTP header — yes, header, if it matters you should be setting it on the server, not in your markup — the value sent to it really shouldn’t be any of HTML’s business on those grounds either!
The “edge” value — contrary to what dipsticks who know not a damned thing about IE think — does not magically make IE behave like Chakra or Chromium based Edge. That there are people out there who are genuinely convinced that’s what it does? Well, never underestimate the stupidity of neurotypicals. There’s not a single blasted thing you can do in a HTTP header to make a browser support code newer than the browser itself!
So what does it actually do? Microsoft used to have an automated and site owner submitted list of websites that didn’t work right in IE 10 and 11, and would use emulation of legacy browsers based on what was reported. If you wrote a modern site that was fully 10/11 capable but your domain was on the blacklist, you’d set it to “edge” to tell those versions of IE to ignore the list and run it “properly”. In a lot of ways this is like the viewport META being used to tell mobile devices we know what we’re doing.
Which is not all that blasted useful when most of us aren’t even supporting IE anymore and our pages thanks to flex, grid, and so forth are not IE compatible!
— edit — When I last tested this it was a warning level event, not an actual error. They appear to have changed that so it’s now an actual error. Seriously, this is why control over the future of web specifications needs to be wrested from the W3C’s hands. — end edit —
As a HTTP header it’s something that should be NONE of the flipping HTML specification or validation services business in the first blasted place! Why?
Because <meta> exists for the sole purpose of users and vendors declaring any damned data and values they want. Apart from the character set META, what content is valid for a vendor specific metadata should be entirely at the whim of that vendor, NOT THE HTML SPEC. THAT’S WHAT THEY’RE FLIPPING FOR!!!
So it’s invalid during validation. You know what I say to that?
F*** the W3C and the source they code in on!
<broken record>
<p>
This is — alongside numb-nut derpitude like the living
document malarkey — why it feels like the W3C wants to
make HTML validation as meaningless as CSS validation
was 15 years ago, and why the WhatWG seems to have been
filled with people unqualified to make HTML 4 Strict’s
successor.
</p>
</broken>Go ahead and use it. The W3C has gone full Gungan.

Testing For ECMAScript Versions
Finally “modern” JavaScript of the past decade has added all sorts of functionality older browsers can’t handle. With browser updates being free, browsers being free, and so forth you’d think this would/should be a non-issue.
But of course it’s still a problem. A lot of phone vendors don’t release updates for their browsers on older devices. Some people are poor and stuck on old outdated versions of their OS that won’t run newer browsers. Sure it’s easy to say “well screw those people” but nothing worth doing right is easy.
Thus it wouldn’t hurt to use “feature detection” to figure out what version of ECMAScript / JavaScript is present. The following conditions generally line up with the supported browsers and ECMAScript versions:
"function" == typeof BigInt // ES 2020
"function" == typeof Object.fromEntries // ES 2019
Promise && ("function" == typeof Promise.prototype.finally) // ES 2018
"function" == typeof Object.entries // ES 2017
"function" == typeof Array.prototype.includes // ES 2016
Number.EPSILON // ES 2015
"function" == typeof Array.isArray // JS 5
"function" == typeof RegExp // JS 3Some people would hardcode an entire detection function around that, but it’s your codebase, you should know what version you need. Thus I would pick the newest one / set of features your codebase uses, and use just that one check.
As we already have a script loader and error box, we can just integrate this into those conditions. Except for one problem…
Legacy Browsers Can’t Do Modern Scripting!
The very thing we’re testing for means the code I presented above for JS can’t be used. Const? Let? Arrow functions? For..of?

So tonight we’re gonna code like it’s 1999.
<!--[if !IE]>-->
<script>
/*
This entire script NEEDS to be written to 1999
standards to prevent it crashing in older UA's.
*/
(function(d) {
var
loadErrors = d.getElementById("loadErrors");
scripts = [
{ src : "scripts/app.js" }
];
function loadErrorsAdd(text) {
loadErrors.appendChild(
d.createElement("p")
).textContent = text;
}
if ("function" != typeof BigInt) { // Tests for ES 2020
// for other tests see ECMATests.txt
loadErrorsAdd("\
This page requires at least ECMAScript 2020 to function.\
Please return in a compatible browser.\
");
} else if (matchMedia && matchMedia("screen").matches) {
for (var i = 0, script; script = scripts[i]; i++) {
var e = d.createElement('script');
for (var key in script) e[key] = script[key];
d.body.appendChild(e);
}
}
} )(document);
</script>
<!--<![endif]-->I almost feel dirty after writing that. Couldn’t even use my beloved Object.assign
Hooking the div#loadErrors and adding a loadErrorAdd function simplifies expanding it to support more errors / detection states. Indeed you may have other initialization checks you’ll want to run when web facing. The above checks for each ECMAScript version is by no means complete as sometimes some features got implemented in some browsers before others. You also sometimes have to look for an “equivalent” code as some things — arrow functions, spread operators, etc. — cannot be simply detected by an IF statement. “CanIUse” or MDN’s compatibility charts are your friend in that case, as you can look for something implemented at the same time or after those features in all browsers, and test for that instead.
Because this would be — almost by demand — in the markup right before I used the IIFE — our only legacy means of scope isolation — and pass “document” to it as “d” to save bandwidth and to speed execution. (local var faster than global object).
The scripts array contains all the attributes for the script tags you want to load when/if we’re successfully “modern” enough.
If it’s not ES2020 compatible, throw the message, otherwise load our scripts from the array. That “for” loop method might be unfamiliar to some of you, but with JavaScript like many C syntax languages returning value on assignment, and with an array access past the last index returning null — a loose false — that is in fact one of the fastest executing ways to zip through an Array of Objects.
The laugh being that scripting is compatible — to my knowledge — all the way back to Exploder 5 and Nyetscape 4.
Putting It All Together
That’s enough for us to make a simple boilerplate that tells the users we aren’t supporting WHY they’re not supported, as opposed to the hot and trendy way of showing either a completely broken page, or a white page of nothingness.
I’ve built such a thing in this directory: https://cutcodedown.com/for_others/medium_articles/webAppBoilerplate/
You can open up the template.html to test it, or grab the .rar of the whole shebang to play with, use it as you like, expand upon, etc, etm.
That would/should be a far better ‘starting place” for any decent web app than the rather enfeebled way most such pages and sites are built. If you disagree with that, see problems, or have suggestions to make it better, let me know as I’d like to try and maintain this.
I’m also starting a new project (canvas driven) where this is my baseline. Something I think most apps should have before they even start playing with plugging in content scripting.
Conclusion
It kind of sucks telling users “this isn’t for you because of your Browser or other limitations” but when it comes to web apps that is indeed often the case. That’s not saying there’s no place for web apps, but it does mean that they are utterly, completely, and ridiculously unsuited for use anywhere accessibility matters.
Thus if you cross that line, and tell those users to sod off because they just can’t use what you’re making, at least have the common decency to leave them a message and not try to run your scripting. Beats the tar out of a half-rendered broken mess or a giant blank slate of nothingness.
Semi Unrelated Side Note
I just want to remind people that there is no such thing as:
<div />Because DIV is NOT an “empty / void / whatever the W3C is calling it this week” tag! Only tags from HTML 4 that do not have closing tags can use the /> declaration. What you have there is an unclosed
I just want to mention that because you see React, Angular, Vue, and other front-end idiocy — or at least the marks suckered into using them — doing it all the blasted time. No, it’s not valid code, Yes, it can lead to problems.
And no, it didn’t exist in XHTML either. STOP DOING THAT!





