Code Review Stack Exchange is a question and answer site for peer programmer code reviews. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Soo... I recently read a meta-post about the inherent problems of reputation. It listed some things like:

  • Confirmation bias.
  • Demotivating for low-rep users.
  • Hubris and elitism of high-rep users over low-rep users.
  • Incorrect representation of competence.
  • ...

So I thought to myself: "How would the network be, just without reputation?"

I grabbed the inspector, checked questions, my profile page and the active page and how reputation is displayed.

Then I quickly hacked together the following small user-script (ECMA6) for Greasemonkey that would hide all reputation from sight:

// ==UserScript==
// @name         Reputation Hider
// @namespace    http://gihub.com/Vogel612/ReputationHider
// @version      0.1
// @description  Hides all reputation displays from StackExchange sites
// @author       Vogel612
// @grant        none
// @include      /https?:\/\/(meta\.)?.*\.stackexchange\.com\/.*/
// @include      /https?:\/\/(stackoverflow|serverfault|superuser|askubuntu)\.com\/.*/
// ==/UserScript==
/* jshint -W097 */
'use strict';

let slice = function(collection) {
  return [].slice.call(collection);   
}

let repClasses = [ "rep", "reputation", "reputation-score" ];

let hideRepz = function() {
  repClasses.forEach(clazz => 
    slice(document.getElementsByClassName(clazz))
      .forEach(el => { 
        // this results in minor inconsistencies in CSS, but alas
        el.style.visibility = 'hidden';
      })
    );
}

hideRepz();
// add event listener for live-updates
document.addEventListener("DOMNodeInserted", hideRepz);

This has been running for a while now for me and I like to say it's a refreshing change. That aside:

It hides all reputation numbers on the page. If you're curious you can still find them of course. Also it looks funny on /questions when the active timers are outdented differently, but that's just my OCD going mad there.
It also checks for new questions you load over the notification.

What can I do better in that userscript?

share|improve this question
51  
Welcome to Stack Exchange, the network where everything's made up and the points don't matter. – Josay Feb 15 at 15:59
18  
Why are you using JS for this? Wouldn't custom CSS solve this better (and much easier)? – Bergi Feb 15 at 16:18
6  
You could do the same from a userscript actually. Just document.head.appendChild(document.createElement("style")).textContent = ".rep, .reputation, .reputation-scope { visibility: hidden; }"; – Bergi Feb 15 at 16:44
5  
Will you post this on StackApps? – A.L Feb 16 at 12:08
1  
@A.L. I didn't intend to originally, but now I am considering. I would add some more functionality though before doing that. Feel free to post yourself, even though it's 3 lines of CSS for that matter – Vogel612 Feb 16 at 12:10
up vote 35 down vote accepted

It would be better to do this with a user style, e.g. using the Stylish extension, like this:

.rep, .reputation, .reputation-score {
    visibility: hidden;
}

One advantage of user styles over scripts is that they're applied as soon as the page starts to load, so you won't see the rep counts flicker briefly while the page is loading. Also, user styles will automatically apply also to new dynamic content loaded e.g. via Ajax (such as new answers posted while you're viewing the page).

If you insist on using a user script, it's possibly to emulate user styles by using @run-at document-start and injecting a custom style element into the page early, like this:

// ==UserScript==
// @name        Hide Stack Exchange reputation (demo)
// @namespace   http://vyznev.net/
// @description A demonstration of using style injection to emulate user styles
// @author      Ilmari Karonen
// @version     1.0
// @license     Public domain
// @match       *://*.stackexchange.com/*
// @match       *://*.stackoverflow.com/*
// @match       *://*.superuser.com/*
// @match       *://*.serverfault.com/*
// @match       *://*.stackapps.com/*
// @match       *://*.mathoverflow.net/*
// @match       *://*.askubuntu.com/*
// @homepageURL http://codereview.stackexchange.com/a/120100
// @grant       none
// @run-at      document-start
// ==/UserScript==

var css =
    ".rep, .reputation, .reputation-score {\n" +
    "    visibility: hidden;\n" +
    "}\n";

var style = document.createElement('style');
style.textContent = css;
(document.head || document.documentElement).appendChild(style);

I use this method in my Stack Overflow Unofficial Patch (SOUP) user script (which combines a bunch of more or less minor interface bugfixes and improvements together) to let me efficiently apply both CSS-only and JS-based tweaks from a single user script.

One detail worth noting is that styles injected this way will generally end up being included before any actual site style sheets on the page, which means that they will be overridden by site CSS selectors of equal specificity. In some cases, you may need to use !important and/or various specificity hacks to make sure that your custom styles will take precedence.

share|improve this answer
9  
If ES6 is available, best use template strings instead of concatenating. – Joseph the Dreamer Feb 16 at 14:59

You could use querySelectorAll instead of getElementsByClassName. That way, you have a bit more flexibility in targeting elements. It also allows multiple selectors. That way, you don't have to loop twice.

Instead of [].slice which uses an unnecessary array as host, try using Array.prototype.slice.call instead. Longer, but without that extra array.

Also, try using display:none as visibility just hides the element but maintain layout. This way, there's no awkward whitespace where the reputation was, making the layout all natural.

Seeing that you put ES6, we can use the handy dandy arrow functions as well.

'use strict';

const REPUTATION_ELEMENTS = '.rep, .reputation, .reputation-score';

function getElementArray(selector){
  return Array.prototype.slice.call(document.querySelectorAll(selector));
}

document.addEventListener("DOMNodeInserted", event => {
  getElementArray(REPUTATION_ELEMENTS).forEach( element => {
    element.style.display = 'none';
  });
});
share|improve this answer
6  
The display:noneadvice is worth gold. One small thing: this doesn't run when loading the page by default. That's just a really minor thing. Also I recall hearing something about querySelectorAll being slower than the more specialized calls, but I might be wrong. Thanks for the review :) – Vogel612 Feb 15 at 15:28
7  
If you're using ES6, you should rather use a for of loop instead of forEach. – Bergi Feb 15 at 16:17
2  
Another way to hide things: element.hidden = true. ciu, whatwg – deltab Feb 15 at 17:54
1  
@Vogel612 Yup, it's relatively slower than the older element selection APIs, but for a few elements it's probably negligible and more familiar because of jQuery. Speaking of jQuery, not sure if possible, but if the userscript can utilize the one that is already on the page, it can be as simple as $(selector).hide(). – Joseph the Dreamer Feb 16 at 1:24
    
