diff --git a/src/adUnit/VastAdUnit.js b/src/adUnit/VastAdUnit.js index ef1a6b71..6ac8a91b 100644 --- a/src/adUnit/VastAdUnit.js +++ b/src/adUnit/VastAdUnit.js @@ -68,6 +68,7 @@ class VastAdUnit extends VideoAdUnit { * Defaults to `window.console` * @param {Object} [options.hooks] - Optional map with hooks to configure the behaviour of the ad. * @param {Function} [options.hooks.createSkipControl] - If provided it will be called to generate the skip control. Must return a clickable [HTMLElement](https://developer.mozilla.org/es/docs/Web/API/HTMLElement) that is detached from the DOM. + * @param {Function} [options.hooks.createClickControl] - If provided it will be called to generate the click control. Must return a clickable [HTMLElement](https://developer.mozilla.org/es/docs/Web/API/HTMLElement) that is detached from the DOM. * @param {Function} [options.hooks.getMediaFile] - If provided it will be called to get a {@link MediaFile} by size of the current video element. * @param {boolean} [options.viewability] - if true it will pause the ad whenever is not visible for the viewer. * Defaults to `false` diff --git a/src/adUnit/helpers/metrics/handlers/__tests__/onClickThrough.spec.js b/src/adUnit/helpers/metrics/handlers/__tests__/onClickThrough.spec.js index b368aff6..dbb3cdaa 100644 --- a/src/adUnit/helpers/metrics/handlers/__tests__/onClickThrough.spec.js +++ b/src/adUnit/helpers/metrics/handlers/__tests__/onClickThrough.spec.js @@ -137,3 +137,21 @@ test('onClickThrough must remove the anchor on disconnect', () => { expect(element.querySelector('a.mol-vast-clickthrough')).toEqual(null); }); + +test('onClickThrough must use custom click element', () => { + const {element, videoElement} = videoAdContainer; + const customClickElement = document.createElement('span'); + + element.appendChild(customClickElement); + + const createClickControl = () => customClickElement; + + onClickThrough(videoAdContainer, callback, {createClickControl}); + + videoElement.paused = false; + customClickElement.click(); + + expect(element.querySelector('a.mol-vast-clickthrough')).toBeNull(); + expect(callback).toHaveBeenCalledTimes(1); + expect(callback).toHaveBeenCalledWith(clickThrough); +}); diff --git a/src/adUnit/helpers/metrics/handlers/onClickThrough.js b/src/adUnit/helpers/metrics/handlers/onClickThrough.js index 485a50ae..fed2f757 100644 --- a/src/adUnit/helpers/metrics/handlers/onClickThrough.js +++ b/src/adUnit/helpers/metrics/handlers/onClickThrough.js @@ -3,8 +3,7 @@ import {linearEvents} from '../../../../tracker'; const {clickThrough} = linearEvents; -const onClickThrough = ({videoElement, element}, callback, {clickThroughUrl, pauseOnAdClick = true} = {}) => { - const placeholder = element || videoElement.parentNode; +const createDefaultClickControl = () => { const anchor = document.createElement('A'); anchor.classList.add('mol-vast-clickthrough'); @@ -14,7 +13,19 @@ const onClickThrough = ({videoElement, element}, callback, {clickThroughUrl, pau anchor.style.left = 0; anchor.style.top = 0; - if (clickThroughUrl) { + return anchor; +}; + +const onClickThrough = ({videoElement, element}, callback, {clickThroughUrl, pauseOnAdClick = true, createClickControl = createDefaultClickControl} = {}) => { + const placeholder = element || videoElement.parentNode; + const anchor = createClickControl(); + const isVirtual = !document.body.contains(anchor); + + if (isVirtual) { + placeholder.appendChild(anchor); + } + + if (clickThroughUrl && anchor.tagName === 'A') { anchor.href = clickThroughUrl; anchor.target = '_blank'; } @@ -39,9 +50,11 @@ const onClickThrough = ({videoElement, element}, callback, {clickThroughUrl, pau } }; - placeholder.appendChild(anchor); - - return () => placeholder.removeChild(anchor); + return () => { + if (isVirtual) { + placeholder.removeChild(anchor); + } + }; }; export default onClickThrough; diff --git a/src/runner/run.js b/src/runner/run.js index 1da5fdbb..c1a640b7 100644 --- a/src/runner/run.js +++ b/src/runner/run.js @@ -25,6 +25,7 @@ import startVideoAd from './helpers/startVideoAd'; * @param {TrackerFn} [options.tracker] - If provided it will be used to track the VAST events instead of the default {@link pixelTracker}. * @param {Object} [options.hooks] - Optional map with hooks to configure the behaviour of the ad. * @param {Function} [options.hooks.createSkipControl] - If provided it will be called to generate the skip control. Must return a clickable [HTMLElement](https://developer.mozilla.org/es/docs/Web/API/HTMLElement) that is detached from the DOM. + * @param {Function} [options.hooks.createClickControl] - If provided it will be called to generate the click control. Must return a clickable [HTMLElement](https://developer.mozilla.org/es/docs/Web/API/HTMLElement) that is detached from the DOM. * @param {Function} [options.hooks.getMediaFile] - If provided it will be called to get a {@link MediaFile} by size of the current video element. * @returns {Promise.} - The video ad unit. */ diff --git a/src/runner/runWaterfall.js b/src/runner/runWaterfall.js index 7fed408f..4b63068a 100644 --- a/src/runner/runWaterfall.js +++ b/src/runner/runWaterfall.js @@ -156,6 +156,7 @@ const waterfall = async (fetchVastChain, placeholder, options, isCanceled) => { * Defaults to `true`. * @param {Object} [options.hooks] - Optional map with hooks to configure the behaviour of the ad. * @param {Function} [options.hooks.createSkipControl] - If provided it will be called to generate the skip control. Must return a clickable [HTMLElement](https://developer.mozilla.org/es/docs/Web/API/HTMLElement) that is detached from the DOM. + * @param {Function} [options.hooks.createClickControl] - If provided it will be called to generate the click control. Must return a clickable [HTMLElement](https://developer.mozilla.org/es/docs/Web/API/HTMLElement) that is detached from the DOM. * @param {Function} [options.hooks.getMediaFile] - If provided it will be called to get a {@link MediaFile} by size of the current video element. * @param {Function} [options.hooks.validateVastResponse] - If provided it will be called passing the current {@link VastChain} for each valid vast response. Must throw if there is a problem with the vast response. If the Error instance has an `code` number then it will be tracked using the error macros in the Vast response. It will also call {@link runWaterfall~onError} with the thrown error. * @param {Function} [options.hooks.transformVastResponse] - If provided it will be called with the current {@link VastChain} before building the adUnit allowing the modification of the vastResponse if needed.