← Wszystkie wpisy

API i helpers

Zbuduj watcher ceny i dostępności z MutationObserver

Chcesz tego przedmiotu, ale tylko w odpowiedniej cenie — albo chcesz wiedzieć w momencie, gdy wyprzedany produkt znów jest dostępny. Odświeżanie karty przez cały dzień to żaden sposób na życie. Ten artykuł buduje watcher ceny i dostępności za pomocą reguły JS w JustZix: MutationObserver obserwuje odpowiedni element, porównuje wartość i alarmuje Cię, gdy spełniony jest Twój warunek.

Jak działa watcher

Plan ma cztery części i każda odpowiada sekcji poniżej:

  1. Wybierz stabilny selektor dla elementu ceny lub stanu magazynowego.
  2. Podepnij MutationObserver, abyś reagował na zmiany bez odpytywania.
  3. Sparsuj tekst do porównywalnej liczby lub wartości boolean.
  4. Powiadom siebie — baner na stronie lub powiadomienie systemowe — i zapamiętaj ostatnią wartość.

Wszystko działa po stronie klienta w karcie. Trzymaj stronę produktu otwartą (przypięta karta jest idealna), a reguła zajmie się obserwowaniem.

Wybór stabilnego selektora

Watcher jest tak niezawodny jak jego selektor. Otwórz DevTools, panel Elements, i znajdź element trzymający cenę. Preferuj, w tej kolejności:

Unikaj zahaszowanych klas (.css-1a2b3c) i długich łańcuchów potomków — psują się przy następnym wdrożeniu. Wiele sklepów osadza też dane strukturalne w bloku <script type="application/ld+json">; jeśli widoczny DOM jest bałaganiarski, parsowanie tego JSON-a często jest najbardziej stabilną drogą.

Konfiguracja MutationObserver

MutationObserver wywołuje callback za każdym razem, gdy obserwowane poddrzewo się zmienia — bez odpytywania, bez marnowania CPU. Obserwuj węzeł ceny pod kątem zmian tekstu i dzieci:

const SELECTOR = '[itemprop="price"]';   // your stable selector
const priceEl = document.querySelector(SELECTOR);

if (!priceEl) {
  console.warn('[watcher] price element not found - check the selector');
} else {
  const observer = new MutationObserver(() => checkPrice(priceEl));
  observer.observe(priceEl, {
    childList: true,
    subtree: true,
    characterData: true
  });

  // Also check once on load - the price may already be a deal
  checkPrice(priceEl);
}

Sklepy typu single-page-app czasem zastępują cały węzeł ceny przy aktualizacji. Jeśli Twój obserwator milknie, obserwuj stabilny kontener nadrzędny z subtree: true i ponownie odpytaj cenę wewnątrz callbacku.

Parsowanie ceny do liczby

Tekst ceny jest bałaganiarski: symbole walut, separatory tysięcy, przecinki dziesiętne. Sprowadź go do czystej liczby przed porównaniem:

// Turn "$1,299.00" or "1 299,00 zl" into 1299
function parsePrice(text) {
  if (!text) return NaN;
  // Keep digits, dot and comma; drop everything else
  let s = text.replace(/[^0-9.,]/g, '');
  // If comma is the decimal separator, normalise it
  if (/,\d{2}$/.test(s)) s = s.replace(/\./g, '').replace(',', '.');
  else s = s.replace(/,/g, '');
  return parseFloat(s);
}

Porównywanie z progiem

Teraz logika decyzyjna. Porównaj sparsowaną cenę z celem i alarmuj tylko wtedy, gdy faktycznie go przekroczy — i tylko raz na spadek, abyś nie był spamowany przy każdej drobnej mutacji DOM:

const TARGET = 999;            // alert when price drops to/below this
const STORAGE_KEY = 'jz-watch-' + location.pathname;

function checkPrice(el) {
  const price = parsePrice(el.textContent);
  if (Number.isNaN(price)) return;

  const last = parseFloat(localStorage.getItem(STORAGE_KEY)) || Infinity;
  localStorage.setItem(STORAGE_KEY, String(price));

  console.log('[watcher] price now', price, '- last', last);

  // Fire only when we newly cross the threshold
  if (price <= TARGET && last > TARGET) {
    notify('Price drop! Now ' + price + ' (target ' + TARGET + ')');
  }
}

