0

I' trying to create a function that simultaneously checks if a link to a certain CSS style sheet exists in the document and/or, if it doesn't, creates the link and returns the Javascript object of that linked stylesheet:

(function(doc) {
  const sheets = doc.styleSheets;
  const newSheet_path = location.origin + '/newSheet.css';

  const newSheet = sheets => {
    let sheetExists;
    for(let sheet in sheets) {
      if(sheets[sheet].href === newSheet_path) {
        sheetExists = sheets[sheet];
      }
    }

    if(!sheetExists) {
      let addSheet = doc.createElement('link');
      addSheet.setAttribute('rel', 'stylesheet');
      addSheet.setAttribute('href', 'newSheet.css');
      doc.head.appendChild(addSheet);
      return addSheet;
    } else {
      return sheetExists;
    }
  }
  console.log(newSheet(sheets));
}(window.document));

With this code, it returns the link element if the sheet doesn't exist, which makes sense, but I need the Javascript object associated with it. I've tried replacing the addSheet return statement with something like this:

return Array.from(sheets).pop();

which just returns any existing sheet before the new one, and also this:

return Array.from(sheets).find((s,i,S) => S[i].href === newSheet_path);

but that comes back undefined. If I'm guessing correctly, it's because sheets is defined as the list of stylesheets before the new one gets added and as such, the variable doesn't get redefined with the new sheet included. I tried redefining it just before the return statement--

sheets = doc.styleSheets;
return Array.from(sheets).pop();

--but to no avail. Is this possible, what I'm trying to accomplish? Or am I going to have to break this process out into multiple functions?

panrosmithe
  • 69
  • 10
  • Unless I'm missing something the `newSheet` function is useless - it iterates and checks if a sheet exists but it doesn't return that result. The `if(!sheetExists) {` line should just bomb out because the variable is not defined. – VLAZ Sep 22 '18 at 17:30
  • 2
    @vlaz's second point is very well-taken. `sheetExists` is out of scope as of that `if` statement. `newSheet` wouldn't be useless if `sheetExists` were moved out to the enclosing scope, but it would make much more sense for `newSheet` to simply return the flag. Also note that `newSheet` could easily just be a call to `Array.prototype.find`: `const foundSheet = Array.prototype.find.call(sheets, sheet => sheet.href === newSheet_path);` – T.J. Crowder Sep 22 '18 at 17:34
  • Note: `for-in` isn't the right way to loop through arrays or collections. See [my answer here](https://stackoverflow.com/questions/9329446/for-each-over-an-array-in-javascript/9329476#9329476) for a list of correct ways to do it. – T.J. Crowder Sep 22 '18 at 17:37
  • .... oh I see why you’re all saying this.. hang on for edit – panrosmithe Sep 22 '18 at 17:40
  • you `console.log` at the end of the function is unreachable – Aagam Jain Sep 22 '18 at 17:53
  • add this before return in if `setTimeout(function(){ console.log(newSheet(document.stylesheets)); });` – Aagam Jain Sep 22 '18 at 17:54

1 Answers1

0

If I'm guessing correctly, it's because sheets is defined as the list of stylesheets before the new one gets added and as such, the variable doesn't get redefined with the new sheet included.

No. const sheets = doc.styleSheets; just assigns sheets the value in doc.styleSheets, which is a reference to the browser's list of stylesheets. sheets doesn't get a copy of that list.

I suspect the reason you're not seeing it is that the stylesheet hasn't been loaded yet. Adding a link element to the document starts the process, but the process won't have completed yet.

If you need the StyleSheet instance, you'll need to handle the asynchronous nature of loading the stylesheet — by having your function accept a callback, or return a promise, etc.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875