// ==UserScript==
// @name 隐藏b站视频详情页右侧的"活动推广"和"大家围观的直播"以及首页广告
// @name:en Hide promotions on Bilibili's video details page and homepage
// @namespace http://tampermonkey.net/
// @version 0.1.25
// @description Hide specified Bilibili elements using MutationObserver
// @description:en Hide specified Bilibili elements using MutationObserver
// @author aspen138
// @match *://www.bilibili.com/video/*
// @match *://www.bilibili.com/*
// @match *://www.bilibili.com
// @match *://search.bilibili.com/*
// @icon https://www.bilibili.com/favicon.ico
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_notification
// @grant GM_info
// @grant window.onurlchange
// @license MIT
// @sandbox JavaScript
// ==/UserScript==
// ↓↓↓↓↓↓↓↓↓模板,建议直接复制 //
// 自定义 urlchange 事件(用来监听 URL 变化)
function addUrlChangeEvent() {
history.pushState = ( f => function pushState(){
var ret = f.apply(this, arguments);
window.dispatchEvent(new Event('pushstate'));
window.dispatchEvent(new Event('urlchange'));
return ret;
})(history.pushState);
history.replaceState = ( f => function replaceState(){
var ret = f.apply(this, arguments);
window.dispatchEvent(new Event('replacestate'));
window.dispatchEvent(new Event('urlchange'));
return ret;
})(history.replaceState);
window.addEventListener('popstate',()=>{
window.dispatchEvent(new Event('urlchange'))
});
}
var menu_ALL = [
['menu_isEnableAppendCoverLink', '默认追加视频封面链接', '默认追加视频封面链接', true]
], menu_ID = [];
for (let i=0;i<menu_ALL.length;i++){ // 如果读取到的值为 null 就写入默认值
if (GM_getValue(menu_ALL[i][0]) == null){GM_setValue(menu_ALL[i][0], menu_ALL[i][3])};
}
// 注册脚本菜单
function registerMenuCommand() {
if (menu_ID.length >= menu_ALL.length){ // 如果菜单ID数组长度大于等于菜单数组长度,说明不是首次添加菜单,需要卸载所有脚本菜单
for (let i=0;i<menu_ID.length;i++){
GM_unregisterMenuCommand(menu_ID[i]);
}
}
for (let i=0;i<menu_ALL.length;i++){ // 循环注册脚本菜单
menu_ALL[i][3] = GM_getValue(menu_ALL[i][0]);
menu_ID[i] = GM_registerMenuCommand(`${menu_ALL[i][3]?'✅':'❌'} ${menu_ALL[i][1]}`, function(){menu_switch(`${menu_ALL[i][3]}`,`${menu_ALL[i][0]}`,`${menu_ALL[i][2]}`)});
}
}
// 菜单开关
function menu_switch(menu_status, Name, Tips) {
if (menu_status == 'true'){
GM_setValue(`${Name}`, false);
GM_notification({text: `已关闭 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});
}else{
GM_setValue(`${Name}`, true);
GM_notification({text: `已开启 [${Tips}] 功能\n(点击刷新网页后生效)`, timeout: 3500, onclick: function(){location.reload();}});
}
registerMenuCommand(); // 重新注册脚本菜单
};
// 返回菜单值
function menu_value(menuName) {
for (let menu of menu_ALL) {
if (menu[0] == menuName) {
return menu[3]
}
}
}
for (let i=0;i<menu_ALL.length;i++){ // 如果读取到的值为 null 就写入默认值
if (GM_getValue(menu_ALL[i][0]) == null){GM_setValue(menu_ALL[i][0], menu_ALL[i][3])};
}
registerMenuCommand();
if (window.onurlchange === undefined) {addUrlChangeEvent();} // Tampermonkey v4.11 版本添加的 onurlchange 事件 grant,可以监控 pjax 等网页的 URL 变化
// ↑↑↑↑↑↑↑↑↑↑↑↑模板,建议直接复制 //
function appendCoverLink() {
let BVid = '';
const match1 = location.pathname.match(/BV[^/]*/);
if (match1) {
BVid = match1[0];
} else {
// console.error("No match found");
}
// Select the parent container where the new element will be appended
const parentContainer = document.querySelector(`.video-info-detail-list.video-info-detail-content:not(.${BVid}modified)`);
if (!parentContainer) {
console.warn('Parent container not found. Ensure the selector is correct.');
return;
}
// Ensure the link is only appended once
const existingCoverLink = parentContainer.querySelector(' .cover-link');
if (existingCoverLink) {
existingCoverLink.remove();
}
// Extract the cover image URL from the document head
const imageMetaTag = document.head.querySelector('[itemprop="image"]');
if (!imageMetaTag) {
console.warn('Image meta tag with [itemprop="image"] not found.');
return;
}
// Create the new container div with appropriate classes
const coverItem = document.createElement('div');
coverItem.classList.add('cover-item', 'item');
// Create the SVG icon (optional)
const coverIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
coverIcon.setAttribute('class', 'cover-icon');
coverIcon.setAttribute('style', 'width:20px;height:20px;');
coverIcon.setAttribute('viewBox', '0 0 20 20');
coverIcon.setAttribute('width', '20');
coverIcon.setAttribute('height', '20');
// Example SVG path
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'M10 0 L20 20 L0 20 Z');
path.setAttribute('fill', 'currentColor');
coverIcon.appendChild(path);
// Create the link element
const coverLink = document.createElement('a');
coverLink.setAttribute('target', '_blank');
coverLink.setAttribute('rel', 'noopener noreferrer');
coverLink.classList.add('cover-link');
coverLink.setAttribute('title', 'You may need F5 refresh to get consistent cover');
// Create the text node
const linkText = document.createElement('span');
linkText.classList.add('cover-text');
linkText.textContent = 'Cover';
// Assemble the link
coverLink.appendChild(coverIcon);
coverLink.appendChild(linkText);
// Append the link to the new item container
coverItem.appendChild(coverLink);
setTimeout(() => {
BVid = 'default';
const match = location.pathname.match(/BV[^/]*/);
if (match) {
BVid = match[0];
} else {
// console.error("No match found");
}
// Mark this container as modified
parentContainer.classList.add(BVid + 'modified');
// Replace 'http' with 'https' if necessary
const coverImgUrl =
'https://' + imageMetaTag.getAttribute('content').replace('http', 'https').split('@')[0];
console.log("coverImgUrl=", coverImgUrl);
coverLink.setAttribute('href', coverImgUrl);
// Ensure the link is only appended once
const existingCoverLink = parentContainer.querySelector('.cover-link');
if (existingCoverLink) {
existingCoverLink.remove();
}
parentContainer.append(coverLink);
console.log('Cover link appended successfully.');
}, 2*1000);
}
(function () {
'use strict';
registerMenuCommand();
if (window.location.hostname.includes('bilibili.com')) {
const styleElement1 = document.createElement('style');
styleElement1.textContent = `.login-tip, .vip-login, .vip-login-tip, .login-panel-popover { display: none !important; }`;
document.head.appendChild(styleElement1);
}
if (document.cookie.includes('DedeUserID')) {
//console.log("has loged in.");
// add CSS to hide some elements
const styleElement = document.createElement('style');
styleElement.textContent = ` .desktop-download-tip { display: none !important; }`; //隐藏右下角的下载客户端的推广弹窗
document.head.appendChild(styleElement);
}
else {
//console.log("not loged in.");
// add CSS to hide some elements
const originAppendChild = Node.prototype.appendChild;
Node.prototype.appendChild = function (childElement) {
if (childElement.tagName === 'SCRIPT' && childElement.src.includes("login")) {
console.log("src=", src);
return null;
}
else {
return originAppendChild.call(this, childElement);
}
}
}
// Select elements with href containing 'cm.bilibili.com/cm/api'
var elements = document.querySelectorAll('a[href*="cm.bilibili.com/cm/api"]');
elements.forEach(function(element) {
// Find the closest parent with class 'bili-video-card'
var parentCard = element.closest('.bili-video-card');
if (parentCard) {
// Store original height to maintain layout
var originalHeight = parentCard.children[0].children[0].offsetHeight;
// Create replacement message div
var messageDiv = document.createElement('div');
messageDiv.style.cssText = `
background-color: #f0f0f0;
color: #666;
padding: 15px;
text-align: center;
font-size: 14px;
height: ${originalHeight}px;
display: flex;
align-items: center;
justify-content: center;
`;
messageDiv.textContent = "The AD content is hidden";
// Replace content while keeping the card
parentCard.innerHTML = '';
parentCard.appendChild(messageDiv);
}
});
// Enhanced function to thoroughly hide elements
function hideElement(element) {
if (!element) return;
// Apply more aggressive hiding styles
const hideStyles = {
'display': 'none !important',
'visibility': 'hidden !important',
'opacity': '0 !important',
'background': 'white !important',
'color': 'white !important',
'pointer-events': 'none !important',
'height': '0 !important',
'width': '0 !important',
'overflow': 'hidden !important',
'position': 'absolute !important',
'z-index': '-9999 !important',
'clip': 'rect(0, 0, 0, 0) !important'
};
// Apply styles using both direct style and cssText for maximum effectiveness
Object.entries(hideStyles).forEach(([property, value]) => {
element.style.setProperty(property, value.replace(' !important', ''), 'important');
});
// Hide all child elements recursively
Array.from(element.children).forEach(child => {
hideElement(child);
});
// Remove any inline event listeners
element.onclick = null;
element.onmouseover = null;
element.onmouseenter = null;
element.onmouseleave = null;
}
// Function to handle all target elements
function hideAllTargetElements() {
const targetElements = [
'#slide_ad',
'#right-bottom-banner',
'.pop-live-small-mode.part-1',
'.ad-floor-cover.b-img',
'#bannerAd',
'.vcd',
'a[data-loc-id="4331"]',
'#activity_vote',
'.ad-report.video-card-ad-small',
'.ad-report.ad-floor-exp',
'.slide-ad-exp',
'.activity-m-v1.act-now',
'.video-page-special-card-small',
'.btn-ad',
'div[data-v-2ce37bb8].btn-ad',
'.palette-button-adcard.is-bottom', // New element
'.palette-button-adcard' // More specific selector for the new element
//,'div[data-v-7b35db32].vip-login-tip'
];
targetElements.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(hideElement);
});
}
// Create a more specific MutationObserver
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
// Check for added nodes
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) { // Element node
// Check if the added node is a target element
if (node.id === 'slide_ad' ||
node.classList.contains('slide-ad-exp') ||
node.classList.contains('ad-report') ||
node.classList.contains('activity-m-v1') ||
node.classList.contains('video-page-special-card-small') ||
node.classList.contains('btn-ad') ||
node.classList.contains('palette-button-adcard')) { // Added new class check
hideElement(node);
}
// Also check children of added nodes
const targetElements = node.querySelectorAll('#slide_ad, .slide-ad-exp, .ad-report, .activity-m-v1, .video-page-special-card-small, .btn-ad, .palette-button-adcard');
targetElements.forEach(hideElement);
}
});
}
// Check for attribute changes
if (mutation.type === 'attributes') {
const element = mutation.target;
if (element.id === 'slide_ad' ||
element.classList.contains('slide-ad-exp') ||
element.classList.contains('ad-report') ||
element.classList.contains('activity-m-v1') ||
element.classList.contains('video-page-special-card-small') ||
element.classList.contains('btn-ad') ||
element.classList.contains('palette-button-adcard')
// ||element.classList.contains('vip-login-tip')
) { // Added new class check
hideElement(element);
}
}
});
});
// Configure the observer to watch for everything
const observerConfig = {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['style', 'class']
};
// Initial hiding
hideAllTargetElements();
// Start observing
observer.observe(document.body, observerConfig);
// Set up periodic checks just in case
const checkInterval = setInterval(hideAllTargetElements, 1000);
// Cleanup after 30 seconds
setTimeout(() => {
clearInterval(checkInterval);
observer.disconnect(); // Optionally disconnect the observer after cleanup
}, 30*1000);
// window.addEventListener('urlchange', function(){ //
// setTimeout(()=>{appendCoverLink();}, 1*1000);
// })
// Retrieve the current setting, default to false
let isEnableAppendCoverLink = GM_getValue('menu_isEnableAppendCoverLink', false);
if (isEnableAppendCoverLink){
const checkIntervalAppendCoverLink = setInterval(()=>appendCoverLink(), 300);
// Cleanup after 30 seconds
setTimeout(() => {
clearInterval(checkIntervalAppendCoverLink);
observer.disconnect(); // Optionally disconnect the observer after cleanup
}, 30*1000);
}
})();