改善 Leaflet popup 樣式並強化清理保護

- 新增 isLeafletDescendant 輔助函式,統一所有清理流程的地圖元素豁免邏輯
- 加入 popup / pane 內容保護 CSS,避免被廣告清除規則誤殺
- 移除 popup 預設的白色三角 tip,wrapper 改用自訂 padding 控制上下留白
- MutationObserver 略過 Leaflet 後代節點變動,避免點 marker 時誤觸發清理
- 版本 1.1.7 → 1.1.15

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-27 14:19:48 +08:00
parent d184c6d4f5
commit 2cd3031560

View File

@@ -1,7 +1,7 @@
// ==UserScript==
// @name TWPKInfo 廣告清除器
// @namespace http://tampermonkey.net/
// @version 1.1.7
// @version 1.1.15
// @description 移除頂部導覽列、底部功能列與空白列,保留地圖工具
// @author AdBlocker
// @match https://twpkinfo.com/*
@@ -19,6 +19,21 @@
let cleanupStarted = false;
let tooltipTimer = null;
// 判斷元素是否屬於 Leaflet 地圖/彈窗系統(清理時必須跳過)
function isLeafletDescendant(element) {
if (!element || !element.closest) {
return false;
}
return Boolean(
element.closest('#map') ||
element.closest('.leaflet-container') ||
element.closest('.leaflet-pane') ||
element.closest('.leaflet-popup') ||
element.closest('.leaflet-tooltip') ||
element.closest('.leaflet-control')
);
}
// 清理用 CSS
function addCleanupCSS() {
if (document.getElementById('twpk-ad-blocker-cleanup')) {
@@ -131,6 +146,47 @@
visibility: visible !important;
}
/* 保護 Leaflet 彈出視窗popup所有內容避免被廣告清除規則誤殺 */
html body .leaflet-pane,
html body .leaflet-pane *,
html body .leaflet-popup,
html body .leaflet-popup * {
visibility: visible !important;
opacity: 1 !important;
}
/* 只強制 popup 內文rows的顯示避免被 display:none 規則蓋掉;
不再覆寫 .leaflet-popup / .leaflet-popup-content-wrapper / tip 容器尺寸,
讓 TWPKInfo 與 Leaflet 自身的排版(含底部 tip 三角)正常運作 */
html body .leaflet-popup-content > *,
html body .leaflet-popup-content > * > *,
html body .leaflet-popup-content > * > * > *,
html body .leaflet-popup-content > * > * > * > * {
display: revert !important;
height: auto !important;
min-height: 0 !important;
max-height: none !important;
overflow: visible !important;
}
/* 把 Leaflet popup 預設的白色三角 tip 拿掉,並把 wrapper 改成自己控制留白 */
html body .leaflet-popup-content-wrapper {
overflow: hidden !important;
border-radius: 12px !important;
padding: 14px 4px !important;
}
/* 內層 content margin 歸零,全部交給 wrapper 的 padding 負責 */
html body .leaflet-popup-content {
margin: 0 14px !important;
}
html body .leaflet-popup-tip-container,
html body .leaflet-popup-tip,
html body [class*="leaflet-popup-tip"] {
display: none !important;
}
#map,
.leaflet-container {
top: 0 !important;
@@ -339,8 +395,7 @@
if (div.id === 'map' ||
div.classList.contains('leaflet-container') ||
div.classList.contains('leaflet-control-attribution') ||
div.closest('#map') ||
div.closest('.leaflet-container')) {
isLeafletDescendant(div)) {
return;
}
@@ -366,7 +421,10 @@
// 策略2通過內容識別
document.querySelectorAll('div').forEach(div => {
const classList = div.className.toLowerCase();
if (isLeafletDescendant(div)) {
return;
}
const classList = (typeof div.className === 'string' ? div.className : '').toLowerCase();
if (classList.includes('bottom') &&
(classList.includes('nav') || classList.includes('bar') || classList.includes('menu') || classList.includes('toolbar')) &&
!classList.includes('pokemon') &&
@@ -420,8 +478,7 @@
candidates.forEach(element => {
if (element.id === 'map' ||
element.classList.contains('leaflet-container') ||
element.closest('#map') ||
element.closest('.leaflet-container')) {
isLeafletDescendant(element)) {
return;
}
@@ -460,8 +517,18 @@
let hasElementChanges = false;
mutations.forEach((mutation) => {
// 略過 Leaflet popup / pane 內部的變動,避免誤判清理
if (mutation.target && mutation.target.nodeType === 1 &&
isLeafletDescendant(mutation.target)) {
return;
}
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) { // 元素節點
if (isLeafletDescendant(node)) {
return;
}
hasElementChanges = true;
const rect = node.getBoundingClientRect();
const style = window.getComputedStyle(node);
@@ -532,8 +599,7 @@
document.querySelectorAll('div').forEach(div => {
if (div.id === 'map' ||
div.classList.contains('leaflet-container') ||
div.closest('#map') ||
div.closest('.leaflet-container')) {
isLeafletDescendant(div)) {
return;
}
@@ -624,8 +690,7 @@
document.querySelectorAll('div').forEach(div => {
if (div.id === 'map' ||
div.classList.contains('leaflet-container') ||
div.closest('#map') ||
div.closest('.leaflet-container')) {
isLeafletDescendant(div)) {
return;
}