@Bergi forEach just came out of instinct, especially with arrays. :D – Joseph the Dreamer Feb 16 at 1:29

Update url

UserScripts have the magical ability to update themselves if you provide a link to a raw version of your script as a UserScript header:

Note: You actually have to increase the version, or it won't update.

// ==UserScript==
// @name         Reputation Hider
// @namespace    http://gihub.com/Vogel612/ReputationHider
// @version      0.1
// @downloadURL
// @updateURL

Method

I'm not particularly a fan of your method.

Checking for DOM updates before scanning through everything all over again.

It would make more sense to simply inject a stylesheet to override SE's stylesheets.

Much more labour performant.


ES6 features

I like the use of ES6 in your code, but you haven't used it to its full potential:

let slice = function(collection) {
  return [].slice.call(collection);   
}

Instead of using the old function(collection){} way, you can use (collection) => ():

let slice = (collection) => [].slice.call(collection)

The same thing applies to the function in your main loop.


Slicing and Dicing

Currently, you only use slice with your element selection.
You should combine the two!

slice(document.getElementsByClassName(clazz))
let fetchElements(clazz) => [].slice.call(document.getElementsByClassName(clazz))

Or hey, let's take it a step further and do some concatenation magic:

let fetchElements = (clazzArrey) =>
    clazzArrey.map((clazz) => [].slice.call(document.getElementsByClassName(clazz)))

Alternatively, you can use string concatenation to make one huge selector string and use querySelectorAll as Joseph described.

I find that querySelector* can have worse performance, but its always been edge cases that trigger that for me.


Support

Currently, very few browsers support ES6 entirely. I wouldn't necessarily go using it unless necessary. You don't particularly want to kill off support for a large portion of users by using it.


Future gazing

This isn't usually a review point I add, however, I think you could add support for other fields, and make a handy dandy dialog box to choose which you want to hide. That'd be a StackApps worthy creation in my eyes.

share|improve this answer

Instead of document.getElementsByClassName, you can use document.querySelectorAll(CSS selector).

Edit(Reply to @200_success): You can use anyone of those. Just I prefer document.querySelectorAll since I think it is better a little.

  1. document.getElementsByClassName has 31 letters, and document.querySelectorAll has 25 letters.
  2. document.querySelectorAll uses CSS to get DOM elements, but document.getElementsByClassName does not.
  3. IE 8+ supports document.querySelectorAll(IE 8 only supports CSS2 selectors), but document.getElementsByClassName is for IE 9+.

And you don't have to make an Array from the DOMNodeList using slice, since a DOMNodeList object is Array-like. i.e. you can use it as an Array using Array.prototype.forEach.call(DOMNodeList, callbackFunction).

window.addEventListener("load", () => {
    Array.prototype.forEach.call(document.querySelectorAll(".rep, .reputation, .reputation-score, .-flair"), element => {
            element.style.display = "none";
        });
});
share|improve this answer

Since you're using ES6, you could replace your slice function with Array.from like this (implementing Josephs changes too);

let hideRepz = function() {
  repClasses.forEach(clazz => 
    Array.from(document.querySelectorAll(`.${clazz}`))
      .forEach(el => { 
        el.style.display = 'none';
      })
    );
}
share|improve this answer
4  
If you're using ES6, you should rather use a for of loop instead of forEach. – Bergi Feb 15 at 16:17
3  
@Bergi Other than the "it's there, so let's use it" argument, is there a concrete reason why for(..of..) is preferable? – spender Feb 16 at 2:17
1  
It's somewhere between "more concise, more elegant, more performant, less to type, easier to understand" - if there was an adjective like "pythonic" for JS, it would be that. The technical arguments are that it doesn't require intermediate arrays and that you can break from it; basically forEach is deprecated with ES6. – Bergi Feb 16 at 13:44

Avoid using deprecated events

Mutation Events (DOMNodeInserted) is deprecated and you should use Mutation Observers instead.

Sample usage:

function processNewNodes(topNode) {
    var nodes = topNode.querySelectorAll(selector);
    // ...
}

var observer = new MutationObserver(function(mutations, observer) {
    mutations.forEach(function(mutation) {
        Array.prototype.forEach.call(mutation.addedNodes, processNewNodes);
    });
});

observer.observe(document, {
    subtree: true,
    childList: true
});
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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