• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / 21926473833

11 Feb 2026 11:01PM UTC coverage: 96.212% (-0.003%) from 96.215%
21926473833

Pull #14467

github

d17627
web-flow
Update ixBidAdapter.js
Pull Request #14467: Prebid 11: add `adUnit.element` option

54240 of 66604 branches covered (81.44%)

119 of 142 new or added lines in 50 files covered. (83.8%)

6 existing lines in 4 files now uncovered.

208149 of 216344 relevant lines covered (96.21%)

69.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.0
/modules/bidViewabilityIO.js
1
import { logMessage } from '../src/utils.js';
1✔
2
import { config } from '../src/config.js';
3
import * as events from '../src/events.js';
4
import {EVENTS} from '../src/constants.js';
5
import {getAdUnitElement} from '../src/utils/adUnits.js';
6

7
const MODULE_NAME = 'bidViewabilityIO';
1✔
8
const CONFIG_ENABLED = 'enabled';
1✔
9

10
// IAB numbers from: https://support.google.com/admanager/answer/4524488?hl=en
11
const IAB_VIEWABLE_DISPLAY_TIME = 1000;
1✔
12
const IAB_VIEWABLE_DISPLAY_LARGE_PX = 242000;
1✔
13
export const IAB_VIEWABLE_DISPLAY_THRESHOLD = 0.5
1✔
14
export const IAB_VIEWABLE_DISPLAY_LARGE_THRESHOLD = 0.3;
1✔
15

16
const CLIENT_SUPPORTS_IO = window.IntersectionObserver && window.IntersectionObserverEntry && window.IntersectionObserverEntry.prototype &&
1✔
17
    'intersectionRatio' in window.IntersectionObserverEntry.prototype;
18

19
const supportedMediaTypes = [
1✔
20
  'banner'
21
];
22

23
export const isSupportedMediaType = (bid) => {
1✔
24
  return supportedMediaTypes.indexOf(bid.mediaType) > -1;
3✔
25
}
26

27
const _logMessage = (message) => {
1✔
28
  return logMessage(`${MODULE_NAME}: ${message}`);
5✔
29
}
30

31
// returns options for the iO that detects if the ad is viewable
32
export const getViewableOptions = (bid) => {
1✔
33
  if (bid.mediaType === 'banner') {
4✔
34
    return {
2✔
35
      root: null,
36
      rootMargin: '0px',
37
      threshold: bid.width * bid.height > IAB_VIEWABLE_DISPLAY_LARGE_PX ? IAB_VIEWABLE_DISPLAY_LARGE_THRESHOLD : IAB_VIEWABLE_DISPLAY_THRESHOLD
2✔
38
    }
39
  }
40
}
41

42
// markViewed returns a function what will be executed when an ad satisifes the viewable iO
43
export const markViewed = (bid, entry, observer) => {
1✔
44
  return () => {
3✔
45
    observer.unobserve(entry.target);
1✔
46
    events.emit(EVENTS.BID_VIEWABLE, bid);
1✔
47
    _logMessage(`id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode} was viewed`);
1✔
48
  }
49
}
50

51
// viewCallbackFactory creates the callback used by the viewable IntersectionObserver.
52
// When an ad comes into view, it sets a timeout for a function to be executed
53
// when that ad would be considered viewed per the IAB specs. The bid that was rendered
54
// is passed into the factory, so it can pass it into markViewed, so that it can be included
55
// in the BID_VIEWABLE event data. If the ad leaves view before the timer goes off, the setTimeout
56
// is cancelled, an the bid will not be marked as viewed. There's probably some kind of race-ish
57
// thing going on between IO and setTimeout but this isn't going to be perfect, it's just going to
58
// be pretty good.
59
export const viewCallbackFactory = (bid) => {
1✔
60
  return (entries, observer) => {
2✔
61
    entries.forEach(entry => {
1✔
62
      if (entry.isIntersecting) {
3✔
63
        _logMessage(`viewable timer starting for id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode}`);
1✔
64
        entry.target.view_tracker = setTimeout(markViewed(bid, entry, observer), IAB_VIEWABLE_DISPLAY_TIME);
1✔
65
      } else {
66
        _logMessage(`id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode} is out of view`);
2✔
67
        if (entry.target.view_tracker) {
2✔
68
          clearTimeout(entry.target.view_tracker);
1✔
69
          _logMessage(`viewable timer stopped for id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode}`);
1✔
70
        }
71
      }
72
    });
73
  };
74
};
75

76
export const init = () => {
1✔
77
  config.getConfig(MODULE_NAME, conf => {
1✔
78
    if (conf[MODULE_NAME][CONFIG_ENABLED] && CLIENT_SUPPORTS_IO) {
×
79
      // if the module is enabled and the browser supports Intersection Observer,
80
      // then listen to AD_RENDER_SUCCEEDED to setup IO's for supported mediaTypes
81
      events.on(EVENTS.AD_RENDER_SUCCEEDED, ({doc, bid, id}) => {
×
82
        if (isSupportedMediaType(bid)) {
×
83
          const viewable = new IntersectionObserver(viewCallbackFactory(bid), getViewableOptions(bid));
×
NEW
84
          const element = getAdUnitElement(bid);
×
85
          viewable.observe(element);
×
86
        }
87
      });
88
    }
89
  });
90
}
91

92
init()
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc