NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name BlockYaAds
// @description Block all ads on yandex.ru
// @version 0.6.10
// @author Chrono
// @license MIT
// @match https://mail.yandex.ru/*
// @match https://*yandex.ru/*
// @match https://*yandex.eu/*
// @match https://ya.ru/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
var ignoredClasses = [
"MessageBody_body",
"_",
"Aside",
"Attach",
"Avatar",
"Badge",
"Button2-Icon",
"ButtonGroup",
"ButtonGroup_gap_xl",
"ButtonWithIcon-m__iconWrap",
"cke",
"CollapsibleToolbar__overflow",
"CollapsibleToolbar__visible",
"CollapsibleToolbar__zeroWidth",
"CollectorIcon-m__root",
"Collectors-m__root",
"compose",
"Compose",
"Contact",
"ContentHeader-m__header",
"Control",
"cut",
"Cut",
"dialog",
"MidgetsPanel",
"Modal",
"Separator",
"Expander",
"Filter-m__root",
"Filters",
"Folder",
"Footer-m__footerContainer",
"FooterItem-m__item",
"Header",
"HeaderSearch-m__root",
"Image-Container",
"Labels-m__root",
"LabelsExpander-m__root",
"Layout-m__root",
"LeftColumn",
"MainButtons",
"MainContent",
"Message",
"Messenger",
"PageLayout",
"Popup",
"PSHeader",
"Prev",
"Quote",
"Recipient",
"Related",
"Right",
"Root",
"Section",
"Sender",
"Spin",
"StatuslineRoot-m__root",
"Svg360_root",
"Tag-m__icon",
"Tag-m__root",
"Text",
"Title",
"Theme",
"ToggleSelectedAction__root",
"tooltip",
"Tooltip",
"User",
"With",
"WrappingList-m__extra",
"WrappingList-m__item",
"WrappingList-m__root",
"WrappingList-m__tail",
"WrappingList-m__withExtra",
"WrappingList-m__withTail",
"b-",
"common-m__withGhostButton",
"g-hidden",
"is-collapsed",
"js",
"mail",
"message-list-banner-portal",
"messages-date-pager-float-box",
"mid",
"mr_css",
"new__root",
"ns-action",
"ns-view-abook",
"ns-view-advanced",
"ns-view-app",
"ns-view-attach",
"ns-view-browser",
"ns-view-collect",
"ns-view-compact-header",
"ns-view-compose",
"ns-view-contact-actions",
"ns-view-container",
"ns-view-copy",
"ns-view-fake",
"ns-view-fill",
"ns-view-folders",
"ns-view-footer",
"ns-view-header",
"ns-view-id",
"ns-view-infoline",
"ns-view-labels",
"ns-view-layout",
"ns-view-left",
"ns-view-loading",
"ns-view-mail",
"ns-view-main",
"ns-view-message",
"ns-view-midgets",
"ns-view-mops",
"ns-view-notifi",
"ns-view-opt-in-subs-box",
"ns-view-opt-in-subs-view",
"ns-view-page",
"ns-view-ps-header",
"ns-view-quick",
"ns-view-react-left-column",
"ns-view-react-main-buttons",
"ns-view-react-promo-container",
"ns-view-react-quick-reply",
"ns-view-right",
"ns-view-service",
"ns-view-setup-abook",
"ns-view-setup-left",
"ns-view-sidebar-with-widgets",
"ns-view-sidebar-with-widgets-box",
"ns-view-skin",
"ns-view-system-alert-container",
"ns-view-tech",
"ns-view-themes",
"ns-view-themes-messages-list-control",
"ns-view-themes-selector-box",
"ns-view-themes-skin-rotater-box",
"ns-view-thread",
"ns-view-timeline",
"ns-view-timeline-box",
"ns-view-timeline-collapsed",
"ns-view-timeline-loader-box",
"ns-view-timeline-toggle",
"ns-view-timeline-wrap",
"ns-view-tip",
"ns-view-toolbar",
"ns-view-toolbar-box",
"ns-view-view",
"qa",
"react",
"ScreenLayer",
"search",
"styles__buttons",
"styles__item",
"styles__messages",
"styles__root",
"styles__wrapper",
"theme-overrides-m__root",
"ui-dialog",
"ui-widget",
"ui-corner",
"ui-front",
"_nb-popup",
"nb-popup",
"ui-resizable-handle",
"ui-resizable-s"
];
// for all other subdomains remove all divs but the lists below
var specificIgnoredClasses = [
"AdvRsyaCrossPage",
"App",
"b-page",
"Button",
"Carousel",
"cbir",
"Cbir",
"clearfix",
"copyright",
"Content",
"content",
"detect",
"Divider",
"Extra",
"extra",
"fade",
"favicon",
"Favicon",
"Fetch",
"FewLines",
"Flex",
"Footer",
"footer",
"header",
"Header",
"Host",
"i-bem",
"i-react",
"image_history",
"ImagesViewer-Content",
"ImagesViewer-Footer",
"ImagesViewer-Layout",
"ImagesViewer-Modal",
"ImagesViewer-Paranja",
"ImagesViewer-Rim",
"ImagesViewer-Sidebar",
"ImagesViewer-TopSide",
"ImagesViewer-View",
"internal",
"Justifier",
"main",
"mini",
"List",
"Menu",
"Mini",
"Missing",
"MMFavicon",
"MMImage",
"MMOrganic",
"MMViewer",
"Modal",
"Organic",
"organic",
"overlay",
"navigation",
"Native",
"Open",
"Page",
"page",
"Pager",
"paranja",
"Path",
"path",
"presearch",
"Prevention",
"Related",
"Request",
"Root",
"Search",
"SendTimeMark",
"Serp",
"serp",
"Site",
"site",
"Skeleton",
"smart",
"Spyable",
"Svg",
"Swipe",
"Spin",
"spin",
"text",
"Typo",
"unread",
"User",
"Video",
"Viewer",
"z-index-group",
// ya.ru classes
"headline",
"weather",
"usermenu-portal",
"body",
"search3",
"link-bro",
"i-mini-bem",
"simple-popup"
];
var miscellaneousClasses = [
"Container",
"CardDivider",
"Wrapper",
"Tags"
];
// default element visibility
var elementsVisibility = {};
// paths to exclude filtering on
var excludedPaths = [
"/all",
"/maps",
"/products",
"/search",
"/showcaptcha",
"/video/preview"
];
var numElements = 0;
var scriptStarted = false;
var lastRunMillis = 0;
function removeElements(selector, visibility, el) {
// This function sets display: none; it no longer *removes* the element from the DOM.
var number = 0;
if (visibility != "none") {
elementsVisibility[selector] = visibility;
}
el.style.display = visibility;
}
function hideInnerElements(container) {
container.querySelectorAll("*").forEach(function(innerEl) {
innerEl.style.display = "none";
});
}
var mutationConfig = {
attributes: true,
attributeOldValue: true,
characterData: true,
characterDataOldValue: true,
childList: true,
subtree: true
};
var observer;
function containsAnyIgnoredClass(div, ignoredClasses) {
const divClasses = Array.from(div.classList);
return divClasses.some(divClass =>
ignoredClasses.some(ignoredClass => divClass.indexOf(ignoredClass) !== -1)
);
}
function start() {
var isElementFound = false;
var divs = document.querySelectorAll('div');
var currentUrl = window.location;
var baseUrl = currentUrl.host;
if (Date.now() - lastRunMillis < 300) {
lastRunMillis = Date.now();
return;
}
//console.log("start");
// skip filtering if not implemented
var exit = false;
excludedPaths.forEach(function(path) {
if (window.location.pathname.startsWith(path)) {
console.log(`filtering for ${path} is not implemented`);
exit = true;
return;
}
});
if (exit) return;
var rootNodes = document.getElementsByClassName("Root");
for (var i = 0; i < rootNodes.length; i++) {
var rootNode = rootNodes[i];
if (rootNode) {
if (rootNode.firstChild != undefined) {
if (rootNode.firstChild.classList != []) {
var ignoredClass = rootNode.firstChild.classList[0];
//console.log(ignoredClass + " is the root, skipping");
specificIgnoredClasses.push(ignoredClass);
}
}
}
}
// Specific case for "docviewer.yandex.ru" (kept for completeness)
if (baseUrl === "docviewer.yandex.ru") {
document.querySelectorAll("*").forEach(function(el) {
el.classList.forEach(function(className) {
if (className === "js-doc-html") {
var innerElements = el.querySelectorAll("*");
innerElements.forEach(function loop(el1) {
if (loop.stop) {
return;
}
if ((el1.tagName === "DIV") && el1.classList.length === 3) {
removeElements("." + el1.classList[0], "none", el);
loop.stop = true;
}
});
}
});
if (el.tagName.length > 10) {
el.style.display = "none";
}
});
}
// ----------------------------------------------------
// --- START: Specific logic for mail.yandex.ru ---
// ----------------------------------------------------
if (baseUrl === "mail.yandex.ru") {
// Find the message body container. We use the attribute selector [class*="..."]
// because the class name has a random suffix (e.g., MessageBody_body_pmf3j).
const messageBody = document.querySelector('[class*="MessageBody_body"]');
divs.forEach(div => {
// If it's the message body element itself OR a descendant, skip removal.
if (messageBody && (messageBody === div || messageBody.contains(div))) {
return; // Skip this element and move to the next 'div'
}
// Proceed with removal logic for non-message-body and non-descendant divs
if (div.classList.length > 0) {
if (!containsAnyIgnoredClass(div, ignoredClasses)) {
// Use removeElements to set display: none
removeElements(Array.from(div.classList).join('.'), 'none', div);
console.log("Removed element with class:", Array.from(div.classList).join(' '));
}
}
});
// CRITICAL FIX: Exit the start() function here to prevent the generic
// removal logic (the second loop) from processing mail elements.
return;
}
// --------------------------------------------------
// --- END: Specific logic for mail.yandex.ru ---
// --------------------------------------------------
// --------------------------------------------------------------------------
// --- START: Generic logic for other yandex domains (ya.ru, yandex.ru/...) ---
// --------------------------------------------------------------------------
for (i = 0; i < divs.length; i++) {
var div = divs[i];
// NOTE: The logic above for mail.yandex.ru now ensures this loop is skipped
// when on the mail subdomain.
if (window.location.pathname === "/images/search") {
var style = div.getAttribute("style");
if (style === "margin-bottom: 4px;" || div.offsetHeight === 72) {
div.classList.forEach(function(className) {
removeElements("." + className, "none", div);
});
}
}
div.classList.forEach(function(className) {
// Match classes for anything but "mail.yandex.ru"
if (baseUrl === "mail.yandex.ru") {
return;
}
//
if (currentUrl == "https://ya.ru") {
return;
}
// Classes to ignore
isElementFound = true;
// Hide specific class
if (className === "ComposeDoneDirect") {
removeElements("." + className, 'none', div);
}
for (var j = 0; j < miscellaneousClasses.length; j++) {
if (className.indexOf(miscellaneousClasses[j]) !== -1) {
isElementFound = false;
removeElements("." + className, elementsVisibility[className], div);
return;
}
}
if (className === "ImagesViewer") {
isElementFound = false;
removeElements("." + className, elementsVisibility[className], div);
return;
}
for (j = 0; j < specificIgnoredClasses.length; j++) {
if (className.startsWith(specificIgnoredClasses[j])) {
isElementFound = false;
}
}
if (className == "ImagesViewer-LayoutSideblock") {
console.log("ImagesViewer-LayoutSideblock");
if (div.firstChild.classList != undefined) {
div.firstChild.classList.forEach(function(imagesViewerClassName) {
if (!specificIgnoredClasses.includes(imagesViewerClassName)) {
console.log("found LayoutSideblock first child div");
specificIgnoredClasses.push(imagesViewerClassName);
}
});
}
}
if (isElementFound) {
console.log("Removed element with class:", className);
numElements = removeElements("." + className, "none", div);
}
});
if (baseUrl === "mail.yandex.ru" && numElements != 0) {
//console.log("disconnecting observer");
//observer.disconnect();
}
} // End of generic 'divs' loop
// --------------------------------------------------------------------------
// --- END: Generic logic for other yandex domains ---
// --------------------------------------------------------------------------
}
observer = new MutationObserver(start);
function deferredStart() {
setTimeout(function() {
if (document.body instanceof Node && !scriptStarted) {
lastRunMillis = Date.now();
observer.observe(document.body, mutationConfig);
scriptStarted = true;
}
deferredStart();
}, 200);
}
deferredStart();
console.log("BlockYaAds script is running.");
})();