HTML5 Lightbox build guide
Lightbox creatives are a rich, interactive cross-screen creative format that runs only on the Google Display Network.
HTML5 Lightbox creatives use the Studio fullscreen expanding API. Normally, an expanding creative has an expanded state with preset size. Lightbox creatives dynamically resize to fill a majority of the available device or browser viewing area. The remaining area outside the creative is covered with a semi-transparent grey background, and a close button is displayed at the top right corner.
For coverage across all screens, at a bare minimum, create invitation states in sizes 320x50 and 300x250. For wider coverage, consider creating additional popular invitation sizes (listed from most available inventory to least inventory): 728x90, 160x600, 336x280, 300x600, 468x60, 970x90, 120x600.
Lifecycle of an HTML5 Lightbox creative
This section is a general overview of the various lifecycle sequences of a Lightbox creative. There are many references in this guide to the Enabler; the Enabler is the core code library of Studio. Think of it as the brain of the creative. All components and API calls must go through the Enabler. The Enabler library is required for all rich media creatives. For more details, see this page about the Enabler.
Page load
User-initiated expansion
Creatives may only request expansion in response to a user interaction, whether click, tap, or hover. When the user interacts, the creative’s event handler should request the available fullscreen dimensions from the Enabler, in order to use them to request expansion.
In response to the query, the Enabler will dispatch the FULLSCREEN_DIMENSIONS
event to the creative. The available width and height can be used to calculate custom expanded dimensions, which can be passed in as parameters to Enabler.requestFullscreenExpand(width, height)
.
Requesting expansion from the Enabler does not automatically guarantee that expansion will happen. The user has a chance to cancel the expansion, if they did not intend to interact with the creative. When expansion does happen, it will be signaled by a viewport resize event. The creative can take this opportunity to animate the expansion, or immediately display the expanded state.
Expanded state responsive resize
If the user changes their browser size or device orientation while the creative is expanded, the creative will have a chance to request a new size for its expanded state. The creative will be notified that the expanded dimensions are changing via the FULLSCREEN_DIMENSIONS
event. In response to this event the creative should call Enabler.setResponsiveSize(width, height)
to update its expanded dimensions.
When the size of the creative’s container is updated with its new dimensions, a viewport resize event will fire. The creative should take this chance to update its expanded layout given the new dimensions.
Externally-initiated expansion
FULLSCREEN_DIMENSIONS
event. Similarly to when a responsive resize happens, the creative should calculate its expanded dimensions and set them by calling Enabler.setResponsiveSize(width, height)
. The rest of the expansion will proceed in the same way as a user-initiated expansion would. There will be a viewport resize event, and the creative should render its expanded state when it occurs.Collapse
Enabler.requestFullscreenCollapse()
, but collapse is also often externally-initiated. In both cases, the FULLSCREEN_COLLAPSE_START
StudioEvent will fire. At this point, the creative may take the opportunity to animate its collapse. Once any animations are done, the collapse should be completed by calling Enabler.finishFullscreenCollapse()
. This method will trigger a viewport resize back to the creative’s collapsed dimensions, and the creative should render its collapsed state at this point if it has not done so already.Set up your files
Download starter files from the Rich Media Gallery to use as a starting point. The starter files contain boilerplate code used for implementing the fullscreen expansion lifecycle, and places to fill in code that renders the creative. You can also use Google Web Designer templates to build your creative; they come with a separate build guide.
- Add the Enabler in your HTML file.
- On all invitation states with partially black, white, or transparent backgrounds, add a contrasting border. For example, in an invitation state with a white background, add a 1 pixel black border.
- Stop all animation and video 30 seconds or less after load.
Create an HTML5 Lightbox creative
Lightbox creatives expand out in all directions to fill whatever screen they're displayed on. The smaller collapsed size of a Lightbox creative fits within a standard ad placement on a desktop or mobile website, or in a mobile app. When a user hovers for 2 seconds on desktop or taps on devices, the ad expands outside the collapsed size to fill a majority of the desktop's browser window or mobile device's screen and is always centered.
The main part of this guide explains how to build a Lightbox creative, filling in the methods in the Rich Media Gallery starter files. At the end, there's an optional section that goes into more detail about the boilerplate code included at the end of the starter files. This boilerplate code shouldn't need any changes.
Set up
- Create two div elements in your HTML file, and give each a sensible ID.
Code snippet<body> <!-- Invitation (Collapsed) State --> <div id="invitation-state"> <!-- Add Collapsed content here --> <span id="expand-cta"></span> </div> <!-- Engaged (Expanded) State --> <div id="lightbox-state"> <!-- Add Expanded content here --> </div> </body>
- Style the objects you created in the CSS using the IDs you assigned them.
Code snippet#invitation-state { background-color: #fff; background-repeat: no-repeat; display: none; height: 250px; left: 0; position: absolute; top: 0; width: 300px; } #lightbox-state { background-color: #fff; display: none; height: 100%; left: 0; position: absolute; top: 0; width: 100%; } #expand-cta { color: #666; cursor: pointer; left: 0; position: absolute; top: 0; width: 100%; }
-
Specify chargeable interstitial events. These events should be a subset of the ones that you report via
Enabler.counter(eventName)
when the user interacts with the creative. Creatives paying on the Cost Per Engagement bidding model are charged as follows:-
For Lightbox Expandable ad slots, creatives are charged on expansion.
-
For non-expandable ad slots (such as interstitials), creatives are charged on any user interaction. In interstitial inventory the creative must register the counter names for events triggered by user interaction (including exits) in order to allow the creative to bill on those events.
In the starter files, specify chargeable events in the chargeableInterstitialEvents array. If the creative is serving as an interstitial, these events will be registered for you when the Enabler loads.
Code snippet
// Update the line below to specify all counter events in the
// expanded state of the ad that are triggered by user interaction.
var chargeableInterstitialEvents = [
'Background Click',
'A user-initiated event',
'Another user-initiated event'
];
-
- Load any extra modules. For example, the GDN module must be loaded to check if the creative is serving in-app or as an interstitial. If using the Studio Video module, you should add
studio.module.ModuleId.VIDEO
to the list of modules to load.
Code snippet
// Add additional studio modules you want to load to this list.
// Do not remove the GDN module.
var modulesToLoad = [studio.module.ModuleId.GDN];
Initialize the ad components
The preInit()
method in the starter files is automatically called on the page load event of the ad frame and is responsible for calling some internal boilerplate code in addition to setting up the DOM.
Code snippet
function preInit() {
// Do not change these lines.
internalPreInit();
setupDom();
};
The setupDom()
method called in the code snippet above is used to set up references to the creative’s DOM components, such as the main container. It can also be used to perform any necessary initialization steps for custom components or features. The starter files will also set up variables such as creative.isInterstitial
and creative.isTouchable
that can be used during ad initialization.
Lightbox ads run on both desktop and mobile devices, so you'll need two background images for the two different invitation states. One background image should have a 'tap to expand' CTA for smartphones/tablets, and the other image should have a 'hover to expand' CTA for desktop display. Use the creative.isTouchable
variable to differentiate between these two cases.
You should also tell the Enabler which user action should cause the creative to expand when viewed on desktop computers. Add one of these lines of code, depending on what action should expand your creative:
- To expand your creative on mouse hover, add:
Enabler.setHint('expansionTrigger', 'onHover');
- Or, to expand your creative on mouse click, add:
Enabler.setHint('expansionTrigger', 'onClick');
Code snippet
function setupDom() {
// Set up references to DOM components.
creative.dom = {};
creative.dom.mainContainer = document.getElementById('main-container');
creative.dom.lightboxExit = document.getElementById('lightbox-exit');
creative.dom.lightboxState = document.getElementById('lightbox-state');
creative.dom.lightboxFeature = document.getElementById('lightbox-feature');
creative.dom.invitationState = document.getElementById('invitation-state');
creative.dom.expandCta = document.getElementById('expand-cta');
// It is not safe to use other creative.* fields until after the init()
// method has been called, which indicates the Enabler has initialized
// and loaded its additional modules.
if (creative.isTouchable) {
creative.dom.expandCta.textContent = 'Tap to expand';
} else {
creative.dom.expandCta.textContent = 'Hover to expand';
}
}
Part of the boilerplate code that executed on load (internalPreInit()
) will listen for events which signal that the Enabler and additional Enabler modules are initialized. After the Enabler and its modules have initialized, the init()
method will be called.
Code snippet
function init() {
// Do not change these lines.
internalInit();
addListeners();
show();
}
Add event listeners
After the creative’s DOM has been set up, the next step is to add event listeners to the creative. Any handlers for user interactions with the expanded state of the creative should report the interactions to the Enabler.
If fullscreen expansion is supported, the onExpandHandler()
method should be bound to either click or mouseover events. The handler will request the fullscreen dimensions from the Enabler before requesting expansion. Note that the touchstart and touchend events should not be used to trigger expansion, as users may inadvertently touch the ad while scrolling past it. This sort of interaction should not trigger expansion.
The addFullscreenExpansionListeners()
method will add all of the event listeners that are necessary for fullscreen expansion. You do not need to modify this method or add any extra listeners for the fullscreen expansion lifecycle to work.
Code snippet
Add listeners in the addListeners()
method.
function addListeners() {
// Add listeners on the expanded state to report interactions.
creative.dom.lightboxFeature.addEventListener('click', function() {
Enabler.counter('Background Click');
}, false);
// Add more listeners to the expanded state elements.
creative.dom.lightboxExit.addEventListener('click', exitClickHandler);
if (creative.isFullscreenSupported) {
if (creative.isTouchable) {
// Do not use the touchstart or touchend events to trigger
// expansion on touch devices. Creatives using those events trigger
// expansion when users touch the ad while scrolling past, which is a
// violation of lightbox creative policies. Instead, use the click event,
// which will not fire when the user touches the ad while scrolling.
creative.dom.expandButton.addEventListener(
'click', onExpandHandler, false);
} else {
creative.dom.expandButton.addEventListener(
'mouseover', onExpandHandler, false);
}
internalAddFullscreenExpansionListeners();
}
}
Calculate custom fullscreen expanded dimensions
Whenever the user interacts with the creative, the onExpandHandler()
will request the available fullscreen dimensions from the Enabler. The available width and height are passed to calculateCustomDimensions()
, in case you want to expand to smaller than or up to the maximum dimensions. This can be useful for maintaining an aspect ratio in the expanded state. Alternatively, to expand to the full available dimensions, the method can return the available width and height.
calculateCustomDimensions()
may also be called outside of the expansion lifecycle. For example, the Enabler might have detected that the user has changed orientations or resized their browser window. It will also be called when there has been an external expansion request. This method provides an opportunity to recalculate the size of the creative’s expanded container. The creative should provide new expanded dimensions regardless of whether it is currently expanded or collapsed.
Note that when the creative is serving in-app, even if custom dimensions are provided, the Enabler will use the full available fullscreen dimensions when expanding. The creative.isInApp
variable can be used to detect this state.
Code snippet
The calculateCustomDimensions()
returns an object with 'width' and 'height' properties denoting the expanded dimensions of the creative.
function calculateCustomDimensions(availableWidth, availableHeight) {
// When served in-app, creatives will ignore these width/height values and
// expand to the dimensions of the screen.
var dimensions = {
'width': availableWidth,
'height': availableHeight
};
return dimensions;
}
Handle viewport resize events
Viewport resize events occur when the creative expands and collapses. Resize events are how the creative knows to render its expanded and collapsed states. They also happen when the browser window resizes or the device changes orientation, and the expanded dimensions have changed. These cases can be differentiated in the following manner:
- When the creative is collapsing, the creative.isCollapsing variable will be true. The creative should render its collapsed state by calling
renderCollapsedView()
. - When creative.isCollapsing is false, the creative is expanding, or there has been a resize event while the creative is expanded. These two cases are combined because the creative should respond to both by rendering its expanded state via the
renderExpandedView()
. Optionally, if you would like to add an animation before expanding, thecreative.isExpanded
variable will be false when the creative is expanding.
During expansion and collapse, make sure to start and stop the ‘Panel Expansion’ timer.
Code snippet
function viewportResizeHandler() {
if (creative.isCollapsing) {
Enabler.stopTimer('Panel Expansion');
Enabler.counter('Collapse Ctr');
renderCollapsedView();
} else {
if (!creative.isExpanded) {
// Optionally animate the expansion before rendering the expanded layout.
Enabler.startTimer('Panel Expansion');
Enabler.counter('Expanded Ctr');
}
renderExpandedView();
}
}
Render the creative
The renderExpandedView()
and renderCollapsedView()
methods are used to show the expanded and collapsed views of the creative, as well as update the creative.isExpanded
state variable. The renderCollapsedView()
method will also be used to show the creative when it becomes visible on the page for the first time.
Code snippet
function renderExpandedView() {
creative.dom.lightboxState.style.display = 'block';
creative.dom.lightboxExit.style.display = 'block';
creative.dom.invitationState.style.display = 'none';
creative.isExpanded = true;
}
function renderCollapsedView() {
creative.dom.lightboxState.style.display = 'none';
creative.dom.lightboxExit.style.display = 'none';
creative.dom.invitationState.style.display = 'block';
creative.isExpanded = false;
creative.isCollapsing = false;
}
(Optional) Add a collapse animation
Unlike expansion animations, collapse animations should happen before the creative’s frame resizes to the collapsed dimensions. Any collapse animations can be added in the collapseStartHandler()
method, which marks the start of the process of collapsing the creative.
Code snippet
function collapseStartHandler() {
// Optionally perform an animation before collapsing.
// Note the creative is collapsing so the viewportResizeHandler knows how to
// redraw the creative.
creative.isCollapsing = true;
Enabler.finishFullscreenCollapse();
}
Handle exit clicks
The exitClickHandler()
method handles exit clicks on the creative’s expanded state.
When creatives are serving in-app to iOS environments, there's a known issue: calling collapse on an exit causes the landing page to close immediately. By policy, the Lightbox should collapse when the user clicks to a landing page. However on iOS in-app environments this prevents the user from actually getting to the landing page, so we allow for the ad to remain expanded.
Code snippet
function exitClickHandler() {
// In-app iOS environments known issue: Calling collapse on an exit
// causes the landing page to close immediately so we leave it expanded
// on exit.
// In other environments it is required that the ad collapses on exit
// click per policy.
if (!(creative.isInApp && isMobile.iOS())) {
Enabler.requestFullscreenCollapse();
}
Enabler.stopTimer('Panel Expansion');
Enabler.exit('Background Exit');
}
This marks the end of the section of the guide that covers code that needs to be filled in by developers. The rest of the guide provides an overview of the boilerplate code included in the starter files.
Boilerplate code you shouldn't need to change
The boilerplate functions contained in the starter files handle much of the initialization and fullscreen expansion lifecycle. These methods should not need to be changed, but notes on them are provided here as a reference.
Initialization and loading modules
The internalPreInit()
method is the window onload handler, and the first method to be called. It sets up creative state variables such as creative.isTouchable and creative.isExpanded via the setupCreativeState()
helper method. Next, once the Enabler has been initialized, the modules in the modulesToLoad
array are loaded.
Code snippet
function internalPreInit() {
// Initialize state variables such as creative.isExpanded.
setupCreativeState();
if (Enabler.isInitialized()) {
// Load the GDN module before initializing.
Enabler.loadModules(modulesToLoad, modulesLoadedHandler);
} else {
Enabler.addEventListener(studio.events.StudioEvent.INIT, function() {
Enabler.loadModules(modulesToLoad, modulesLoadedHandler);
});
}
}
Once all modules have finished loading, the Enabler calls the modulesLoadedHandler()
method, which was provided as a callback. Here, the Google Display Network configuration object is retrieved and used to set the creative.isInApp
and creative.isInterstitial
state variables.
After the expansion mode is set, Enabler.queryFullscreenSupport()
is called in order to determine whether fullscreen expansion is supported, and the creative.isFullscreenSupported
variable is set.
Code snippet
function modulesLoadedHandler() {
studio.sdk.gdn.getInitializedConfigByCallback(function(gdnConfig) {
creative.isInApp =
(gdnConfig.getClientEnvironment().browserClass ==
studio.sdk.gdn.BrowserClass.IN_APP);
// gdnConfig.isInterstitial is set via a callback function.
gdnConfig.isInterstitial(function(state) {
creative.isInterstitial = state;
Enabler.addEventListener(
studio.events.StudioEvent.FULLSCREEN_SUPPORT, function(e) {
creative.isFullscreenSupported = e.supported;
init();
});
Enabler.queryFullscreenSupport();
});
});
}
Next, the init()
method is called (described above), which then goes through the internalInit() method to bind the viewportResizeHandler()
to the window resize event. Additionally, the expansion mode is set to Lightbox.
As you'll notice, there's no close button added in the content. A standard Lightbox close button is automatically displayed over the creative when served. Setting the expansion mode to Lightbox ensures that the following Lightbox functionalities are implemented when the tag is served in a GDN environment:
- Two-second hover delay
- Expanding progress bar on the invitation (collapsed) state
- Expands the engaged state to the center of the webpage
- Close button (top right corner) on the engaged state
- Gray translucent background on the engaged state
Finally, the creative is rendered. If the creative is serving as an interstitial, the expanded view is rendered via the renderExpandedView()
method, and the events specified in the chargeableInterstitialEvents
array are registered as chargeable events with the Enabler. If the creative is not serving as an interstitial, the invitation state is rendered by calling renderCollapsedView()
once the creative is visible.
Code snippet
function internalShow() {
if (creative.isInterstitial) {
// Render the creative in its expanded state within its current ad slot.
renderExpandedView();
// Register the chargeable events that have been specified in
// chargeableInterstitialEvents above with the Enabler.
registerChargeableInterstitialEvents();
} else {
// Polite loading
if (Enabler.isVisible()) {
renderCollapsedView();
}
else {
Enabler.addEventListener(
studio.events.StudioEvent.VISIBLE, renderCollapsedView);
}
}
}
Expansion and collapse event handlers
The fullscreen expansion lifecycle is an asynchronous process that progresses through events. The internalAddFullscreenExpansionListeners()
method, which is called from addListeners()
above, adds the appropriate methods as handlers for the FULLSCREEN_DIMENSIONS
, FULLSCREEN_EXPAND_START
, and FULLSCREEN_COLLAPSE_START
events. This method also calls Enabler.setResponsiveExpanding()
to signal that the creative will behave responsively when expanded to fullscreen. It is important to call this method before the first time the creative expands, since it will be ignored if called afterwards.
As noted above, the onExpandHandler()
method is bound to user interactions. If the user interacts with the creative while it is collapsed, the handler will start the process of expanding by requesting the fullscreen dimensions.
Code snippet
function onExpandHandler() {
if (creative.isExpanded) {
// If the creative is already expanded, do nothing.
return;
}
// Request the available dimensions to use for expanding.
creative.shouldRequestExpansion = true;
Enabler.queryFullscreenDimensions();
}
The fullscreenDimensionsHandler()
method is bound to the FULLSCREEN_DIMENSIONS
event. It retrieves the available dimensions from the event object, then passes them to the calculateCustomDimensions()
method. If there was a user interaction and creative.shouldRequestExpansion
is true, expansion will be requested. Otherwise, the updated expanded dimensions will be passed to Enabler.setResponsiveSize(width, height)
, as part of responding to a responsive resize.
Code snippet
function fullscreenDimensionsHandler(e) {
// When served in-app, creatives will always expand or resize fullscreen, and
// the custom expanded dimensions determined below are ignored.
var availableWidth = e.width;
var availableHeight = e.height;
// Calculate custom dimensions.
var dimensions = calculateCustomDimensions(availableWidth, availableHeight);
// If we asked for the dimensions in response to a user interaction,
// expand now.
// Creatives served in-app will ignore specified width/height values.
if (creative.shouldRequestExpansion) {
Enabler.requestFullscreenExpand(dimensions.width, dimensions.height);
creative.shouldRequestExpansion = false;
} else {
// There was a responsive resize event, so update the size of the ad.
Enabler.setResponsiveSize(dimensions.width, dimensions.height);
}
}
The expandStartHandler()
method is bound to the FULLSCREEN_EXPAND_START
event, and responds by calling Enabler.finishFullscreenExpand()
to complete the expansion process.
Note: you cannot rely on the FULLSCREEN_EXPAND_START
event to render the expanded state of the creative. There is no guarantee that this event will happen before the ad frame is resized. Instead, the expanded state of the creative should be rendered in response to window resize events, as detailed above in the viewportResizeHandler()
method.
Code snippet
function expandStartHandler() {
Enabler.finishFullscreenExpand();
}
The collapseStartHandler()
is called in response to the FULLSCREEN_COLLAPSE_START
event, which marks the beginning of the collapse process. It is detailed more above.
Mobile detection
isMobile
and hasTouchScreen()
are helper methods to detect whether or not the creative is in a mobile environment.
Code snippet
var isMobile = {
Android: function() {
return navigator.userAgent.match(/Android/i);
},
BlackBerry: function() {
return navigator.userAgent.match(/BlackBerry/i);
},
iOS: function() {
return navigator.userAgent.match(/iPhone|iPad|iPod/i);
},
Opera: function() {
return navigator.userAgent.match(/Opera Mini/i);
},
Windows: function() {
return navigator.userAgent.match(/IEMobile/i);
},
any: function() {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() ||
isMobile.Opera() || isMobile.Windows()) &&
hasTouchScreen();
}
};
var hasTouchScreen = function() {
var n = !1, o = function(n) {
return -1 !== window.navigator.userAgent.toLowerCase().indexOf(n);
};
return ('ontouchstart' in window || navigator.maxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0) &&
(o('NT 5') || o('NT 6.1') || o('NT 6.0') || (n = !0)),
n;
};