Angular ng-swipe with youtube iframe - javascript

I'm trying to build simply slider with images and youtube videos. I want make it works fine on touch devices, so I want to use ng-swipe-* from angular's ngTouch module. Unfortunately swipe doesn't work over youtube's iframe. I tried to set lower z-index: -10;, but then I cannot play the video.
Do you have any idea how to solve this problem?
There is a snippet:
var app = angular.module('app', ['ngTouch']);
app.controller('ctrl', function($scope) {
$scope.msg = function(msg) {
alert(msg);
}
});
.ok {
width: 300px;
height: 100px;
background: green;
}
<script src="https://code.angularjs.org/1.4.8/angular.min.js"></script>
<script src="https://code.angularjs.org/1.4.8/angular-touch.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl" ng-swipe-right="msg('right')" ng-swipe-left="msg('left')">
<div class="ok">swipe works here</div>
<div>
<iframe width="300" height="200" src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe>
</div>
</div>
</div>
(the best way to test it, is to run it in Chrome developer console and emulate on touch device)

Here's a pretty hacky workaround: using two overlay divs, set to the right and the left of the player allows the user to play and pause, and setting the height to 80% allows them to use the menu on the bottom. This is not perfect, but it kind of works!
Note 1: It's kind of buggy if you play it here, so I'm adding a codepen: http://codepen.io/anon/pen/LGjwYZ
Second version, a little bit more bloated but with more area coverage: http://codepen.io/anon/pen/rxzXxB
Note 2: I used a transparent background on the divs for demonstrational purposes.
var app = angular.module('app', ['ngTouch']);
app.controller('ctrl', function($scope) {
$scope.msg = function(msg) {
alert(msg);
}
});
.ok {
width: 300px;
height: 100px;
background: green;
}
<script src="https://code.angularjs.org/1.4.8/angular.min.js"></script>
<script src="https://code.angularjs.org/1.4.8/angular-touch.min.js"></script>
<div ng-app="app">
<div ng-controller="ctrl" ng-swipe-right="msg('right')" ng-swipe-left="msg('left')">
<div class="ok">swipe works here</div>
<div style="position:relative; height:200px; width:300px;">
<iframe style="position:absolute;width:100%;height:100%;z-index:10;" src="https://www.youtube.com/embed/dQw4w9WgXcQ"></iframe>
<div style="background:rgba(0,0,0,0.3);height:80%;width:40%;left:0;position:absolute;z-index:20;"></div>
<div style="background:rgba(0,0,0,0.3);height:80%;width:40%;right:0;position:absolute;z-index:20;"></div>
</div>
</div>
</div>

The issue is that you don't have control of events within the iframe, so can't tell when the user swipes over that area. The work around I suggest is to replace the iframe with a image placeholder while not watching. In order to do this use YouTube's Iframe API to keep track of video events. When the video goes from play to pause we will hide the video and show the image. Here is a demo.
HTML
<div ng-app="app">
<div ng-controller="ctrl" ng-swipe-right="msg($event, 'right')" ng-swipe-left="msg($event, 'left')">
<div id="player"></div>
<img id="player-cover" src="http://img.youtube.com/vi/M7lc1UVf-VE/hqdefault.jpg" />
</div>
</div>
JS
On initiation it hides the video. When ever the image is clicked it shows and plays the video. When the video goes from paused to play onPlayerStateChange handles toggling the image and video. Every time the swiping event gets called on the image it also triggers the click event handler for the image. The variable swiping keeps track of if the event was just a click or also a swipe.
var app = angular.module('app', ['ngTouch']);
app.controller('ctrl', function($scope) {
$scope.msg = function(event, msg) {
swiping = true;
alert(msg);
}
});
// keep track of the user swiping. onYouTubeIframeAPIReady needs to occur outside of Angular.
var swiping = false;
// Youtube related.
document.getElementById('player').style.display = 'none';
document.getElementById('player-cover').addEventListener("click", function() {
if (swiping) {
swiping = false;
return;
}
document.getElementById('player').style.display = 'block';
player.playVideo();
});
// This code loads the IFrame Player API code asynchronously.
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// This function creates an <iframe> (and YouTube player) after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: { 'onStateChange': onPlayerStateChange }
});
}
function onPlayerStateChange(event) {
if (event.data === YT.PlayerState.PAUSED || event.data === YT.PlayerState.ENDED) {
document.getElementById('player-cover').style.display = 'block';
document.getElementById('player').style.display = 'none';
} else {
document.getElementById('player-cover').style.display = 'none';
document.getElementById('player').display = 'block';
}
}

