← Todos los artículos

API y helpers

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:

  1. Elige un selector estable para el elemento de precio o de stock.
  2. Adjunta un MutationObserver para reaccionar a los cambios sin sondear.
  3. Parsea el texto a un número comparable o un booleano.
  4. 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:

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

  1. Crea una regla con el patrón de URL del producto exacto, por ejemplo https://shop.example.com/product/12345*.
  2. Pega el script combinado en la pestaña JS — configura SELECTOR y TARGET para ese producto.
  3. Fija la pestaña y déjala abierta; el observer reacciona cada vez que la tienda actualiza el DOM.
  4. ¿Quieres una recomprobación periódica incluso sin cambios en el DOM? Añade un setInterval que recargue la página cada pocos minutos.

Mira tambié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.

Pruébalo tú mismo

Instala JustZix y pega cualquier snippet de este artículo. Dos minutos de cero a una regla funcionando en todos tus dispositivos.

Obtener JustZix

Funciones · Cómo funciona · Ejemplos · Casos de uso