Of course, Cookies and HTTP headers are great solutions, but both would require explicit server side involvement.
For simple sites, or where I don't have backend access, I prefer client side solutions.
--
I use the following to set a class attribute to the HTML element itself, so my CSS can handle pretty much all other display type logic.
METHODS:
1) place <script>document.getElementsByTagName('html')[0].classList.add('js-enabled');</script>
above the <html>
element.
WARNING!!!!
This method, will override all <html>
class attributes, not to mention may not be "valid" HTML, but works in all browsers, I've tested it in.
*NOTES: Due to the timing of when the script is run, before the <html>
tag is processed, it ends up getting an empty classList collection with no nodes, so by the time the script completes, the <html>
element will be given only the classes you added.
2) Preserves all other <html>
class attributes, simply place the script <script>document.getElementsByTagName('html')[0].classList.add('js-enabled');</script>
right after the opening <html>
tag.
In both cases, If JS was disabled, then no changes to the <html>
class attributes will be made.
ALTERNATIVES
Over the years I've used a few other methods:
<script type="text/javascript">
<!--
(function(d, a, b){
let x = function(){
// Select and swap
let hits = d.getElementsByClassName(a);
for( let i = hits.length - 1; i >= 0; i-- ){
hits[i].classList.add(b);
hits[i].classList.remove(a);
}
};
// Initialize Second Pass...
setTimeout(function(){ x(); },0);
x();
})(document, 'no-js', 'js-enabled' );
-->
</script>
// Minified as:
<script type="text/javascript">
<!--
(function(d, a, b, x, hits, i){x=function(){hits=d.getElementsByClassName(a);for(i=hits.length-1;i>=0;i--){hits[i].classList.add(b);hits[i].classList.remove(a);}};setTimeout(function(){ x(); },0);x();})(document, 'no-js', 'js-enabled' );
-->
</script>
- This will cycle through the page twice, once at the point where it is in the page, generally right after the
<html>
and
once again after page load. Two times was required, as I injected into a header.tpl file of a CMS which I did not have backend access to, but wanted to present styling options for no-js snippets.
The first pass, would set set the .js-enabled class permitting any global styles to kick in, and prevented most further reflows. the second pass, was a catchall for any later included content.
REASONINGS:
The main reasons, I've cared if JS was enabled or not was for "Styling" purposes, hide/show a form, enable/disable buttons or restyle presentation and layouts of sliders, tables, and other presentations which required JS to function "correctly" and would be useless or unusable without JS to animate or handle the interactions.
Also, you can't directly detect with javascript, if javascript is "disabled"... only if it is "enabled", by executing some javascript, so you either rely on <meta http-equiv="refresh" content="2;url=/url/to/no-js/content.html" />
or you can rely on css to switch styles and if javascript executes, to switch to a "js-enabled" mode.