Related

After I played 2 videos and closed iFrame, the "Back" button on browser will play sound without video, how to fix it?

On my site [ now at : http://gatecybertech.com/test but will be moved to main site later : http://gatecybertech.com ] you can click on the top right "Video" link to get to the videos section, after I played a few clips and closed the iFrame, if I click on the "Back" button on browser, the last video will be played again, but the strange thing is : I can hear the sound, but can't see the video ? So how to solve the problem by achieving one of the following :
[1] Show the video again in iFrame and play it normally
or
[2] Don't play the sound & don't show the video
Either of the above will do. How can I achieve that ?
My html looks like this :
<div class="tools-icon">
<img src=https://i.ytimg.com/vi/IgBIaZgoAQc/hqdefault.jpg?sqp=-oaymwEXCPYBEIoBSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLDW3KcjXsTR5utmlvhFfibLe-bvRg width=170 height=110>
<p class="tools-icon__text">Keypad Pins Easily Stolen</p>
</div>
...
<!-- the modal div that will open when an anchor link is clicked to show the related video in an iframe. -->
<div id="modal" tabindex="-1">
<div class="content">
<h4 class="title"></h4>
<iframe class="yt-video" src="https://www.youtube.com/embed/A1nRiiWYgZw" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
</div>
<div class="black_overlay" onclick="closeLightBox()" style="display: block;">
<div style=" z-index: 0; left: 76%; top: 17%; width: 22px; position: absolute;">
<a class="close" onclick = "return close_iFrame();"><h2>×</h2></a>
</div>
</div>
</div>
Script looks like this :
<script>
var modal = document.getElementById('modal'),
closeBtn = modal.querySelector('close'),
ytVideo = modal.querySelector('.content .yt-video'),
title = modal.querySelector('.content .title'),
anchors = document.querySelectorAll('a[data-target="modal"]'),
l = anchors.length;
for (var i = 0; i < l; i++)
{
anchors[i].addEventListener("click", function(e)
{
e.preventDefault();
title.textContent = this.dataset.videoTitle || 'No title';
ytVideo.src = this.href;
modal.classList.toggle('is-visible');
modal.focus();
});
}
modal.addEventListener("keydown", function(e)
{
if (e.keyCode == 27)
{
title.textContent = '';
ytVideo.src = '';
this.classList.toggle('is-visible');
}
});
</script>
<script language="javascript" type="text/javascript">
function close_iFrame()
{
var modal = document.getElementById('modal'),
ytVideo = modal.querySelector('.content .yt-video');
ytVideo.src = '';
modal.classList.toggle('is-visible');
}
</script>
OK, I figured it out, just make the following change :
<script language="javascript" type="text/javascript">
function close_iFrame()
{
var modal = document.getElementById('modal'),
ytVideo = modal.querySelector('.content .yt-video');
ytVideo.src = '';
modal.classList.toggle('is-visible');
history.go(-1);
window.location.href = '#Videos';
}
</script>

Embedded Youtube videos not playing in mobile iOS Safari

Recently we've found a bug that exists only in Safari on iPhone(s) however I cannot pinpoint the source of the issue. When pressing the play button the video appears to load but then closes immediately.
All answers I've found so far aren't recent and/or don't solve the problem. Testing in BrowserStack is giving me this error: Invalid CSS property declaration at: * from the www-embed-player-sprite-mode-vfl9mHoaB.css file served from Youtube.
I'm also open to optional ways of handling embedded videos to avoid this issue.
The code:
#set($showVideo = $request.getParameter("showVideo"))
#set($howItWorksID = $placeholder.getAttributeValueGroup().getAttributeValue('product_howitworks', $sesShoppingCart.getLocale()))
#set($embeddedURL = "https://www.youtube.com/embed/" + $howItWorksID + "?rel=0")
#set($hasVideoID = false)
#if( $howItWorksID && $howItWorksID != "" )
#set( $hasVideoID = true )
#end
<div id="howItWorksModal" class="modal-howItWorks modal fade">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<button type="button" class="close js-modalClose close--howItWorks" data-dismiss="modal">
<span aria-hidden="true">×</span>
<span class="sr-only">close</span>
</button>
<div>
<div class="prose howItWorks-embedVideoWrapper">
<div class="iframe-container">
<iframe id="howItWorks-ModalVersion" class="howItWorks js-howItWorks-iframe" width="100%" src="" frameborder="0" allowfullscreen preload></iframe>
</div>
</div>
</div>
</div>
</div>
</div>
jQueryReady(function() {
var videoURL = "$embeddedURL";
// Load destination video and autoplay when modal opens
$("#howItWorksModal").on('shown.bs.modal', function(ev) {
ev.preventDefault();
$("#howItWorks-ModalVersion").attr("src", videoURL + "&autoplay=1");
console.log('clicked on');
});
// Kill video when modal is closed
$('#howItWorksModal').on('hidden.bs.modal', function (e) {
$('.js-howItWorks-iframe').each( function(){
$(this).attr('src', '');
});
});
// Kill mobile video if playing while window is resized
function mobileVideoSource(){
var mobileBlock = $('#BuyNow-mobileBlock'),
howToVid_mobile = $('#howItWorks-MobileVersion');
if (mobileBlock.is(":hidden")) {
// if mobile block is hidden remove it's source
howToVid_mobile.attr('src', '');
} else {
// if mobile block is displayed add a source
howToVid_mobile.attr('src', videoURL);
}
}
sdi.window.on('resize', mobileVideoSource);
})
mobileVideoSource() was being called and removing the src when the video was opening. I did not investigate into why this was happening, however I restructured the script to completely remove the iframe instead of the src and inject a new instance if (for whatever rare edge use case) the screen is resized back down to mobile and the user wants to watch the video again:
function mobileVideoSource() {
var mobileBlock = $('#BuyNow-mobileBlock'),
currentVid = $('#howItWorks-MobileVersion'),
videoContainer = $('.iframe-container')
new_iframe = $('<iframe id="howItWorks-MobileVersion" class="howItWorks" width="560" height="315" src="$embeddedURL" frameborder="0" allowfullscreen></iframe>');
if (mobileBlock.is(":hidden")) {
currentVid.remove();
} else {
videoContainer.append(new_iframe);
}
}

YouTube as popup play on desktop version but not on mobile devices

I am using below script to play YouTube video as a bootstrap Modal, It play video on desktop version but same fails to play on mobile devices such as iphone and iPad.
YouTubeLoader.js
// Load & insert into DOM Youtube iframe_api
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
/* Create Player */
var player;
function onYouTubeIframeAPIReady() {
console.log('Api Loaded');
}
$(".youtubeVideoLoader").each(function() {
// Set the BG image from the youtube ID
$(this).css('background-image', 'url(//i.ytimg.com/vi/' + this.id + '/hqdefault.jpg)');
// Click the video div
$(document).delegate('#' + this.id, 'click', function() {
// Vemos le id del video
console.log(this.id);
// Create new instance of player
player = new YT.Player('videoModalContainer', {
videoId: this.id,
events: {
'onReady': OpenModal,
'onStateChange': console.log('Changed')
}
});
// Show Modal
function OpenModal() {
$("#youtubeModal").modal({backdrop: "static"});
// Set Highres
player.setPlaybackQuality('highres');
// Play Video
player.playVideo();
};
});// /click
}); // /each video
// Add a Lisener to Modal CLose Button
$('#CloseModalButton').click(function(){
console.log('Stop Preset');
player.destroy();
});
HTML
<div id="3WCMCeS7Na4" class="youtubeVideoLoader">
</div>
<!-- Modal Template -->
<div class="modal fade" id="youtubeModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-footer">
<button id="CloseModalButton" type="button" class="btn btn-default" data-dismiss="modal">X</button>
</div>
<div class="modal-content">
<div class="modal-body">
<div id="videoModalContainer">
</div>
</div>
I have tried it with other video and i face the same issue as i initially thought it might be one video which is not playing due to privileges
Issue was resolved removing following two lines of code.
//player.setPlaybackQuality('highres');
//player.playVideo();
It was not playing on mobile devices as Youtube has stopped the AutoPlay functionality of video. Not sure why it worked on desktop version...

Playing a video in slider and then swapping out for an image at end

So I currently have a video playing inside a HeroCarousel slider. There is an image before the slider and another image after the slider.
My code:
<div data-time="8000" data-prev-src="/media/splash/slider-left.png" data-next-src="/media/splash/slider-right.png">
<a href="/island-careers/retail.html">
<img src="/media/island/splash/now-hiring-splash.jpg" />
</a>
</div>
<div data-time="9000" data-video="true" data-prev-src="/media/splash/slider-left-black.png" data-next-src="/media/splash/slider-right-black.png" id="video">
<a href="/catalog/island-collections/packing-list/">
<video width="1275" height="557" preload="auto" id="myVideo">
<source src="/media/island/splash/video-2.mp4" type="video/mp4">
</video>
<img src="/media/island/splash/escape-splash.jpg" />
</a>
</div>
<div data-time="8000" data-prev-src="/media/splash/slider-left.png" data-next-src="/media/splash/slider-right.png">
<a href="/catalog/mens-resort-wear/classic-shirts/breaker-striped-red-linen-shirt.html">
<img src="/media/island/splash/breakers-shirt-splash.jpg" />
</a>
</div>
So my issue is that the video is playing when the page loads. When the slide enters into view, the video is already at its completed frame. I want the video to play when the slide enters into view and not when the page loads. The slider adds the class current to the current slide so I started writing the following script in order to get it to play:
//playing video
jQuery(document).ready(function() {
$play = 0;
if($play > 0) {
if(jQuery("article#video").hasClass("current")) {
jQuery("#myVideo").get(0).play();
$play++;
}
}
});
This script is not working completely.
I would also like to swap out the video for the image found underneath it once the video ends (the video should only be played once)
One way to achieve this would be using flags and listening to DOMSubtreeModified and ended event.
for starting the video only when the slide is visible, you can do
var canPlay = false, played = false;
$('.hero-carousel article').bind('DOMSubtreeModified', function(e) {
if(played){ // flag to make sure it is played only once.
$('.hero-carousel article').unbind('DOMSubtreeModified');
}else if($(e.target).hasClass('current')){
canPlay = true; // flag to make sure, playing starts only when slide is current.
var video = $(e.target).find('video')[0];
if(video){
video.play();
}
}
});
$('.hero-carousel article video').bind('play', function(){
if(!canPlay){
this.pause();
this.currentTime = 0;
}else{
played = true;
}
});
and for hiding the video once it is finished, you can do:
$('.hero-carousel article video').bind('ended', function(){
this.style.display = 'none';
});
fiddle demo

How do I control the playback of multiple Youtube Videos with the Youtube JS api?

Here's the problem:
I have multiple youtube videos in a slider, and I want the videos to stop playing when a link that controls the slider is clicked. It's really annoying to have the videos just keep playing when you can't see it.
I used this code:
<div id="ytapiplayer2">
You need Flash player 8+ and JavaScript enabled to view this video.
</div>
<script type="text/javascript">
function onYouTubePlayerReady(playerId) {ytplayer2 = document.getElementById("myvideo");}
var params = { allowScriptAccess: "always" };var atts = { id: "myvideo" };
swfobject.embedSWF("http://www.youtube.com/v/hCarSDT7cSQ?enablejsapi=1&version=3&playerapiid=myvideo","ytapiplayer2", "425", "270", "8", null, null, params, atts);
function play() {if (ytplayer2) {ytplayer2.playVideo();}}
function pause() {if (ytplayer2) {ytplayer2.pauseVideo();}}
</script>
Which is basically directly from Google's Youtube js api stuff.1 It works GREAT for just ONE video, per page.
My problem is I have some pages where I want to have multiple videos in the same slider. Videos are contained within divs, and called exactly as shown above in each respective div.
BUT -- The videos don't stop playing back now when navigating links for the slider are clicked. :(
I have tried to change the variable and id names to be unique each time the script is called. I have even changed the function name from function onYouTubePlayerReady to onYouTubePlayerReady2 ... but nothing works.
I'm stumped. Halp?
Edit to show more detailed code set up:
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js" type="text/javascript"></script><!--Uses Google API library-->
<script src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js" type="text/javascript"></script><!--Uses Google API library-->
</head>
<body>
<div id="bodyContent">
<div id="leftCol">
Content
</div><!-- #leftCol -->
<div id="middleCol">
<ul id="subNav">
<li class="category">Mini Header</li>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
</ul><!--subNav-->
</div><!-- #middleCol -->
<div id="rightCol" class="slider">
<div class="scroll">
<div class="scrollContainer">
<div id="link1" class="panel">
<div id="ytapiplayer1">
You need Flash player 8+ and JavaScript enabled to view this video.
</div>
<script type="text/javascript">
function onYouTubePlayerReady(playerId) {ytplayer1 = document.getElementById("myVideo1");}
var params = { allowScriptAccess: "always" };var atts = { id: "myVideo1" };
swfobject.embedSWF("http://www.youtube.com/v/67aIIRv-dYU?enablejsapi=1&version=3&playerapiid=ytplayer1","ytapiplayer1", "425", "347", "8", null, null, params, atts);
function play() {if (ytplayer1) {ytplayer1.playVideo();}}
function pause() {if (ytplayer1) {ytplayer1.pauseVideo();}}
</script>
</div><!-- #link1 -->
<div id="link2" class="panel">
<div id="ytapiplayer2">
You need Flash player 8+ and JavaScript enabled to view this video.
</div>
<script type="text/javascript">
function onYouTubePlayerReady(playerId) {ytplayer2 = document.getElementById("myVideo2");}
var params = { allowScriptAccess: "always" };var atts = { id: "myVideo2" };
swfobject.embedSWF("http://www.youtube.com/v/67aIIRv-dYU?enablejsapi=1&version=3&playerapiid=ytplayer1","ytapiplayer2", "425", "347", "8", null, null, params, atts);
function play() {if (ytplayer2) {ytplayer2.playVideo();}}
function pause() {if (ytplayer2) {ytplayer2.pauseVideo();}}
</script>
</div><!-- #link2 -->
<div>Video 3 set up is same as above...</div>
</div>
</div><!-- .scroll -->
</div><!-- #rightCol -->
</div><!-- #bodyContent -->
</body>
Wrong approach. Why use two or more YouTube players when you can accomplish the task with one player, specially when you're using YouTube JS API.
Relevant code:
<div id="ytapiplayer">
You need Flash player 8+ and JavaScript enabled to view this video.
</div>
Video 1
Video 2
<script type="text/javascript">
function load(id) {
if (ytplayer) {
ytplayer.loadVideoById(id);
}
}
</script>
Demo for solution 1
Alternately you can create multiple players and figure out some way of storing how many players you have on the page in a variable. I think it is sufficient to store the id of the player. Do not confuse the id with (i) the id you assign to the div (ii) the id you pass as playerapiid.
swfobject.embedSWF() will replace the div with either an object or embed tag, that tag is assigned an id that you pass to this method in the last parameter. This is the id that you can later use to reference the player(s).
Demo for solution 2
<script type="text/javascript" src="http://www.youtube.com/player_api"></script>
<script>
var player1st;
var player2nd;
function onYouTubePlayerAPIReady() {
player1st = new YT.Player('frame1st', {events: {'onStateChange': onPlayerStateChange}});
player2nd = new YT.Player('frame2nd', {events: {'onStateChange': onPlayerStateChange}});
}
//function onYouTubePlayerAPIReady() { player2nd = new YT.Player('frame2nd', {events: {'onStateChange': onPlayerStateChange}}); }
function onPlayerStateChange(event) {
alert(event.target.getPlayerState());
}
</script>
<iframe id="frame1st" style="position: relative; height: 220px; width: 400px" src="http://www.youtube.com/embed/1g30Xyh6Wqc?rel=0&enablejsapi=1"></iframe><br>
play1st<br>
pause1st<br>
stop1st
<iframe id="frame2nd" style="position: relative; height: 220px; width: 400px" src="http://www.youtube.com/embed/T39hYJAwR40?rel=0&enablejsapi=1"></iframe><br>
play2nd<br>
pause2nd<br>
stop2nd
Store the references to each player in an array, then when you want to stop them all loop through the array. If you could post more of your HTML/JavaScript for the multiple video setup I might have a more specific suggestion, otherwise fill in the blanks yourself:
// add a global array of video refs
var myVideos = [];
// some code to create the first video, similar to OP code
// ...
myVideos.push(ytplayer2);
// some code to create second video
// ...
myVideos.push(anotherVideoRef);
// some code to create third video
// ...
myVideos.push(thirdVideoRef);
// etc.
function pauseAllVideos(){
for (var i = 0; i < myVideos.length; i++) {
myVideos[i].pauseVideo();
}
}
I would prefer to setup the videos in a loop, rather than one at a time like I did above, but I don't know how the rest of your code is structured, so...

Categories

Resources