Crear un vigilante de precio y stock con MutationObserver
Quieres ese artículo, pero solo al precio adecuado — o quieres saber el momento en que un producto agotado vuelve. Refrescar la pestaña todo el día no es manera de vivir. Este artículo crea un vigilante de precio y stock con una regla JS de JustZix: un MutationObserver vigila el elemento relevante, compara el valor y te avisa cuando se cumple tu condición.
Cómo funciona el vigilante
El plan tiene cuatro partes, y cada una se corresponde con una sección de abajo:
- Elige un selector estable para el elemento de precio o de stock.
- Adjunta un
MutationObserverpara reaccionar a los cambios sin sondear. - Parsea el texto a un número comparable o un booleano.
- Avísate — un banner en la página o una notificación del sistema — y recuerda el último valor.
Todo se ejecuta en el cliente, dentro de la pestaña. Mantén la página del producto abierta (una pestaña fijada es ideal) y la regla hace la vigilancia.
Elegir un selector estable
El vigilante es tan fiable como su selector. Abre DevTools, panel Elements, y encuentra el elemento que contiene el precio. Prefiere, en este orden:
- Un anclaje semántico:
[itemprop="price"],[data-price],[data-testid="product-price"]. Estos rara vez cambian. - Un ID estable:
#priceblock_ourprice,#product-price. - Una clase con significado:
.price-now,.product__price.
Evita las clases con hash (.css-1a2b3c) y las cadenas largas de descendientes — se rompen en el siguiente despliegue. Muchas tiendas también incrustan datos estructurados en un bloque <script type="application/ld+json">; si el DOM visible es un caos, parsear ese JSON suele ser la ruta más estable.
Configurar el MutationObserver
Un MutationObserver dispara un callback cada vez que el subárbol vigilado cambia — sin sondeo, sin CPU desperdiciada. Vigila el nodo del precio en busca de cambios de texto e hijos:
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);
}
Las tiendas tipo single-page-app a veces reemplazan el nodo de precio entero al actualizar. Si tu observer se queda en silencio, observa un contenedor padre estable con subtree: true y vuelve a consultar el precio dentro del callback.
Parsear el precio a un número
El texto del precio es un caos: símbolos de moneda, separadores de miles, comas decimales. Redúcelo a un número limpio antes de comparar:
// 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);
}
Comparar contra un umbral
Ahora la lógica de decisión. Compara el precio parseado con tu objetivo y avisa solo cuando realmente lo cruce — y solo una vez por bajada, para que no te bombardee en cada mutación menor del 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 + ')');
}
}
Vigilar la vuelta a stock
Mismo patrón, señal distinta. En lugar de un número vigilas un booleano — ¿está habilitado el botón de comprar?, ¿ha desaparecido la etiqueta de «agotado»?:
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.');
}
}
Avisarte a ti mismo
Un aviso es inútil si no lo ves. Usa dos canales: un banner en la página imposible de pasar por alto, más una notificación del sistema para cuando la pestaña está en segundo plano.
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();
}
}
El z-index de 2147483647 es el entero de 32 bits máximo — garantiza que el banner quede por encima de cualquier cosa que renderice el sitio. Las notificaciones del sistema necesitan permiso, que el navegador solo concede a partir de un gesto del usuario o de un origen aprobado previamente, así que la primera ejecución puede limitarse a pedirlo; el banner te cubre mientras tanto.
Persistir el último valor visto
Viste localStorage usado arriba, y hace un trabajo real. Sobrevive a las recargas, así que el vigilante sabe si un cambio es genuinamente nuevo. También deduplica los avisos — comparas contra el valor guardado y solo avisas en un cruce real. Indéxalo por página (location.pathname) para que varios vigilantes de producto puedan coexistir sin sobrescribirse entre sí.
Conectarlo en JustZix
- Crea una regla con el patrón de URL del producto exacto, por ejemplo
https://shop.example.com/product/12345*. - Pega el script combinado en la pestaña JS — configura
SELECTORyTARGETpara ese producto. - Fija la pestaña y déjala abierta; el observer reacciona cada vez que la tienda actualiza el DOM.
- ¿Quieres una recomprobación periódica incluso sin cambios en el DOM? Añade un
setIntervalque recargue la página cada pocos minutos.
Mira también
- Desactivar patrones oscuros — elimina la urgencia falsa para ver el precio real.
- Simular respuestas de API — prueba tu vigilante contra un feed de precios falso.
- Casos de uso de JustZix para más ideas de automatización.
Deja de refrescar esa pestaña. Instala JustZix, coloca el script del vigilante en una regla por producto y deja que el navegador te avise en el momento en que el precio sea el adecuado.
Valora este artículo
Sin valoraciones — sé el primero.