您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Zeigt den Preis pro m² und färbt Anzeigen dynamisch nach €/m²-Preis (grün/gelb/rot).
当前为
// ==UserScript== // @name Kleinanzeigen m² Preis Rechner & Farbkodierer V3 // @namespace http://tampermonkey.net/ // @version 1.4 // @description Zeigt den Preis pro m² und färbt Anzeigen dynamisch nach €/m²-Preis (grün/gelb/rot). // @author Dein Name // @license MIT // @match https://www.kleinanzeigen.de/s-wohnung-mieten/* // @match https://www.kleinanzeigen.de/s-haus-mieten/* // @match https://www.kleinanzeigen.de/s-wohnung-kaufen/* // @match https://www.kleinanzeigen.de/s-haus-kaufen/* // @match https://www.kleinanzeigen.de/s-wg-zimmer/* // @match https://www.kleinanzeigen.de/s-immobilien/* // @match https://www.kleinanzeigen.de/s-grundstuecke-gaerten/* // @grant GM_addStyle // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // CSS für die Anzeige des m²-Preises und die Hintergrundfarben GM_addStyle(` .sqm-price-display { font-size: 1.4em; color: #20242C; font-weight: bold; margin-left: 10px; } .aditem-main--middle--price-shipping { display: flex; align-items: baseline; flex-wrap: wrap; } .sqm-price-container { margin-left: auto; padding-left: 5px; } /* Klassen für die Hintergrundfarben der Artikel */ .sqm-price-low { background-color: rgba(144, 238, 144, 0.4) !important; } /* Hellgrün */ .sqm-price-medium { background-color: rgba(255, 210, 100, 0.4) !important; } /* Helles Orange/Gelb */ .sqm-price-high { background-color: rgba(255, 127, 127, 0.4) !important; } /* Hellrot */ .sqm-price-uniform { background-color: rgba(200, 200, 200, 0.3) !important; } /* Hellgrau, wenn alle Preise gleich */ `); function parsePrice(priceString) { if (!priceString) return null; const cleanedPrice = priceString .replace(/\s*VB\s*/i, '') .replace(/\s*€\s*/g, '') .replace(/\./g, '') .replace(/,/g, '.') .trim(); if (cleanedPrice.toLowerCase() === 'zu verschenken') return 0; if (cleanedPrice.toLowerCase() === 'auf anfrage' || cleanedPrice.toLowerCase() === 'anfrage') return null; const price = parseFloat(cleanedPrice); return isNaN(price) ? null : price; } function parseArea(areaString) { if (!areaString) return null; const match = areaString.match(/([\d\.,]+)\s*m²/i); if (match && match[1]) { const cleanedArea = match[1].replace(/\./g, '').replace(/,/g, '.'); const area = parseFloat(cleanedArea); return isNaN(area) || area === 0 ? null : area; } return null; } function removeColorClasses(element) { element.classList.remove('sqm-price-low', 'sqm-price-medium', 'sqm-price-high', 'sqm-price-uniform'); } function processAdItems() { const items = document.querySelectorAll('article.aditem'); const itemsData = []; // Sammelt { element, pricePerSqm } für die aktuelle Seite // --- PASS 1: €/m² berechnen, Text anzeigen, Daten für Farbkodierung sammeln --- items.forEach(item => { // Vorherige Farbklassen vom Artikel entfernen (wichtig für Neuberechnung durch MutationObserver) removeColorClasses(item); const priceShippingContainer = item.querySelector('.aditem-main--middle--price-shipping'); if (!priceShippingContainer) return; // Bestehenden m²-Preistext entfernen, um Duplikate bei Neuausführung zu vermeiden const existingSqmTextContainer = priceShippingContainer.querySelector('.sqm-price-container'); if (existingSqmTextContainer) { existingSqmTextContainer.remove(); } const priceElement = priceShippingContainer.querySelector('.aditem-main--middle--price-shipping--price'); const priceText = priceElement ? priceElement.textContent : null; const tagsContainer = item.querySelector('.aditem-main--bottom p'); // Allgemeinerer Selektor let areaText = null; if (tagsContainer) { const simpleTags = tagsContainer.querySelectorAll('span.simpletag'); simpleTags.forEach(tag => { if (tag.textContent && tag.textContent.includes('m²')) { areaText = tag.textContent; } }); } const price = parsePrice(priceText); const area = parseArea(areaText); if (price !== null && area !== null && area > 0) { const pricePerSqm = price / area; itemsData.push({ element: item, pricePerSqm: pricePerSqm }); // Für spätere Farbkodierung speichern // m²-Preis Text erstellen und anzeigen const sqmPriceOuterContainer = document.createElement('div'); sqmPriceOuterContainer.classList.add('sqm-price-container'); const sqmPriceElement = document.createElement('span'); sqmPriceElement.classList.add('sqm-price-display'); sqmPriceElement.textContent = `${pricePerSqm.toFixed(2).replace('.', ',')} €/m²`; sqmPriceOuterContainer.appendChild(sqmPriceElement); priceShippingContainer.appendChild(sqmPriceOuterContainer); } }); // --- PASS 2: Schwellenwerte für Farbkodierung bestimmen --- if (itemsData.length < 1) { // Brauchen mindestens ein Item für die Berechnung return; } const allPricesPerSqm = itemsData.map(d => d.pricePerSqm); const minPrice = Math.min(...allPricesPerSqm); const maxPrice = Math.max(...allPricesPerSqm); const range = maxPrice - minPrice; let lowerBound, upperBound; if (range === 0) { // Alle €/m² Preise sind identisch itemsData.forEach(data => { data.element.classList.add('sqm-price-uniform'); // Eine neutrale Farbe }); return; } // Dynamische Aufteilung in drei Bereiche (Drittel der Preisspanne) [1] lowerBound = minPrice + range / 3; upperBound = minPrice + (2 * range) / 3; // --- PASS 3: Farben basierend auf Schwellenwerten anwenden --- itemsData.forEach(data => { const itemElement = data.element; // Das <article class="aditem"> Element const pricePerSqmVal = data.pricePerSqm; // Die removeColorClasses wurde bereits am Anfang der Schleife über `items` aufgerufen. // Daher ist es hier nicht erneut notwendig. if (pricePerSqmVal <= lowerBound) { itemElement.classList.add('sqm-price-low'); // Grün } else if (pricePerSqmVal <= upperBound) { itemElement.classList.add('sqm-price-medium'); // Gelb/Orange } else { itemElement.classList.add('sqm-price-high'); // Rot } }); } // Führe die Funktion initial aus processAdItems(); // MutationObserver für dynamisch geladene Inhalte const observerTargetNode = document.getElementById('srchrslt-adtable') || document.body; const observer = new MutationObserver(function(mutationsList) { for (const mutation of mutationsList) { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { let hasNewAdItems = false; for (const addedNode of mutation.addedNodes) { if (addedNode.nodeType === Node.ELEMENT_NODE && (addedNode.matches && (addedNode.matches('.aditem') || addedNode.querySelector('.aditem')))) { hasNewAdItems = true; break; } } if (hasNewAdItems) { setTimeout(processAdItems, 350); // Kleine Verzögerung kann helfen, dass alle neuen Elemente da sind return; } } } }); observer.observe(observerTargetNode, { childList: true, subtree: true }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址