Skip to content

Commit 0f17397

Browse files
committed
Display Click to Load placeholders on demand
Historically, the Click to Load placeholders were only drawn once at the point page load completed. That had two problems: 1. Sometimes placeholders would take too long to display, if the page took a long time to load fully. 2. Sometimes placeholders would not be displayed at all, if the blocked content was only created after page load had finished. To fix that, let's display the placeholders when instructed by the platform using a "displayClickToLoadPlaceholders" update message.
1 parent 0b9715e commit 0f17397

File tree

1 file changed

+32
-32
lines changed

1 file changed

+32
-32
lines changed

src/features/click-to-play.js

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ let sharedStrings = null
2020
const entities = []
2121
const entityData = {}
2222

23+
let readyResolver
24+
const ready = new Promise(resolve => { readyResolver = resolve })
25+
2326
/*********************************************************
2427
* Widget Replacement logic
2528
*********************************************************/
@@ -364,22 +367,6 @@ class DuckWidget {
364367
}
365368
}
366369

367-
/**
368-
* Initialise the Click to Load feature, once the necessary details have been
369-
* returned by the platform.
370-
* @returns {Promise}
371-
*/
372-
async function initCTL () {
373-
await replaceClickToLoadElements()
374-
375-
window.addEventListener('ddg-ctp-replace-element', ({ target }) => {
376-
replaceClickToLoadElements(target)
377-
}, { capture: true })
378-
379-
// Inform surrogate scripts that CTP is ready
380-
originalWindowDispatchEvent(createCustomEvent('ddg-ctp-ready'))
381-
}
382-
383370
function replaceTrackingElement (widget, trackingElement, placeholderElement, hideTrackingElement = false, currentPlaceholder = null) {
384371
widget.dispatchEvent(trackingElement, 'ddg-ctp-tracking-element')
385372

@@ -537,6 +524,8 @@ function showExtraUnblockIfShortPlaceholder (shadowRoot, placeholder) {
537524
* in the document will be replaced instead.
538525
*/
539526
async function replaceClickToLoadElements (targetElement) {
527+
await ready
528+
540529
for (const entity of Object.keys(config)) {
541530
for (const widgetData of Object.values(config[entity].elementData)) {
542531
const selector = widgetData.selectors.join()
@@ -1312,7 +1301,7 @@ async function createYouTubePreview (originalElement, widget) {
13121301
// Convention is that each function should be named the same as the sendMessage
13131302
// method we are calling into eg. calling `sendMessage('getClickToLoadState')`
13141303
// will result in a response routed to `updateHandlers.getClickToLoadState()`.
1315-
const updateHandlers = {
1304+
const messageResponseHandlers = {
13161305
getClickToLoadState (response) {
13171306
devMode = response.devMode
13181307
isYoutubePreviewsEnabled = response.youtubePreviewsEnabled
@@ -1322,14 +1311,15 @@ const updateHandlers = {
13221311
// first.
13231312

13241313
// Start Click to Load
1325-
if (document.readyState === 'complete') {
1326-
initCTL()
1327-
} else {
1328-
// Content script loaded before page content, so wait for load.
1329-
window.addEventListener('load', (event) => {
1330-
initCTL()
1331-
})
1332-
}
1314+
window.addEventListener('ddg-ctp-replace-element', ({ target }) => {
1315+
replaceClickToLoadElements(target)
1316+
}, { capture: true })
1317+
1318+
// Inform surrogate scripts that CTP is ready
1319+
originalWindowDispatchEvent(createCustomEvent('ddg-ctp-ready'))
1320+
1321+
// Mark the feature as ready, to allow placeholder replacements.
1322+
readyResolver()
13331323
},
13341324
setYoutubePreviewsEnabled: function (resp) {
13351325
if (resp?.messageType && typeof resp?.value === 'boolean') {
@@ -1346,6 +1336,8 @@ const updateHandlers = {
13461336
}
13471337
}
13481338

1339+
const knownMessage = Object.prototype.hasOwnProperty.bind(messageResponseHandlers)
1340+
13491341
export function init (args) {
13501342
const websiteOwner = args?.site?.parentEntity
13511343
const settings = args?.featureSettings?.clickToPlay || {}
@@ -1411,17 +1403,25 @@ export function init (args) {
14111403
})
14121404

14131405
// Request the current state of Click to Load from the platform.
1414-
// Note: When the response is received, initCTL() is then called by the
1415-
// response handler to finish starting up the feature.
1406+
// Note: When the response is received, the response handler finishes
1407+
// starting up the feature.
14161408
sendMessage('getClickToLoadState')
14171409
}
14181410

14191411
export function update (args) {
1420-
const detail = args && args.detail
1421-
if (!(detail && detail.func)) { return }
1412+
// Unsupported update message.
1413+
if (!args?.detail) return
14221414

1423-
const fn = updateHandlers[detail.func]
1424-
if (typeof fn !== 'function') { return }
1415+
// Message response.
1416+
if (typeof args?.detail?.func === 'string' && knownMessage(args.detail.func)) {
1417+
return messageResponseHandlers[args.detail.func](args.detail.response)
1418+
}
14251419

1426-
fn(detail.response)
1420+
// Other known update messages.
1421+
if (args?.detail?.type === 'displayClickToLoadPlaceholders') {
1422+
// TODO: Pass `args.detail.ruleAction` through, that way only content
1423+
// corresponding to the entity for that ruleAction need to be
1424+
// replaced with a placeholder.
1425+
return replaceClickToLoadElements()
1426+
}
14271427
}

0 commit comments

Comments
 (0)