Kleinanzeigen.de Grid Viewer

04/08/2023, 21:46:24

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Kleinanzeigen.de Grid Viewer
// @namespace   Violentmonkey Scripts
// @match       https://www.kleinanzeigen.de/*
// @grant       none
// @version     1.1
// @author      -
// @description 04/08/2023, 21:46:24
// @run-at      Document-Start
// @license     MIT
// ==/UserScript==

var modalStyle = document.createElement('style');
modalStyle.innerHTML = `
  /* Styles for the modal overlay */
  .modal-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%; /* Make the overlay width 100% of the viewport */
    height: 100%;
    background-color: rgba(0, 0, 0, 0.7);
    justify-content: center;
    align-items: center;
    z-index: 9999; /* Adjust the z-index to ensure the overlay is on top */
  }

  /* Styles for the modal content */
  .modal-content {
    background-color: white;
    padding: 20px;
    border-radius: 5px;
    box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.2);
    width: 90%; /* Set the width of the content within the overlay */
    max-width: 1200px; /* Optionally, set a maximum width for the content */
    max-height: 90%;
    overflow: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 20px;
    align-items: end;
  }
`;
document.head.appendChild(modalStyle);

// Define a function to extract the product information from a listing element
function getProductInfo(listing) {
  var imageBox = listing.querySelector(".imagebox.srpimagebox"); // Updated selector
  var image = imageBox.querySelector("img"); // Select the img element inside the imagebox
  var imageSrc = image ? image.getAttribute("src") : "";
  // Get the link element
  var link = listing.querySelector(".ellipsis");
  // Get the link href
  var linkHref = link ? link.href : "";
  // Get the link text
  var linkText = link ? link.textContent : "";
  // Get the price element
  var priceElement = listing.querySelector(".aditem-main--middle--price-shipping--price");
  // Get the price text
  var priceText = priceElement ? priceElement.textContent : "";
  // Return an object with the product information
  return {
    image: imageSrc,
    url: linkHref,
    title: linkText,
    price: priceText
  };
}

// Define a function to loop through all the listings on the page and add them to the products array
function getProducts() {
  // Create a local products array
  var products = [];
  // Get all the listing elements
  var listings = document.querySelectorAll(".ad-listitem .aditem");
  // Loop through each listing element
  for (var i = 0; i < listings.length; i++) {
    // Get the current listing element
    var listing = listings[i];
    // Get the product information from the listing element
    var productInfo = getProductInfo(listing);
    // Add the product information to the products array
    products.push(productInfo);
  }
  // Return the products array
  return products;
}

// Define a function to create the modal overlay
function createModalOverlay() {
  var modalOverlay = document.createElement('div');
  modalOverlay.className = 'modal-overlay';

  var modalContent = document.createElement('div');
  modalContent.className = 'modal-content';

  var closeButton = document.createElement('button');
  closeButton.innerHTML = 'X';
  closeButton.style.position = 'absolute';
  closeButton.style.right = '10px';
  closeButton.style.top = '10px';
  closeButton.onclick = function () {
    modalOverlay.style.display = 'none';
  };

  modalContent.appendChild(closeButton);
  modalOverlay.appendChild(modalContent);
  document.body.appendChild(modalOverlay);

  return modalOverlay;
}

// Define a function to add products to the modal content
function addProductsToModal(products, modalContent) {
  for (var i = 0; i < products.length; i++) {
    var product = products[i];

    var productCard = document.createElement('div');
    productCard.className = 'product-card';
    productCard.innerHTML = `
      <a href="${product.url}" target="_blank">
        <img src="${product.image}" alt="Product Image"><br/>
        ${product.title}<br/>
        <span>${product.price}</span>
      </a>
    `;

    modalContent.appendChild(productCard);
  }
}

var isFetching = false;
var currentPageURL = window.location.href;

function loadNextPageProducts() {
  // If a fetch is already in progress, return early
  if (isFetching) {
    return;
  }

  isFetching = true;

  var currentPage = currentPageURL.includes('seite:') ? parseInt(currentPageURL.split('seite:')[1]) : 1;
  var nextPage = currentPage + 1;
  var nextPageURL;

  if (currentPageURL.includes('seite:')) {
    nextPageURL = currentPageURL.replace(/(seite:)\d+/, 'seite:' + nextPage);
  } else {
    var urlParts = currentPageURL.split('/');
    urlParts.splice(-1, 0, 'seite:' + nextPage);
    nextPageURL = urlParts.join('/');
  }

  fetch(nextPageURL)
    .then(response => response.text())
    .then(html => {
      var parser = new DOMParser();
      var nextPageDocument = parser.parseFromString(html, 'text/html');
      var nextPageListings = nextPageDocument.querySelectorAll(".ad-listitem .aditem");

      var products = [];
      nextPageListings.forEach(listing => {
        var productInfo = getProductInfo(listing);
        products.push(productInfo);
      });

      var modalContent = document.querySelector('.modal-content');
      addProductsToModal(products, modalContent);
      // When the fetch is complete, set isFetching back to false
      isFetching = false;
      // Update currentPageURL to the URL of the next page
      currentPageURL = nextPageURL;
    }).catch(error => {
      console.error('Error:', error);

      // If an error occurs, set isFetching back to false
      isFetching = false;
    });
}

function showModal() {
  var modalOverlay = document.querySelector('.modal-overlay');
  var modalContent;

  if (!modalOverlay) {
    modalOverlay = createModalOverlay();
  }

  modalContent = modalOverlay.querySelector('.modal-content');

  var products = getProducts();
  addProductsToModal(products, modalContent);

  function loadNextPageIfNearBottom() {
    var buffer = 100; // Number of pixels from the bottom to start loading next page
    if (
      modalContent.scrollTop + modalContent.clientHeight >=
      modalContent.scrollHeight - buffer
    ) {
      loadNextPageProducts();
    }
  }

  // Add a scroll event listener to the modal overlay
  modalContent.addEventListener('scroll', loadNextPageIfNearBottom);

  // Show the modal overlay
  modalOverlay.style.display = 'flex';
}

var targetDiv = document.querySelector('.l-container-row.contentbox-unpadded.no-bg');

if (targetDiv !== null) {
  var triggerButton = document.createElement('button');
  triggerButton.innerHTML = 'Show Grid';
  triggerButton.style.display = 'block';
  triggerButton.style.margin = '0 auto';
  triggerButton.onclick = showModal;
  targetDiv.insertBefore(triggerButton, targetDiv.firstChild);
}