3
\$\begingroup\$

Some time ago someone complained that sites that were not stackoverflow were hard to read at work since people might be screen watching. So I bashed some code and created SO-ify. Some time and several versions later, it is basically as done as I can get it with my current skill.

While I will accept any feedback, that sort I am specifically looking for is optimization. Since I'm screwing around with a page as it loads, I want to work as quickly as possible, and ideally without being noticeable to the user. This is a tamper/greasemonkey script, so if you like, you can install here.

I know parts of this are not very DRY, and if there is a way to fix that without without sacrificing speed, then I'm open to it. I have put the code below in its current state, if you'd rather I comment it up any more to explain decisions, I can definitely do that.

The original site this was created for was Science Fiction & Fantasy, so thats why some of the comments and variable names refer to "scifi". Though, to be clear, this does, and should, work on all SE sites (with like two exceptions).

// ==UserScript==
// @name         SO-ify
// @namespace    http://tampermonkey.net/
// @version      2.2.5
// @description  Style SE sites like SO
// @author       amflare
// @include      https://*stackexchange.com
// @include      https://*stackexchange.com/*
// @exclude      https://chat.stackexchange.com/*
// @exclude      https://area51.stackexchange.com/*
// @exclude      https://data.stackexchange.com/*
// @exclude      https://stackexchange.com/
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
  'use strict';
  var primaryCss = 'https://cdn.sstatic.net/Sites/stackoverflow/primary.css';
  var secondaryCss = 'https://cdn.sstatic.net/Sites/stackoverflow/secondary.css';
  var head = document.head || document.getElementsByTagName('head')[0];
  var regex = /\/Sites\/(?!stackoverflow).*\.css/;

  // Remove scifi et al
  var checkScifi = setInterval(function() {
    let sheets = document.querySelectorAll("link[rel='stylesheet']");
    sheets.forEach(function(sheet){
      if (regex.test(sheet.href)) {
        sheet.remove();
        clearInterval(checkScifi);
      }
    });
  }, 100);

  // Add SO Style Sheet
  let link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = primaryCss;
  head.appendChild(link);

  // Add secondary stylesheet
  let secondary = document.createElement('link');
  secondary.rel = 'stylesheet';
  secondary.href = secondaryCss;
  document.head.appendChild(secondary);

  // Replace Favicon
  document.querySelector("link[rel='shortcut icon']").href = 'https://cdn.sstatic.net/Sites/stackoverflow/img/favicon.ico';

  // Remove Site Logo From Top Bar
  var checkLogo= setInterval(function() {
    let logo = document.querySelector('.-logo svg');
    if (logo !== null) {
      logo.remove();
      clearInterval(checkLogo);
    }
  }, 100);

  // Change Site Name in Browser Tab
  if (window.location.pathname == '/') {
    document.querySelector('title').innerText = 'Stack Overflow - Where Developers Learn, Share, & Build Careers';
  } else {
    var title = document.querySelector('title').innerText.split(" - ");
    if (title.length > 2) {
      document.querySelector('title').innerText = title[0] +' - '+ title[1] +' - Stack Overflow';
    } else {
      document.querySelector('title').innerText = title[0] + ' - Stack Overflow';
    }
  }

  // Change Site Name In Search Placeholder
  var checkSearch= setInterval(function() {
    let search = document.querySelector('input[name="q"]');
    if (search !== null) {
      search.placeholder = 'Search on Stack Overflow...';
      clearInterval(checkSearch);
    }
  }, 100);

  // Fix font issue
  var addCss = setInterval(function() {
    let body = document.getElementsByTagName("body")[0];
    if (typeof body != "undefined") {
      body.style.fontFamily = 'Arial,"Helvetica Neue",Helvetica,sans-serif';
      body.style.fontSize = '13px';
      body.style.lineHeight = '1.26666667';
      body.style.color = '#242729';
      clearInterval(addCss);
    }
  }, 100);

  // Re-add css files to cache bust
  setTimeout(function(){
    let reLink = document.createElement('link');
    reLink.rel = 'stylesheet';
    reLink.href = primaryCss;
    head.appendChild(reLink);
    // remove original because we can
    link.remove();
  },100);
})();
\$\endgroup\$
1
\$\begingroup\$

Feedback

This is an interesting script. I understand the desire to update the styles but don't see the point in changing the titles and placeholder texts to contain Stack OverFlow since it isn't the case that the search would involve SO posts...

Do you still use this userscript? I see that the linked code has evolved (and I have feedback on that code but I'll save that for another review). The styles from February 16th 2018 are a bit different that those of today - especially with the new sidebar that was introduced back in June...

Suggestions

Many elements are obtained via queried when properties could be used:

  • document.querySelectorAll("link[rel='stylesheet']"); -> document.styleSheets (though that wouldn't include the favicon - I see the latest verison updates that)

    I also considered suggesting that the filtering of stylesheets could be moved into the CSS selector - something like document.querySelectorAll("link[rel='stylesheet']:not([href~='stackoverflow']") - that way the if condition within the callback function passed to sheets.forEach() could be removed... This would be analogous to filtering in a database query instead of a scripting language.

  • document.getElementsByTagName("body")[0] -> document.body
  • document.querySelector('title').innerText -> document.title
  • document.querySelector('input[name="q"]'); -> document.forms[0].elements.q

Use const instead of let wherever re-assignment is not needed

This will avoid unintentional variable clobbering. Even if properties of an object or elements of an array are assigned later, const can still be used

Variable created for <head> element but not always used when it could be

The first SO styleSheet is added via head but the second is added via document.head

D.R.Y. adding CSS files

primaryCSS and secondaryCss could be added to an array, and then for each of those paths, an iterator loop could create the link elements - possibly using Array.forEach() with a callback function - or that callback function could be a named function. Also, the stack exchange sites should have jQuery, which could also be used to dramatically shorten the code needed to create elements.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the feedback! WRT your musings: Yes, this is still in use. The reason I replace logo/title/placeholder is for visual obfuscation. And SO is still hosting their old SO.css files, so I am able to use those on the non-modified sites (they also work on modified sites, but are admittedly a bit strange). \$\endgroup\$ – amflare Aug 27 '18 at 21:05
  • 1
    \$\begingroup\$ WRT the document.property suggestions, thanks for those. The links are being updated in place, which is causing a bit of a problem with document.styleSheets, so I'll have to look into that further. The rest, though, appear to be operating better. I can't believe I was so inconsistent with me document.head! WRT to dry css, thanks. I'm also experimenting with this some more, but its a definite improvement to code cleanliness, so thank you. WRT jQuery, I am operating under the (potentially inaccurate) assumption that vanilla JS will naturally be faster than jQuery \$\endgroup\$ – amflare Aug 27 '18 at 21:06
  • \$\begingroup\$ Cool - I added another thought about the stylesheet filtering \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Aug 27 '18 at 21:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.