Files
roi-theme/openspec/changes/refactor-adsense-lazy-loading/tasks.md
FrankZamora 179a83e9cd feat(js): implement intersection observer lazy loading for adsense
- Add per-slot lazy loading with Intersection Observer API
- Implement fill detection via MutationObserver and data-ad-status
- Add configurable rootMargin and fillTimeout from database
- Generate dynamic CSS based on lazy_loading_enabled setting
- Add legacy mode fallback for browsers without IO support
- Include backup of previous implementation (adsense-loader.legacy.js)
- Add OpenSpec documentation with test plan (72 tests verified)

Schema changes:
- Add lazy_loading_enabled (boolean, default: true)
- Add lazy_rootmargin (select: 0-500px, default: 200)
- Add lazy_fill_timeout (select: 3000-10000ms, default: 5000)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 15:48:20 -06:00

5.0 KiB

Tasks: Refactorizar AdSense Lazy Loading

Nota: Las tareas siguen el flujo de 5 fases del proyecto. Pasos adicionales (FieldMapper, Asset Enqueuer, JS) son subtareas de infraestructura.


FASE 1: Schema JSON

1.1 Actualizar adsense-placement.json

  • Incrementar version de 1.4.0 a 1.5.0
  • Agregar campo lazy_loading_enabled al grupo behavior:
    "lazy_loading_enabled": {
      "type": "boolean",
      "label": "Lazy Loading de Anuncios",
      "default": true,
      "editable": true,
      "description": "Cargar anuncios individualmente al entrar al viewport (mejora fill rate)"
    }
    
  • Agregar campo lazy_rootmargin al grupo behavior (tipo select, default "200")
  • Agregar campo lazy_fill_timeout al grupo behavior (tipo select, default "5000")

1.2 Sincronizar a BD

  • Ejecutar wp roi-theme sync-component adsense-placement
  • Verificar campos creados en BD con valores default

FASE 2: Renderer (BD → HTML + CSS)

2.1 Actualizar AdsensePlacementRenderer.php

  • Leer lazy_loading_enabled desde settings
  • Generar CSS dinamico via CSSGeneratorService:
    if ($settings['lazy_loading_enabled']) {
        $this->cssGenerator->generate([
            '.roi-ad-slot' => ['display' => 'none'],
            '.roi-ad-slot.roi-ad-filled' => ['display' => 'block'],
            '.roi-ad-slot.roi-ad-empty' => ['display' => 'none'],
        ]);
    }
    
  • Agregar data-ad-lazy="true" al markup del slot si lazy enabled
  • Mantener compatibilidad con lazy_loading_enabled: false

2.2 Actualizar enqueue-scripts.php (AssetEnqueuer)

  • Leer settings de lazy loading desde BD
  • Usar wp_localize_script() para pasar config a JS:
    wp_localize_script('adsense-loader', 'roiAdsenseConfig', [
        'lazyEnabled' => (bool) $settings['lazy_loading_enabled'],
        'rootMargin' => (int) $settings['lazy_rootmargin'] . 'px 0px',
        'fillTimeout' => (int) $settings['lazy_fill_timeout'],
        'debug' => WP_DEBUG,
    ]);
    
  • Remover cualquier configuracion hardcodeada existente

2.3 Actualizar AdsensePlacementFieldMapper.php

  • Agregar lazy_loading_enabled al array de mappings
  • Agregar lazy_rootmargin al array de mappings
  • Agregar lazy_fill_timeout al array de mappings
  • Verificar tipos correctos (boolean, string, string → parseados en Enqueuer)

FASE 3: FormBuilder (UI Admin)

3.1 Actualizar AdsensePlacementFormBuilder.php

  • Agregar seccion "Lazy Loading" dentro del grupo Exclusions/Forms
  • Agregar toggle para lazy_loading_enabled
  • Agregar select para lazy_rootmargin (label: "Pre-carga (px)")
  • Agregar select para lazy_fill_timeout (label: "Timeout fill (ms)")
  • Agregar nota indicando que cambios requieren vaciar cache

FASE 4: JavaScript (Infrastructure)

4.1 Backup

  • Crear backup adsense-loader.legacy.js

4.2 Refactorizar adsense-loader.js

  • Refactorizar para leer config de window.roiAdsenseConfig
  • Implementar deteccion de soporte Intersection Observer
  • Implementar observeAdSlots() con Intersection Observer
  • Implementar activateAdSlot(slot) para activacion individual
  • Implementar MutationObserver para detectar contenido en <ins>
  • Implementar checkAdFill() con criterios concretos:
    • Verificar data-ad-status primero
    • Fallback a verificar children (iframe, div[id])
  • Implementar timeout por slot para marcar como vacio
  • Implementar manejo de error de red con retry (2s delay, max 1 retry)
  • Implementar fallback para navegadores sin soporte
  • Mantener carga diferida de adsbygoogle.js (primera activacion)
  • Mantener compatibilidad con lazyEnabled: false (modo legacy)

FASE 5: Validacion

5.1 Validacion de Arquitectura

  • Ejecutar php Shared/Infrastructure/Scripts/validate-architecture.php adsense-placement
  • Verificar que no hay CSS estatico nuevo
  • Verificar que config viene de BD, no hardcodeada
  • Verificar que FieldMapper tiene todos los campos

5.2 Testing Local

  • Probar con lazy_loading_enabled: true
  • Verificar ads cargan al scroll (DevTools Network)
  • Verificar slots vacios NO se muestran
  • Probar con lazy_loading_enabled: false (modo legacy)
  • Verificar fallback en navegador sin Intersection Observer
  • Medir Core Web Vitals con Lighthouse (antes/despues)

POST-IMPLEMENTACION

Deploy

  • Commit con mensaje descriptivo
  • Deploy a produccion
  • Ejecutar sync-component en produccion
  • Vaciar cache (Redis, W3TC)
  • Verificar funcionamiento en produccion

Monitoreo (24-48h)

  • Monitorear fill rate en AdSense dashboard
  • Verificar no hay errores en consola de usuarios
  • Comparar Core Web Vitals antes/despues

Cleanup

  • Remover debug: true de adsense-loader.js (ya pendiente)
  • Remover debug de ContentAdInjector.php (ya pendiente)
  • Remover adsense-loader.legacy.js si todo funciona (7+ dias)
  • Archivar esta especificacion en openspec/archive/