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:
- Wybierz stabilny selektor dla elementu ceny lub stanu magazynowego.
- Podepnij
MutationObserver, abyś reagował na zmiany bez odpytywania. - Sparsuj tekst do porównywalnej liczby lub wartości boolean.
- 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:
- Zaczep semantyczny:
[itemprop="price"],[data-price],[data-testid="product-price"]. Te rzadko się zmieniają. - Stabilny ID:
#priceblock_ourprice,#product-price. - Sensowną klasę:
.price-now,.product__price.
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
- Utwórz regułę ze wzorcem URL dokładnie tego produktu, np.
https://shop.example.com/product/12345*. - Wklej połączony skrypt do zakładki JS — ustaw
SELECTORiTARGETdla tego produktu. - Przypnij kartę i zostaw ją otwartą; obserwator reaguje, gdy sklep aktualizuje DOM.
- Chcesz okresowego ponownego sprawdzenia nawet bez zmian DOM? Dodaj
setInterval, który przeładowuje stronę co kilka minut.
Zobacz też
- Wyłącz dark patterns — usuń fałszywą pilność, abyś widział prawdziwą cenę.
- Mockuj odpowiedzi API — przetestuj swój watcher pod fałszywym strumieniem cen.
- Zastosowania JustZix — więcej pomysłów na automatyzację.
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.