oglasi: manj vsiljivo in bolje placano
- vse enote se zahtevajo sele ~200 px pred vstopom v vidno polje (IntersectionObserver); prej so se vse pushale ob nalaganju strani, tudi tiste globoko spodaj, ki jih uporabnik ni nikoli videl. Odlocitev o praznosti (3 s) tece sele od zahteve, ne od mounta. - clanek: zgornji pas odstranjen (potiskal je naslovnico in naslov pod pregib, placan pa je najslabse). Prvi oglas pride sele v besedilu. - clanek: stevilo oglasov vezano na dolzino — 2 osnovna + 1 na vsakih 5 blokov cez 10, strop 5. Mediana (9 blokov) ostane pri 2, dolgi clanki dobijo do 5. Prvi je fluid in-article, vsi nadaljnji nov InContentDisplayAd (odzivni display, visja kreativa). - TopBannerAd na seznamih: odzivni namesto trdih 90 px. - mobilni lepljivi pas: odzivni vodoravni (do 110 px) namesto trdih 320x50, z onAdStatus — prazen se ne izrise. - PageSideAds: en desni 300x600 sticky z onAdStatus namesto dveh 160x600 brez zaznave (prazna je pustila luknjo); prikaze se od 1700 px navzgor. - interstitial odstranjen s horoskopa (prekrivalo cez zaslon, zaklenilo scroll); interstitial-ad.tsx in neuporabljen parallax-ad.tsx izbrisana. CMP ostaja nedotaknjen po dogovoru.
This commit is contained in:
@@ -11,6 +11,7 @@ interface AdSenseProps {
|
||||
style?: Record<string, string>;
|
||||
layout?: string;
|
||||
layoutKey?: string;
|
||||
eager?: boolean;
|
||||
onAdStatus?: (filled: boolean) => void;
|
||||
}
|
||||
|
||||
@@ -22,6 +23,7 @@ export default function AdSense({
|
||||
style,
|
||||
layout,
|
||||
layoutKey,
|
||||
eager = false,
|
||||
onAdStatus,
|
||||
}: AdSenseProps) {
|
||||
const adRef = useRef<HTMLModElement>(null);
|
||||
@@ -29,44 +31,72 @@ export default function AdSense({
|
||||
|
||||
useEffect(() => {
|
||||
if (pushed.current) return;
|
||||
try {
|
||||
const adsbygoogle = (window as any).adsbygoogle || [];
|
||||
adsbygoogle.push({});
|
||||
pushed.current = true;
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
if (!onAdStatus) return;
|
||||
const ins = adRef.current;
|
||||
if (!ins) return;
|
||||
|
||||
let io: IntersectionObserver | null = null;
|
||||
let obs: MutationObserver | null = null;
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
let decided = false;
|
||||
|
||||
// Google postavi data-ad-status na "filled", "unfilled" ali "unfill-optimized".
|
||||
// Ce statusa ni, se zanesemo na dejansko visino. Odlocimo se enkrat.
|
||||
let decided = false;
|
||||
const decide = (filled: boolean) => {
|
||||
if (decided) return;
|
||||
decided = true;
|
||||
obs.disconnect();
|
||||
clearTimeout(timer);
|
||||
onAdStatus(filled);
|
||||
obs?.disconnect();
|
||||
if (timer) clearTimeout(timer);
|
||||
onAdStatus?.(filled);
|
||||
};
|
||||
// Visina samega <ins> ni dokaz - nekatere enote imajo rezerviran prostor.
|
||||
// Napolnjen oglas ima notri okvir (iframe) z dejansko visino.
|
||||
const hasFrame = () => {
|
||||
const f = ins.querySelector("iframe");
|
||||
const f = ins?.querySelector("iframe");
|
||||
return !!f && (f as HTMLIFrameElement).clientHeight > 10;
|
||||
};
|
||||
const check = () => {
|
||||
const st = ins.getAttribute("data-ad-status") || "";
|
||||
const st = ins?.getAttribute("data-ad-status") || "";
|
||||
if (st === "filled") return decide(true);
|
||||
if (st.startsWith("unfill")) return decide(false);
|
||||
if (hasFrame()) return decide(true);
|
||||
};
|
||||
const obs = new MutationObserver(check);
|
||||
obs.observe(ins, { attributes: true, childList: true, subtree: true, attributeFilter: ["data-ad-status", "style"] });
|
||||
const timer = setTimeout(() => decide(hasFrame()), 3000);
|
||||
check();
|
||||
return () => { obs.disconnect(); clearTimeout(timer); };
|
||||
|
||||
// Oglas zahtevamo sele, ko se enota priblizuje vidnemu polju (200 px prej).
|
||||
// Enote globoko pod pregibom so se prej zahtevale takoj ob nalaganju strani in
|
||||
// vecine uporabnik ni nikoli videl - nizka vidnost pomeni nizjo ceno.
|
||||
// Odlocitev o praznosti steje sele od zahteve naprej, ne od mounta.
|
||||
const push = () => {
|
||||
if (pushed.current) return;
|
||||
try {
|
||||
const adsbygoogle = (window as any).adsbygoogle || [];
|
||||
adsbygoogle.push({});
|
||||
pushed.current = true;
|
||||
} catch (e) {
|
||||
}
|
||||
if (!onAdStatus || !ins) return;
|
||||
obs = new MutationObserver(check);
|
||||
obs.observe(ins, { attributes: true, childList: true, subtree: true, attributeFilter: ["data-ad-status", "style"] });
|
||||
timer = setTimeout(() => decide(hasFrame()), 3000);
|
||||
check();
|
||||
};
|
||||
|
||||
if (eager || !ins || typeof IntersectionObserver === "undefined") {
|
||||
push();
|
||||
} else {
|
||||
io = new IntersectionObserver((entries) => {
|
||||
if (entries.some((e) => e.isIntersecting)) {
|
||||
io?.disconnect();
|
||||
io = null;
|
||||
push();
|
||||
}
|
||||
}, { rootMargin: "200px 0px" });
|
||||
io.observe(ins);
|
||||
}
|
||||
|
||||
return () => {
|
||||
io?.disconnect();
|
||||
obs?.disconnect();
|
||||
if (timer) clearTimeout(timer);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -123,8 +153,35 @@ export function TopBannerAd() {
|
||||
slot="4154017639"
|
||||
format="horizontal"
|
||||
className="w-full"
|
||||
style={{ display: "block", width: "100%", height: "90px", textAlign: "center", margin: "0 auto" }}
|
||||
responsive={false}
|
||||
style={{ display: "block", width: "100%", textAlign: "center", margin: "0 auto" }}
|
||||
responsive
|
||||
eager
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Navaden odzivni display v toku besedila. Za razliko od fluid in-article enote
|
||||
// dobi Google celotno sirino in lahko postrezhe visoko kreativo (336x280, 300x600),
|
||||
// torej en cel oglas, mimo katerega bralec odskrola - namesto vec drobnih.
|
||||
export function InContentDisplayAd() {
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
if (filled === false) return null;
|
||||
const shown = filled === true;
|
||||
return (
|
||||
<div
|
||||
className={shown ? "my-6 rounded-md overflow-hidden bg-card border border-card-border" : ""}
|
||||
data-testid="ad-in-content-display"
|
||||
>
|
||||
{shown && (
|
||||
<div className="text-[9px] text-muted-foreground/40 text-center pt-1 uppercase tracking-widest">Anzeige</div>
|
||||
)}
|
||||
<AdSense
|
||||
slot="4154017639"
|
||||
format="auto"
|
||||
responsive
|
||||
style={{ display: "block", textAlign: "center" }}
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
@@ -173,26 +230,29 @@ export function MultiplexAd() {
|
||||
);
|
||||
}
|
||||
|
||||
// En sam desni stolpec 300x600 namesto dveh 160x600.
|
||||
// 160x600 je stara velikost z malo povprasevanja, dve enoti sta si konkurirali,
|
||||
// prazna pa je brez onAdStatus pustila luknjo. Prikaze se sele, ko je dovolj
|
||||
// prostora desno od vsebine (min-[1700px]).
|
||||
export function PageSideAds({ contentHalfWidth = 640 }: { contentHalfWidth?: number }) {
|
||||
const gap = 10;
|
||||
const gap = 24;
|
||||
const offset = contentHalfWidth + gap;
|
||||
const [filled, setFilled] = useState<boolean | null>(null);
|
||||
if (filled === false) return null;
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none" style={{ left: `calc(50% - ${offset}px - 160px)` }} data-testid="ad-left-sidebar">
|
||||
<div
|
||||
className="hidden min-[1700px]:flex fixed top-0 bottom-0 w-[300px] z-10 items-center justify-center pointer-events-none"
|
||||
style={{ right: `calc(50% - ${offset}px - 300px)` }}
|
||||
data-testid="ad-right-sidebar"
|
||||
>
|
||||
<div className="pointer-events-auto">
|
||||
<AdSense
|
||||
slot="2398082838"
|
||||
format="vertical"
|
||||
style={{ display: "block", width: "160px", height: "600px" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden 2xl:flex fixed top-0 bottom-0 w-[160px] z-10 items-center justify-center pointer-events-none" style={{ right: `calc(50% - ${offset}px - 160px)` }} data-testid="ad-right-sidebar">
|
||||
<div className="pointer-events-auto">
|
||||
<AdSense
|
||||
slot="2398082838"
|
||||
format="vertical"
|
||||
style={{ display: "block", width: "160px", height: "600px" }}
|
||||
responsive={false}
|
||||
style={{ display: "block", width: "300px", height: "600px" }}
|
||||
onAdStatus={setFilled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user