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>
This commit is contained in:
306
Assets/Js/adsense-loader.legacy.js
Normal file
306
Assets/Js/adsense-loader.legacy.js
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Cargador Retrasado de AdSense
|
||||
*
|
||||
* Este script retrasa la carga de Google AdSense hasta que haya interacción
|
||||
* del usuario o se cumpla un timeout, mejorando el rendimiento de carga inicial.
|
||||
*
|
||||
* @package ROI_Theme
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Configuración
|
||||
const CONFIG = {
|
||||
timeout: 5000, // Timeout de fallback en milisegundos
|
||||
loadedClass: 'adsense-loaded',
|
||||
debug: true // TEMPORAL: Habilitado para diagnóstico
|
||||
};
|
||||
|
||||
// Estado
|
||||
let adsenseLoaded = false;
|
||||
let loadTimeout = null;
|
||||
|
||||
/**
|
||||
* Registra mensajes de debug si el modo debug está habilitado
|
||||
* @param {string} message - El mensaje a registrar
|
||||
*/
|
||||
function debugLog(message) {
|
||||
if (CONFIG.debug && typeof console !== 'undefined') {
|
||||
console.log('[AdSense Loader] ' + message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Carga los scripts de AdSense e inicializa los ads
|
||||
*/
|
||||
function loadAdSense() {
|
||||
// Prevenir múltiples cargas
|
||||
if (adsenseLoaded) {
|
||||
debugLog('AdSense ya fue cargado, omitiendo...');
|
||||
return;
|
||||
}
|
||||
|
||||
adsenseLoaded = true;
|
||||
debugLog('Cargando scripts de AdSense...');
|
||||
|
||||
// Limpiar el timeout si existe
|
||||
if (loadTimeout) {
|
||||
clearTimeout(loadTimeout);
|
||||
loadTimeout = null;
|
||||
}
|
||||
|
||||
// Remover event listeners para prevenir múltiples triggers
|
||||
removeEventListeners();
|
||||
|
||||
// Cargar etiquetas de script de AdSense y esperar a que cargue
|
||||
// IMPORTANTE: Debe esperar a que adsbygoogle.js cargue antes de ejecutar push
|
||||
loadAdSenseScripts(function() {
|
||||
debugLog('Biblioteca AdSense cargada, ejecutando push scripts...');
|
||||
|
||||
// Ejecutar scripts de push de AdSense
|
||||
executeAdSensePushScripts();
|
||||
|
||||
// Agregar clase loaded al body
|
||||
document.body.classList.add(CONFIG.loadedClass);
|
||||
|
||||
debugLog('Carga de AdSense completada');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Encuentra y carga todas las etiquetas de script de AdSense retrasadas
|
||||
* @param {Function} callback - Función a ejecutar cuando la biblioteca cargue
|
||||
*/
|
||||
function loadAdSenseScripts(callback) {
|
||||
const delayedScripts = document.querySelectorAll('script[data-adsense-script]');
|
||||
|
||||
if (delayedScripts.length === 0) {
|
||||
debugLog('No se encontraron scripts retrasados de AdSense');
|
||||
// Ejecutar callback de todas formas (puede haber ads sin script principal)
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('Se encontraron ' + delayedScripts.length + ' script(s) retrasado(s) de AdSense');
|
||||
|
||||
var scriptsLoaded = 0;
|
||||
var totalScripts = delayedScripts.length;
|
||||
|
||||
delayedScripts.forEach(function(oldScript) {
|
||||
const newScript = document.createElement('script');
|
||||
|
||||
// Copiar atributos
|
||||
if (oldScript.src) {
|
||||
newScript.src = oldScript.src;
|
||||
}
|
||||
|
||||
// Establecer atributo async
|
||||
newScript.async = true;
|
||||
|
||||
// Copiar crossorigin si está presente
|
||||
if (oldScript.getAttribute('crossorigin')) {
|
||||
newScript.crossorigin = oldScript.getAttribute('crossorigin');
|
||||
}
|
||||
|
||||
// Esperar a que cargue antes de ejecutar callback
|
||||
newScript.onload = function() {
|
||||
scriptsLoaded++;
|
||||
debugLog('Script cargado (' + scriptsLoaded + '/' + totalScripts + '): ' + newScript.src.substring(0, 50) + '...');
|
||||
if (scriptsLoaded === totalScripts && typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
newScript.onerror = function() {
|
||||
scriptsLoaded++;
|
||||
debugLog('Error cargando script: ' + newScript.src);
|
||||
if (scriptsLoaded === totalScripts && typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
// Reemplazar script viejo con el nuevo
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ejecuta scripts de push de AdSense retrasados
|
||||
*/
|
||||
function executeAdSensePushScripts() {
|
||||
const delayedPushScripts = document.querySelectorAll('script[data-adsense-push]');
|
||||
|
||||
if (delayedPushScripts.length === 0) {
|
||||
debugLog('No se encontraron scripts de push retrasados de AdSense');
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('Se encontraron ' + delayedPushScripts.length + ' script(s) de push retrasado(s)');
|
||||
|
||||
// Inicializar array adsbygoogle si no existe
|
||||
window.adsbygoogle = window.adsbygoogle || [];
|
||||
|
||||
delayedPushScripts.forEach(function(oldScript) {
|
||||
const scriptContent = oldScript.innerHTML;
|
||||
|
||||
// Crear y ejecutar nuevo script
|
||||
const newScript = document.createElement('script');
|
||||
newScript.innerHTML = scriptContent;
|
||||
newScript.type = 'text/javascript';
|
||||
|
||||
// Reemplazar script viejo con el nuevo
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Manejador de eventos para interacciones del usuario
|
||||
*/
|
||||
function handleUserInteraction() {
|
||||
debugLog('Interacción del usuario detectada');
|
||||
loadAdSense();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remueve todos los event listeners
|
||||
*/
|
||||
function removeEventListeners() {
|
||||
window.removeEventListener('scroll', handleUserInteraction, { passive: true });
|
||||
window.removeEventListener('mousemove', handleUserInteraction, { passive: true });
|
||||
window.removeEventListener('touchstart', handleUserInteraction, { passive: true });
|
||||
window.removeEventListener('click', handleUserInteraction, { passive: true });
|
||||
window.removeEventListener('keydown', handleUserInteraction, { passive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Agrega event listeners para interacciones del usuario
|
||||
*/
|
||||
function addEventListeners() {
|
||||
debugLog('Agregando event listeners para interacción del usuario');
|
||||
|
||||
// Evento de scroll - cargar en primer scroll
|
||||
window.addEventListener('scroll', handleUserInteraction, { passive: true, once: true });
|
||||
|
||||
// Movimiento de mouse - cargar cuando el usuario mueve el mouse
|
||||
window.addEventListener('mousemove', handleUserInteraction, { passive: true, once: true });
|
||||
|
||||
// Eventos táctiles - cargar en primer toque (móviles)
|
||||
window.addEventListener('touchstart', handleUserInteraction, { passive: true, once: true });
|
||||
|
||||
// Eventos de click - cargar en primer click
|
||||
window.addEventListener('click', handleUserInteraction, { passive: true, once: true });
|
||||
|
||||
// Eventos de teclado - cargar en primera pulsación de tecla
|
||||
window.addEventListener('keydown', handleUserInteraction, { passive: true, once: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Establece timeout de fallback para cargar AdSense después del tiempo especificado
|
||||
*/
|
||||
function setTimeoutFallback() {
|
||||
debugLog('Estableciendo timeout de fallback (' + CONFIG.timeout + 'ms)');
|
||||
|
||||
loadTimeout = setTimeout(function() {
|
||||
debugLog('Timeout alcanzado, cargando AdSense');
|
||||
loadAdSense();
|
||||
}, CONFIG.timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activa slots de AdSense insertados dinamicamente
|
||||
* Escucha el evento 'roi-adsense-activate' disparado por otros scripts
|
||||
*/
|
||||
function setupDynamicAdsListener() {
|
||||
window.addEventListener('roi-adsense-activate', function() {
|
||||
debugLog('Evento roi-adsense-activate recibido');
|
||||
|
||||
// Si AdSense aun no ha cargado, forzar carga ahora
|
||||
if (!adsenseLoaded) {
|
||||
debugLog('AdSense no cargado, forzando carga...');
|
||||
loadAdSense();
|
||||
return;
|
||||
}
|
||||
|
||||
// AdSense ya cargado - activar nuevos slots
|
||||
debugLog('Activando nuevos slots dinamicos...');
|
||||
activateDynamicSlots();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Activa slots de AdSense que fueron insertados despues de la carga inicial
|
||||
*/
|
||||
function activateDynamicSlots() {
|
||||
// Buscar scripts de push que aun no han sido ejecutados
|
||||
var pendingPushScripts = document.querySelectorAll('script[data-adsense-push][type="text/plain"]');
|
||||
|
||||
if (pendingPushScripts.length === 0) {
|
||||
debugLog('No hay slots pendientes por activar');
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('Activando ' + pendingPushScripts.length + ' slot(s) dinamico(s)');
|
||||
|
||||
// Asegurar que adsbygoogle existe
|
||||
window.adsbygoogle = window.adsbygoogle || [];
|
||||
|
||||
pendingPushScripts.forEach(function(oldScript) {
|
||||
try {
|
||||
// Crear nuevo script ejecutable
|
||||
var newScript = document.createElement('script');
|
||||
newScript.type = 'text/javascript';
|
||||
newScript.innerHTML = oldScript.innerHTML;
|
||||
|
||||
// Reemplazar el placeholder con el script real
|
||||
oldScript.parentNode.replaceChild(newScript, oldScript);
|
||||
} catch (e) {
|
||||
debugLog('Error activando slot: ' + e.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicializa el cargador retrasado de AdSense
|
||||
*/
|
||||
function init() {
|
||||
// =========================================================================
|
||||
// NUEVO: Siempre configurar listener para ads dinamicos
|
||||
// IMPORTANTE: Esto debe ejecutarse ANTES del early return
|
||||
// porque los ads dinamicos pueden necesitar activarse aunque
|
||||
// el delay global este deshabilitado
|
||||
// =========================================================================
|
||||
setupDynamicAdsListener();
|
||||
debugLog('Listener para ads dinamicos configurado');
|
||||
|
||||
// Verificar si el retardo de AdSense está habilitado
|
||||
if (!window.roiAdsenseDelayed) {
|
||||
debugLog('Retardo de AdSense no habilitado');
|
||||
return;
|
||||
}
|
||||
|
||||
debugLog('Inicializando cargador retrasado de AdSense');
|
||||
|
||||
// Verificar si la página ya está interactiva o completa
|
||||
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
||||
debugLog('Página ya cargada, iniciando listeners');
|
||||
addEventListeners();
|
||||
setTimeoutFallback();
|
||||
} else {
|
||||
// Esperar a que el DOM esté listo
|
||||
debugLog('Esperando a DOMContentLoaded');
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
debugLog('DOMContentLoaded disparado');
|
||||
addEventListeners();
|
||||
setTimeoutFallback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Iniciar inicialización
|
||||
init();
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user