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:
FrankZamora
2025-12-10 15:48:20 -06:00
parent 555541b2a0
commit 179a83e9cd
14 changed files with 3303 additions and 201 deletions

View File

@@ -64,10 +64,12 @@ final class AdsensePlacementRenderer
}
// 4. Generar CSS (usando CSSGeneratorService)
$lazyEnabled = ($settings['behavior']['lazy_loading_enabled'] ?? true) === true;
$css = $this->cssGenerator->generate(
".roi-ad-slot",
[
'display' => 'block',
'display' => $lazyEnabled ? 'none' : 'block',
'width' => '100%',
'min_width' => '300px',
'margin_top' => '1.5rem',
@@ -76,6 +78,12 @@ final class AdsensePlacementRenderer
]
);
// CSS para slots con lazy loading que reciben contenido
if ($lazyEnabled) {
$css .= $this->cssGenerator->generate('.roi-ad-slot.roi-ad-filled', ['display' => 'block']);
$css .= $this->cssGenerator->generate('.roi-ad-slot.roi-ad-empty', ['display' => 'none']);
}
// 5. Generar HTML del anuncio
$html = $this->buildAdHTML(
$settings,
@@ -161,6 +169,7 @@ final class AdsensePlacementRenderer
{
$publisherId = esc_attr($settings['content']['publisher_id'] ?? '');
$delayEnabled = ($settings['forms']['delay_enabled'] ?? true) === true;
$lazyEnabled = ($settings['behavior']['lazy_loading_enabled'] ?? true) === true;
if (empty($publisherId)) {
return '';
@@ -174,9 +183,10 @@ final class AdsensePlacementRenderer
$scriptType = $delayEnabled ? 'text/plain' : 'text/javascript';
$dataAttr = $delayEnabled ? ' data-adsense-push' : '';
$lazyAttr = $lazyEnabled ? ' data-ad-lazy="true"' : '';
$locationClass = 'roi-ad-' . esc_attr(str_replace('_', '-', $location));
return $this->generateAdMarkup($format, $publisherId, $slotId, $locationClass, $visClasses, $scriptType, $dataAttr);
return $this->generateAdMarkup($format, $publisherId, $slotId, $locationClass, $visClasses, $scriptType, $dataAttr, $lazyAttr);
}
/**
@@ -217,68 +227,69 @@ final class AdsensePlacementRenderer
string $locationClass,
string $visClasses,
string $scriptType,
string $dataAttr
string $dataAttr,
string $lazyAttr = ''
): string {
$allClasses = trim("{$locationClass} {$visClasses}");
return match($format) {
'display' => $this->adDisplay($client, $slot, 728, 90, $allClasses, $scriptType, $dataAttr),
'display-large' => $this->adDisplay($client, $slot, 970, 250, $allClasses, $scriptType, $dataAttr),
'display-square' => $this->adDisplay($client, $slot, 300, 250, $allClasses, $scriptType, $dataAttr),
'in-article' => $this->adInArticle($client, $slot, $allClasses, $scriptType, $dataAttr),
'autorelaxed' => $this->adAutorelaxed($client, $slot, $allClasses, $scriptType, $dataAttr),
default => $this->adAuto($client, $slot, $allClasses, $scriptType, $dataAttr),
'display' => $this->adDisplay($client, $slot, 728, 90, $allClasses, $scriptType, $dataAttr, $lazyAttr),
'display-large' => $this->adDisplay($client, $slot, 970, 250, $allClasses, $scriptType, $dataAttr, $lazyAttr),
'display-square' => $this->adDisplay($client, $slot, 300, 250, $allClasses, $scriptType, $dataAttr, $lazyAttr),
'in-article' => $this->adInArticle($client, $slot, $allClasses, $scriptType, $dataAttr, $lazyAttr),
'autorelaxed' => $this->adAutorelaxed($client, $slot, $allClasses, $scriptType, $dataAttr, $lazyAttr),
default => $this->adAuto($client, $slot, $allClasses, $scriptType, $dataAttr, $lazyAttr),
};
}
private function adDisplay(string $c, string $s, int $w, int $h, string $cl, string $t, string $a): string
private function adDisplay(string $c, string $s, int $w, int $h, string $cl, string $t, string $a, string $lazy = ''): string
{
return sprintf(
'<div class="roi-ad-slot %s">
'<div class="roi-ad-slot %s"%s>
<ins class="adsbygoogle" style="display:inline-block;width:%dpx;height:%dpx"
data-ad-client="%s" data-ad-slot="%s"></ins>
<script type="%s"%s>(adsbygoogle = window.adsbygoogle || []).push({});</script>
</div>',
esc_attr($cl), $w, $h, esc_attr($c), esc_attr($s), $t, $a
esc_attr($cl), $lazy, $w, $h, esc_attr($c), esc_attr($s), $t, $a
);
}
private function adAuto(string $c, string $s, string $cl, string $t, string $a): string
private function adAuto(string $c, string $s, string $cl, string $t, string $a, string $lazy = ''): string
{
return sprintf(
'<div class="roi-ad-slot %s">
'<div class="roi-ad-slot %s"%s>
<ins class="adsbygoogle" style="display:block;min-height:250px"
data-ad-client="%s" data-ad-slot="%s"
data-ad-format="auto" data-full-width-responsive="true"></ins>
<script type="%s"%s>(adsbygoogle = window.adsbygoogle || []).push({});</script>
</div>',
esc_attr($cl), esc_attr($c), esc_attr($s), $t, $a
esc_attr($cl), $lazy, esc_attr($c), esc_attr($s), $t, $a
);
}
private function adInArticle(string $c, string $s, string $cl, string $t, string $a): string
private function adInArticle(string $c, string $s, string $cl, string $t, string $a, string $lazy = ''): string
{
return sprintf(
'<div class="roi-ad-slot %s">
'<div class="roi-ad-slot %s"%s>
<ins class="adsbygoogle" style="display:block;text-align:center;min-height:200px"
data-ad-layout="in-article" data-ad-format="fluid"
data-ad-client="%s" data-ad-slot="%s"></ins>
<script type="%s"%s>(adsbygoogle = window.adsbygoogle || []).push({});</script>
</div>',
esc_attr($cl), esc_attr($c), esc_attr($s), $t, $a
esc_attr($cl), $lazy, esc_attr($c), esc_attr($s), $t, $a
);
}
private function adAutorelaxed(string $c, string $s, string $cl, string $t, string $a): string
private function adAutorelaxed(string $c, string $s, string $cl, string $t, string $a, string $lazy = ''): string
{
return sprintf(
'<div class="roi-ad-slot %s">
'<div class="roi-ad-slot %s"%s>
<ins class="adsbygoogle" style="display:block;min-height:280px"
data-ad-format="autorelaxed"
data-ad-client="%s" data-ad-slot="%s"></ins>
<script type="%s"%s>(adsbygoogle = window.adsbygoogle || []).push({});</script>
</div>',
esc_attr($cl), esc_attr($c), esc_attr($s), $t, $a
esc_attr($cl), $lazy, esc_attr($c), esc_attr($s), $t, $a
);
}