I'm following this overflow discussion ( Play multiple Plyr Vimeo embeds on hover ) this solution perfectly working for embedded videos (YouTube, Vimeo etc) unfortunately not working for HTML5 videos.
In an Ajax query, I'm fetching videos ( video player could be HTML, YouTube, Vimeo or others ) In this query embedded video (YouTube and Vimeo) working on hover but HTML is not.
Here is the section screenshot you might check https://prnt.sc/lYc5q8kIDvb8 first 3 HTML video players ( not working on mouse hover ) and the last 3 YouTube and Vimeo players ( working fine on mouse hover ).
Here is my JS code below -
<?php $attr['section_id'] = 1; ?>
<div class="video-load-more-content-<?php echo esc_attr( $attr['section_id'] ); ?> row"></div>
<div class="row">
<div class="col-12 pagination text-center">
<a href="javascript:void(0)" class="vidiow-primary" id="load-more-<?php echo esc_attr( $attr['section_id'] ); ?>" data-action="vidiow_load_more<?php echo esc_attr( $attr['section_id'] ); ?>">Load more</a>
</div>
</div>
<script type="text/javascript">
;(function ($) {
'use strict';
$(document).ready(function () {
let paged = 0;
let $buttonSelector = $('#load-more-<?php echo esc_attr( $attr['section_id'] ); ?>');
let $content = $('.video-load-more-content-<?php echo esc_attr( $attr['section_id'] ); ?>');
$buttonSelector.on('click', function(e) {
if (e.preventDefault) {
e.preventDefault();
}
paged++; // Do currentPage + 1, because we want to load the next page
$buttonSelector.text('Loading ...');
$.ajax({
type: 'POST',
url: '<?php echo esc_url( admin_url( 'admin-ajax.php' )); ?>',
dataType: 'json', // <-- Change dataType from 'html' to 'json'
data: {
action: $buttonSelector.data('action'),
nonce: '<?php echo wp_create_nonce( 'vidiow_ajax_load_more' ); ?>',
paged,
action_name: $buttonSelector.data('action')
},
success: function (response) {
if(paged >= response.max) {
$buttonSelector.hide();
}
$content.append(response.content);
$buttonSelector.text('Load More');
/**
* Video player initialization
*/
const playerNodes = $('.vidiow_player');
const vidiow_player = playerNodes.map(
(currentValue, index, arr) => new Plyr( index, {
debug: true,
ratio: '16:9',
volume: 0,
controls: false,
muted: true,
fullscreen: { enabled: false },
loop: { active: true }
})
).get();
if( vidiow_player ){
vidiow_player.forEach(( player, index ) =>
player.on("play", () =>
vidiow_player
.filter((p, i) => i !== index)
.forEach((p) => p.pause())
)
);
}
playerNodes.hover(playVideo, pauseVideo);
function playVideo(e){
vidiow_player[playerNodes.index(this)].play();
}
function pauseVideo(e){
vidiow_player[playerNodes.index(this)].pause();
}
// Initialize bootstrap tooltip
$('[data-bs-toggle="tooltip"]').tooltip();
}
});
});
$buttonSelector.trigger('click');
});
})(jQuery);
</script>
HTML code -
<!-- Youtube -->
<div class="plyr__video-embed vidiow_player">
<iframe
src="https://www.youtube.com/embed/<?php echo esc_attr( $custom_video_url ); ?>?origin=https://plyr.io&iv_load_policy=3&modestbranding=1&playsinline=1&showinfo=0&rel=0&enablejsapi=1"
allowfullscreen
allowtransparency
allow="autoplay">
</iframe>
</div>
<!-- HTML 5-->
<video class="vidiow_player" playsinline controls data-poster="img.jpg">
<source src="video.mp4" type="video/mp4" />
<source src="video.mp4" type="video/webm" />
</video>
Another crucial issue I'm facing in the Ajax process is Plyr, masonry or others need reinitialization inside jQuery "success: function (response)" which is already initialized in the main JS code. Only the first load main js code works but doesn't work when clicking on Ajax action.
Note: If you need any more information to make smooth the answering process, please inform me.
Update #2
Since the plyr plugin alters the DOM, it is not possible to dynamically extract the player instances from there, for embedded videos. So a different approach is to have a global Map
to store t
he players linked to their original element. Then you can search in there for all your actions.
;
(function($) {
'use strict';
$(document).ready(function() {
const AllVideos = new Map();
let paged = 0;
let $buttonSelector = $('#load-more-<?php echo esc_attr( $attr['section_id'] ); ?>');
let $content = $('.video-load-more-content-<?php echo esc_attr( $attr['section_id'] ); ?>');
$buttonSelector.on('click', function(e) {
if (e.preventDefault) {
e.preventDefault();
}
paged++; // Do currentPage + 1, because we want to load the next page
$buttonSelector.text('Loading ...');
$.ajax({
type: 'POST',
url: '<?php echo esc_url( admin_url( 'admin-ajax.php' )); ?>',
dataType: 'json', // <-- Change dataType from 'html' to 'json'
data: {
action: $buttonSelector.data('action'),
nonce: '<?php echo wp_create_nonce( 'vidiow_ajax_load_more' ); ?>',
paged,
action_name: $buttonSelector.data('action')
},
success: function(response) {
if (paged >= response.max) {
$buttonSelector.hide();
}
$content.append(response.content);
$buttonSelector.text('Load More');
/**
* Video player initialization
*/
const newPlayerNodes = $('.vidiow_player')
.filter((index, element) => !element.plyr);
const new_vidiow_players = newPlayerNodes.map(
(index, element, arr) => {
const player = new Plyr(element, {
debug: true,
ratio: '16:9',
volume: 0,
controls: false,
muted: true,
fullscreen: {
enabled: false
},
loop: {
active: true
}
});
AllVideos.set(element, player);
return player;
}
).get();
if (new_vidiow_players.length) {
new_vidiow_players.forEach((player) =>
player.on("play", () => {
[...AllVideos.values()]
.filter((p) => p !== player)
.forEach((p) => p.pause())
})
);
}
newPlayerNodes.hover(playVideo, pauseVideo);
function playVideo(e) {
AllVideos.get(this).play();
}
function pauseVideo(e) {
AllVideos.get(this).pause();
}
// Initialize bootstrap tooltip
$('[data-bs-toggle="tooltip"]').tooltip();
}
});
});
$buttonSelector.trigger('click');
});
})(jQuery);
Update #1
after the comment about the css, the issue is that the hover
is applied on the actual video element but the poster is positioned on top of it, so the video does not receive the hover
event and the code never runs.
If you add a css rule .plyr__poster{ pointer-events: none; }
it should fix it.
The original answer might still help avoid some issues in the future, by just initializing the new elements.