Skip to content

Commit a292f6d

Browse files
committed
Update to support play on hover from Nikola changes
1 parent 65ba06b commit a292f6d

File tree

2 files changed

+99
-26
lines changed

2 files changed

+99
-26
lines changed

global-brand-code/custom-components/accordion.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
//---- Accordions ----
21
(function () {
32
"use strict";
43

global-brand-code/custom-components/inline-video.js

Lines changed: 99 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
1-
/**
2-
* Video Library - Custom video functionality for lazy loading, play/pause controls, and accessibility
3-
* Features: lazy loading, play/pause buttons, scroll triggers, reduced motion support
4-
*
5-
* Performance Optimizations for CDN Usage:
6-
* - Early exit if no videos present (no DOM manipulation or event listeners)
7-
* - Minimal object creation on pages without videos
8-
* - Conditional resize listener only for desktop-only videos
9-
* - Zero CSS class dependencies - uses only data attributes
10-
*
11-
* Safe for loading on every page via CDN - will not impact performance on pages without videos.
12-
*/
13-
141
class VideoLibrary {
152
constructor(options = {}) {
163
this.options = {
@@ -52,7 +39,7 @@ class VideoLibrary {
5239
// Initialize video functionality
5340
this.setupLazyLoading();
5441
this.setupVideoControls();
55-
42+
this.setupHoverPlay();
5643
// Only add resize listener if desktop-only videos are present
5744
const desktopOnlyVideos = document.querySelectorAll(
5845
'video[data-video-desktop-only="true"]'
@@ -186,6 +173,12 @@ class VideoLibrary {
186173
videos.forEach((video) => {
187174
const scrollInPlay =
188175
video.getAttribute("data-video-scroll-in-play") === "true";
176+
const hoverPlay = video.getAttribute("data-video-hover") === "true";
177+
178+
// If it's a hover-play video, do nothing here. Its logic is handled in setupHoverPlay.
179+
if (hoverPlay) {
180+
return;
181+
}
189182

190183
if (this.prefersReducedMotion) {
191184
video.pause();
@@ -213,9 +206,6 @@ class VideoLibrary {
213206
source.src = source.getAttribute("data-src");
214207
video.load();
215208

216-
// Hide the corresponding picture element when video starts loading
217-
this.hidePictureElement(video);
218-
219209
video.addEventListener("canplaythrough", function onCanPlayThrough() {
220210
video.removeEventListener("canplaythrough", onCanPlayThrough);
221211
resolve();
@@ -232,21 +222,37 @@ class VideoLibrary {
232222
}
233223

234224
/**
235-
* Hide the corresponding picture element for a video
225+
* Show the corresponding picture element for a video (with opacity transition)
226+
* @param {HTMLVideoElement} video - The video element
227+
*/
228+
showPictureElement(video) {
229+
const videoId = video.getAttribute("data-video");
230+
if (!videoId) return;
231+
232+
const pictureElement = this.findPictureElement(video, videoId);
233+
234+
if (pictureElement) {
235+
pictureElement.style.opacity = "1";
236+
if (this.options.debug) {
237+
console.log(`Faded in picture element for video: ${videoId}`);
238+
}
239+
}
240+
}
241+
242+
/**
243+
* Hide the corresponding picture element for a video (with opacity transition)
236244
* @param {HTMLVideoElement} video - The video element
237245
*/
238246
hidePictureElement(video) {
239247
const videoId = video.getAttribute("data-video");
240248
if (!videoId) return;
241249

242-
// Find the picture element with matching data-video-picture attribute
243-
// Use the most performant approach: check previous siblings first, then fallback to document query
244250
const pictureElement = this.findPictureElement(video, videoId);
245251

246252
if (pictureElement) {
247-
pictureElement.style.display = "none";
253+
pictureElement.style.opacity = "0";
248254
if (this.options.debug) {
249-
console.log(`Hidden picture element for video: ${videoId}`);
255+
console.log(`Faded out picture element for video: ${videoId}`);
250256
}
251257
}
252258
}
@@ -296,13 +302,73 @@ class VideoLibrary {
296302
});
297303
}
298304

305+
/**
306+
* Setup hover-to-play functionality for videos with data-video-hover="true"
307+
*/
308+
setupHoverPlay() {
309+
const hoverVideos = document.querySelectorAll(
310+
'video[data-video-hover="true"]'
311+
);
312+
313+
hoverVideos.forEach((video) => {
314+
const videoId = video.getAttribute("data-video");
315+
if (!videoId) return;
316+
317+
const trigger = video.closest(`[data-video-trigger="${videoId}"]`);
318+
319+
if (trigger) {
320+
// ✅ ADJUSTED: Target the individual play and pause buttons directly
321+
// This leaves the wrapper element untouched.
322+
const videoContainer = video.parentElement;
323+
if (videoContainer) {
324+
const playButton = videoContainer.querySelector(
325+
`[data-video-playback="play"][data-video="${videoId}"]`
326+
);
327+
const pauseButton = videoContainer.querySelector(
328+
`[data-video-playback="pause"][data-video="${videoId}"]`
329+
);
330+
331+
// Make the buttons inaccessible since hover is the primary interaction.
332+
if (playButton) {
333+
playButton.setAttribute("aria-hidden", "true");
334+
playButton.setAttribute("tabindex", "-1");
335+
}
336+
if (pauseButton) {
337+
pauseButton.setAttribute("aria-hidden", "true");
338+
pauseButton.setAttribute("tabindex", "-1");
339+
}
340+
}
341+
342+
// On mouse enter, explicitly hide poster, then lazy load and play
343+
trigger.addEventListener("mouseenter", async () => {
344+
if (this.prefersReducedMotion) return;
345+
try {
346+
this.hidePictureElement(video); // This ensures the poster is hidden on EVERY hover
347+
await this.lazyLoadVideo(video);
348+
video.currentTime = 0;
349+
video.play();
350+
} catch (error) {
351+
console.error(`Error playing hover video ${videoId}:`, error);
352+
}
353+
});
354+
355+
// On mouse leave, pause the video AND show the poster
356+
trigger.addEventListener("mouseleave", () => {
357+
video.pause();
358+
this.showPictureElement(video);
359+
});
360+
}
361+
});
362+
}
299363
/**
300364
* Setup autoplay for videos that should play immediately when loaded
301365
* @param {HTMLVideoElement} video - The video element
302366
*/
303367
setupAutoplay(video) {
304368
video.addEventListener("canplaythrough", () => {
305369
if (!this.prefersReducedMotion) {
370+
// ✅ FIX: Hide the poster before playing for standard autoplay videos
371+
this.hidePictureElement(video);
306372
video.play().catch(console.error);
307373
}
308374
});
@@ -323,19 +389,25 @@ class VideoLibrary {
323389

324390
// If reduced motion is preferred, don't play the video
325391
if (!this.prefersReducedMotion) {
392+
// ✅ FIX: Hide the poster before playing for scroll-in-play videos
393+
this.hidePictureElement(video);
326394
video.currentTime = 0;
327395
video.play();
328396
}
329397
} catch (error) {
330398
console.error(error);
331399
}
332-
} else {
333-
// Pause on scroll out for scroll-in-play videos
400+
}
401+
// On scroll out, pause the video and show the poster
402+
else {
334403
video.pause();
404+
this.showPictureElement(video);
335405
}
336406
});
337407
},
338-
{ threshold: this.options.scrollTriggerThreshold }
408+
{
409+
threshold: this.options.scrollTriggerThreshold,
410+
}
339411
);
340412

341413
observer.observe(video);
@@ -411,6 +483,7 @@ class VideoLibrary {
411483
event.stopPropagation();
412484
try {
413485
await this.lazyLoadVideo(video);
486+
this.hidePictureElement(video); // Hide poster on manual play
414487
video.play();
415488
toggleButtonVisibility(true);
416489
} catch (error) {
@@ -444,6 +517,7 @@ class VideoLibrary {
444517
try {
445518
await this.lazyLoadVideo(video);
446519
if (!this.prefersReducedMotion) {
520+
this.hidePictureElement(video);
447521
video.currentTime = 0;
448522
video.play();
449523
}

0 commit comments

Comments
 (0)