Search code examples
javascripthtmlnoscript

What purpose does a <script> tag serve inside of a <noscript> tag?


I've been on a "view source" spree lately on websites with interesting design and content. One of those websites, Squarespace, has blocks of <script> tags inside of a <noscript> tag, like so:

<!-- Page is at: http://squarespace.com -->
...
...
<noscript id="inline-deps">
  <link rel="stylesheet" type="text/css" href="//cloud.typography.com/7811972/758964/css/fonts.css" />

  <script type="text/javascript" src="https://static.squarespace.com/static/ta/5134cbefe4b0c6fb04df8065/7400/assets/logomark/logomark.min.js?37"></script>
  <link rel="stylesheet" href="https://static.squarespace.com/static/ta/5134cbefe4b0c6fb04df8065/7400/assets/logomark/logomark.min.css?37" type="text/css" />
</noscript>
...
...

It struck me as odd, and got me googling for info to see if there's some kind of hidden functionality/purpose for such an odd bit of HTML, but to no avail. Is there some kind of purpose to having <script> tags inside of <noscript> elements, or is this just an example of bad HTML?


Solution

  • I did some searching through their code and found this snippet (I've cleaned it up to make it more readable):

    var DepLoader = (function () {
      function init() {
        var dependencies = document.getElementById("inline-deps");
        if (!dependencies || JS.hasClass(document.body, "deps--loaded")) {
          webfontsReady();
        } else {
          var html = dependencies.innerText || dependencies.textContent;
          JS.addClass(document.body, "deps--loaded");
          processRaw(html);
        }
      }
    
      function isListed(a, b) {
        for (var i = 0; i < b.length; i++) {
          if (a.indexOf(b[i]) !== -1) {
            return true;
          }
        }
        return false;
      }
    
      function webfontsReady() {
        JS.fireCustom("webfontsReady");
      }
    
      function processRaw(html) {
        var el = document.createElement("div");
        el.innerHTML = html;
    
        var scripts = el.querySelectorAll("script");
        var styles = el.querySelectorAll("link");
        var common, signup, dialog, systemPage, commerce;
        var others = [];
        var inline = [];
        var styleWhiteList = ["site.css", "dialog-", "signup-", "logomark"];
        var scriptBlackList = ["management-", "ckeditor-"];
    
        for (var i = 0; i < styles.length; i++) {
          var style = styles[i];
          if (style.href.indexOf("fonts.css") !== -1) load(style, webfontsReady);
          if (isListed(style.href, styleWhiteList)) load(style);
        }
    
        for (var i = 0; i < scripts.length; i++) {
          var script = scripts[i];
          var src = script.src;
    
          if (!src && script.getAttribute("data-sqs-type") !== "dynamic-assets-loader" && script.innerHTML.indexOf("SQUARESPACE_ROLLUPS") === -1) {
            eval(script.innerHTML);
          }
        }
    
        if (window.SQUARESPACE_ROLLUPS) {
          for (var key in SQUARESPACE_ROLLUPS) {
            var rollup = SQUARESPACE_ROLLUPS[key];
            var js = rollup.js;
            var css = rollup.css;
    
            if (key.indexOf("common") !== -1) {
              common = js;
            } else if (key.indexOf("commerce") !== -1) {
              commerce = js;
            } else if (key.indexOf("signup") !== -1) {
              signup = js;
            } else if (key.indexOf("dialog") !== -1) {
              dialog = js;
            } else if (key.indexOf("system-page") !== -1) {
              systemPage = js;
            } else if (key) {
              others = others.concat(js);
            } else {
              inline = inline.concat(js);
            }
          }
        }
    
        for (var i = 0; i < scripts.length; s++) {
          var script = scripts[i];
          var src = script.src;
    
          if (!isListed(src, scriptBlackList)) {
            if (src.indexOf("common-") !== -1) {
              common = script;
            } else if (src.indexOf("commerce-") !== -1) {
              commerce = script;
            } else if (src.indexOf("signup-") !== -1) {
              signup = script;
            } else if (src.indexOf("dialog-") !== -1) {
              dialog = script;
            } else if (src.indexOf("system-page-") !== -1) {
              systemPage = script;
            } else if (src) {
              others.push(script);
            } else {
              inline.push(script);
            }
          }
        }
    
        function loadOthers() {
          for (var i = 0; i < inline.length; i++) {
            if (inline[i].getAttribute("data-sqs-type") !== "dynamic-assets-loader") {
              load(inline[a]);
            }
          }
    
          for (var i = 0; i < others.length; i++) {
              load(others[i]);
          }
    
          JS.fireCustom("dependenciesLoaded");
        }
    
        var loadSystemPage = load.bind(this, systemPage, loadOthers, "system page");
        var loadSignup = load.bind(this, signup, loadSystemPage, "signup");
        var loadCommerce = load.bind(this, commerce, loadSignup, "commerce");
        var loadDialog = load.bind(this, dialog, loadCommerce, "dialog");
        var loadCommon = load.bind(this, common, loadDialog, "common");
    
        loadCommon();
      }
    
      function load(tag, callback, label) {
        var head = document.head;
    
        if (Array.isArray(tag)) tag = { nodeName: "SCRIPT", src: tag[0] };
    
        if (!tag) {
          if (callback) callback();
          return;
        }
    
        if (tag && (tag.src || tag.href)) {
          var child;
          if ("SCRIPT" === tag.nodeName) {
            child = document.createElement("script");
            child.src = tag.src;
    
            if (child.src.indexOf("combo") !== -1) {
              callback = function () {
                Y.Squarespace.FrontSite.Core.domReady(true)
              };
            }
          } else {
            if ("LINK" === tag.nodeName && "stylesheet" === tag.rel) {
              child = document.createElement("link");
              child.href = tag.href;
              child.rel = "stylesheet";
              child.tyle = "text/css";
            }
    
            if (child) {
              child.onload = callback;
              head.appendChild(child);
            }
          }
        } else {
          try {
            eval(tag.innerHTML);
          } catch (e) {}
        }
      }
    
      return { init: init, webfontsReady: webfontsReady };
    })();
    

    As you can see, the <noscript> tag has the ID #inline-deps, which is referenced in the code (line 3) to load dependencies asynchronously and on-demand.

    They probably use a <noscript> element as it allows them to directly access DOM elements, instead of having to place it in a string or a comment (which I consider particularly bad, as comments are not meant for actual information) and then parse it. It also prevents the execution of scripts and CSS styles until specifically loaded.

    I personally find this an abuse of the <noscript> tag. I'm not even sure if it's valid HTML5 code. Using other methods such as declaring dependencies in a JavaScript object with a script loader should be used where practicable.