1

At the moment in the <head> of my site I have

<script>
    document.write('<style>.jshide{display:none},.jsshow{display:block},.jsblank{visibility:hidden}</style>')
</script>

The idea is that some elements on my site I don't want to display if JS is enabled. For example, maybe a form submit button, because if JS is enabled the form works with an ajax dropdown instead.

I could hide them programmatically with JS script once the page has loaded, but that leads to frustrating flickering etc.

I've had this for getting on for a decade though, and I wonder if there's a better way to achieve the same end goal in these more modern times?

Thanks

Hyyan Abo Fakher
  • 3,207
  • 3
  • 18
  • 32
Codemonkey
  • 3,681
  • 4
  • 34
  • 68

4 Answers4

3

You should not use document.write. Instead, use

var style = document.createElement('style');
style.textContent = '.jshide{display:none}, .jsshow{display:block}, .jsblank{visibility:hidden}';
document.querySelector('head').appendChild(style);

Aside from that, your approach is perfectly fine.

connexo
  • 41,035
  • 12
  • 60
  • 87
  • Out of curiosity, why not use `document.write`? – Nick Aug 24 '18 at 07:38
  • @Nick https://stackoverflow.com/questions/802854/why-is-document-write-considered-a-bad-practice – connexo Aug 24 '18 at 07:39
  • Thanks for the link. It seems there are pros and cons (much like everything! :) – Nick Aug 24 '18 at 07:41
  • I'm going to just go with this approach, since the only thing I didn't really like about my document.write approach was that lighthouse was flagging it up as being an issue, when it wasn't. Thanks – Codemonkey Aug 28 '18 at 12:39
1

The progressive enhancement way would be to start with

<html class="no-js">

and then remove that class with javascript:

<head>
  <!-- your stuff -->
  <script>
    document.querySelector('html').classList.remove('no-js')
  </script>
</head>

you can then style your elements depending on that class.

example:

html:not(.no-js) .jshide {
   display: none;
}
html:not(.no-js) .jsshow {
   display: block;
}
html:not(.no-js) .jsblank {
   visibility: hidden;
}

You could also do the opposite and add a js-enabled class (instead of removing a negative - where you then need to think in double negatives). That would then be like:

<html>
  <head>
    <!-- your stuff -->
    <script>
      document.querySelector('html').classList.add('js-enabled')
    </script>
  </head>
  ...

in your css:

html.js-enabled .jshide {
   display: none;
}
html.js-enabled .jsshow {
   display: block;
}
html.js-enabled .jsblank {
   visibility: hidden;
}

It essentially does the same thing you do (toggling styles), except that this way you're not touching the DOM (or at least you're not adding new elements to it) and you keep your CSS in your CSS file.

Roman
  • 5,300
  • 20
  • 40
  • I've been doing it like this many times, and it leads to the mentioned flickering :( – connexo Aug 24 '18 at 07:47
  • you put the script into your ``tag, the class is removed from the html tag before your content is painted. This means there is no flickering. – Roman Aug 24 '18 at 07:55
  • I've not tried it yet Roman, but upon reading your code I assumed that that's right, there would be no flickering. Can you confirm that you've been calling it from further down the page, @connexo ? – Codemonkey Aug 24 '18 at 08:03
  • @Codemonkey That might have been the reason; I usually have a frontend asset building stack which comprises all JS into a single bundle (which for performance reasons usually is included right before the closing body tag). – connexo Aug 24 '18 at 08:07
0

As a previous (now deleted) answer mentioned, probably the more idiomatic way of doing this is with <noscript> and <style> elements. To cleanly do it though, it'd require a rewrite of your CSS to handle the case where scripts aren't loaded instead of the case where it is loaded. For something quick, you can overwrite the classes for js-only elements, like this:

<head>
  <style>
    .jshide { display: none }
    .jsshow { display: block }
    .jsblank { visibility: hidden }
  </style>
  <noscript>
    <style>
       .jshide { display: block }
       .jsshow { /* ?? */ }
       .jsblank { visibility: initial }
     </style>
   </noscript>
</head>
Steve
  • 5,560
  • 9
  • 13
0

The HTML tag <noscript> has been thought exactly for this matter. Its content will show only if the user disabled scripts or if the browser does not support them. It is allowed in <body>, and <head> since html5.

Using this has benefits over most other answers provided, as it comes directly from HTML specifications, and is supported by all browsers. Since it is native, there is no "patch" to be done. Hiding your elements when javascript is enabled will cause flicker between the time the page is rendered and the time the javascript is downloaded/ran (there are some ways to circumvent most of this but still...).

Another thing I do not like about using javascript to hide elements for non-script users means that everyone who use script are in fact penalized for it in some sort, as it creates overhead for them.

Salketer
  • 11,628
  • 2
  • 23
  • 56