Obserwowanie powrotu do sprzedaży

Ten sam wzorzec, inny sygnał. Zamiast liczby obserwujesz wartość boolean — czy przycisk kupna jest aktywny, czy etykieta „brak w magazynie” zniknęła:

function checkStock() {
  const soldOut = document.querySelector('[class*="out-of-stock" i], .sold-out');
  const buyBtn = document.querySelector('button[name="add-to-cart"], .add-to-cart');
  const inStock = !soldOut && buyBtn && !buyBtn.disabled;

  const wasInStock = localStorage.getItem('jz-stock') === 'yes';
  localStorage.setItem('jz-stock', inStock ? 'yes' : 'no');

  if (inStock && !wasInStock) {
    notify('Back in stock! Grab it now.');
  }
}

Powiadamianie siebie

Alert jest bezużyteczny, jeśli go nie zobaczysz. Użyj dwóch kanałów: niemożliwego do przeoczenia banera na stronie oraz powiadomienia systemowego na wypadek, gdy karta jest w tle.

function notify(message) {
  // 1. On-page banner - always visible in the tab
  let bar = document.getElementById('jz-watch-bar');
  if (!bar) {
    bar = document.createElement('div');
    bar.id = 'jz-watch-bar';
    bar.style.cssText =
      'position:fixed;top:0;left:0;right:0;z-index:2147483647;' +
      'background:#16a34a;color:#fff;font:600 15px/1.4 sans-serif;' +
      'padding:12px 16px;text-align:center;';
    document.body.appendChild(bar);
  }
  bar.textContent = 'JZ watcher: ' + message;

  // 2. System notification - works when the tab is hidden
  if (Notification.permission === 'granted') {
    new Notification('JustZix watcher', { body: message });
  } else if (Notification.permission !== 'denied') {
    Notification.requestPermission();
  }
}

z-index równy 2147483647 to maksymalna 32-bitowa liczba całkowita — gwarantuje, że baner siedzi nad wszystkim, co renderuje strona. Powiadomienia systemowe wymagają uprawnienia, które przeglądarka przyznaje tylko z gestu użytkownika albo dla wcześniej zatwierdzonego źródła, więc pierwsze uruchomienie może po prostu o nie poprosić; baner zabezpiecza Cię w międzyczasie.

Utrwalanie ostatniej widzianej wartości

Powyżej widziałeś użyty localStorage i wykonuje on realną pracę. Przeżywa przeładowania, więc watcher wie, czy zmiana jest faktycznie nowa. Deduplikuje też alerty — porównujesz z zapisaną wartością i powiadamiasz tylko przy realnym przekroczeniu. Indeksuj go per strona (location.pathname), aby kilka watcherów produktów mogło współistnieć bez nadpisywania się nawzajem.

Spinanie tego w JustZix

  1. Utwórz regułę ze wzorcem URL dokładnie tego produktu, np. https://shop.example.com/product/12345*.
  2. Wklej połączony skrypt do zakładki JS — ustaw SELECTOR i TARGET dla tego produktu.
  3. Przypnij kartę i zostaw ją otwartą; obserwator reaguje, gdy sklep aktualizuje DOM.
  4. Chcesz okresowego ponownego sprawdzenia nawet bez zmian DOM? Dodaj setInterval, który przeładowuje stronę co kilka minut.

Zobacz też

Przestań odświeżać tę kartę. Zainstaluj JustZix, wrzuć skrypt watchera do reguły per produkt i pozwól przeglądarce powiedzieć Ci w momencie, gdy cena jest odpowiednia.

Oceń ten wpis

Brak ocen — oceń jako pierwszy.

Wypróbuj samodzielnie

Zainstaluj JustZix i wklej dowolny snippet z tego artykułu. Dwie minuty od zera do działającej reguły na wszystkich Twoich urządzeniach.

Pobierz JustZix

Funkcje · Jak to działa · Przykłady · Zastosowania