Skip to content

Instantly share code, notes, and snippets.

@theftprevention
Last active February 7, 2019 00:33
Show Gist options
  • Save theftprevention/e3e935945f730d0dc2563f34ab38ca55 to your computer and use it in GitHub Desktop.
Save theftprevention/e3e935945f730d0dc2563f34ab38ca55 to your computer and use it in GitHub Desktop.
Periodically removes sponsored content from the Facebook news feed.
// ==UserScript==
// @name Remove Facebook Ads
// @version 1.0
// @description Removes sponsored content from the Facebook news feed.
// @author theftprevention
// @match https://www.facebook.com/
// @grant none
// ==/UserScript==
(function() {
'use strict';
var interval = 1000;
var reg_sponsored = /^Sponsored$/i;
var rightColumnPageletSelector = ".home_right_column .pagelet-group:not(#stories_pagelet_rhc):not(#pagelet_reminders)";
var subtitleSelector = "[data-testid='story-subtitle'] > span:first-child";
var titleSelector = "a[href][data-hovercard]:not([tabindex='-1'])";
var whitelist = new WeakSet();
/**
* @returns {Array<HTMLElement>}
*/
function findAds() {
var a = 0;
var ads = [];
var i, l;
var element;
var subtitle;
var subtitles = document.querySelectorAll(subtitleSelector);
// 1. Sponsored news feed stories
for (i = 0, l = subtitles.length; i < l; i++) {
subtitle = subtitles[i];
if (whitelist.has(subtitle)) {
continue;
}
whitelist.add(subtitle);
element = getParentStoryElement(subtitle);
if (!element || whitelist.has(element)) {
continue;
}
if (reg_sponsored.test(getDisplayedInnerText(subtitle))) {
ads[a++] = element;
} else {
whitelist.add(element);
}
}
// 2. Right column pagelet advertisements
var pagelets = document.querySelectorAll(rightColumnPageletSelector);
for (i = 0, l = pagelets.length; i < l; i++) {
ads[a++] = pagelets[i];
}
return ads;
}
/**
* @param {HTMLElement} element
* @returns {string}
*/
function getDisplayedInnerText(element) {
var style = getComputedStyle(element);
if (style.display === 'none') {
return '';
}
var child;
var children = element.childNodes;
var text = '';
for (var i = 0, l = children.length; i < l; i++) {
child = children[i];
switch (child.nodeType) {
case Node.ELEMENT_NODE:
text += getDisplayedInnerText(child);
break;
case Node.TEXT_NODE:
text += child.textContent;
break;
}
}
return text;
}
/**
* @param {ChildNode} node
* @returns {HTMLElement | undefined}
*/
function getParentStoryElement(node) {
var element = node.parentElement;
while (element && element !== document.body) {
if (element.getAttribute('data-testid') === 'fbfeed_story') {
return element;
}
element = element.parentElement;
}
}
/**
* @returns {Array<HTMLElement>}
*/
function removeAds() {
var ad;
var ads = findAds();
var count = ads.length;
var i = count;
var parent;
var title;
var titleElement;
while (i--) {
ad = ads[i];
parent = ad.parentNode;
if (parent) {
titleElement = ad.getAttribute('data-testid') === 'fbfeed_story' ? ad.querySelector(titleSelector) : null;
title = titleElement ? titleElement.textContent.trim() : '';
try {
parent.removeChild(ad);
if (title) {
console.log('Removed an advertisement from %c' + title + '%c.', 'font-weight: bold', '');
}
} catch (e) {
if (e instanceof Error) {
if (title) {
console.error('Failed to remove an advertisement from %c' + title + '%c: (' + e.name + ') ' + e.message, 'font-weight: bold', '');
} else {
console.error('Failed to remove an advertisement: (' + e.name + ') ' + e.message);
}
}
}
}
}
return ads;
}
/**
* @returns {void}
*/
function initialize() {
document.removeEventListener('DOMContentLoaded', initialize);
setInterval(removeAds, interval);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialize);
} else {
initialize();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment