Native Fullscreen JavaScript API toggle button - javascript

I can't figure out how to modify the below code to include a toggle button. When in 'normal' mode the button would make the element go fullscreen and then change its function to go back to 'normal' state.
I've modified the code from John Dyer's Native Fullscreen JavaScript API example:
var fsButton = document.getElementById('fsbutton'),
fsElement = document.getElementById('specialstuff'),
fsStatus = document.getElementById('fsstatus');
if (window.fullScreenApi.supportsFullScreen) {
fsStatus.innerHTML = 'YES: Your browser supports FullScreen';
fsStatus.className = 'fullScreenSupported';
// handle button click
fsButton.addEventListener('click', function() {
window.fullScreenApi.requestFullScreen(fsElement);
}, true);
fsElement.addEventListener(fullScreenApi.fullScreenEventName, function() {
if (fullScreenApi.isFullScreen()) {
fsStatus.innerHTML = 'Whoa, you went fullscreen';
} else {
fsStatus.innerHTML = 'Back to normal';
}
}, true);
} else {
fsStatus.innerHTML = 'SORRY: Your browser does not support FullScreen';
}
to this:
var container = document.getElementById('canvas'),
fsButton = document.getElementById('fsbutton');
if (window.fullScreenApi.supportsFullScreen) { // fullscreen supported
fsButton.style.display = 'block';
container.addEventListener(fullScreenApi.fullScreenEventName, function() {
fsButton.addEventListener('click', function() {
if (fullScreenApi.isFullScreen()) { // fullscreen is on
window.fullScreenApi.CancelFullScreen( container );
fsButton.className = 'fs-off';
} else { // fullscreen is off
window.fullScreenApi.requestFullScreen( container );
container.style.width = "100%";
container.style.height = "100%";
fsButton.className = 'fs-on';
}
}, true)
}, true);
} else {
// no fullscreen support - do nothing
}
But it doesn't work. Any suggestions much appreciated!

The other problem you'll have is that Mozilla wants you to listen to the fullscreenchange event on the document element, not the element that is going fullscreen.
// which object can handle a fullscreen event
var fullscreenObj = (fullScreenApi.fullScreenEventName.indexOf('moz') > -1 : document : container;
fullscreenObj.addEventListener(fullScreenApi.fullScreenEventName, function() {
if (fullScreenApi.isFullScreen()) {
container.style.width = container.style.height = '100%';
fsButton.className = 'fs-on';
} else {
fsButton.className = 'fs-off';
}
}, true);

First of all, you shouldn't nest fsButton click event listener into fullScreenApi's event listener because it won't work until container goes fullscreen. fsButton's click is responsible for going fullscreen, event listener is being attached after going fullscreen so the action will never happen.
Here's a modified version of the code which should suit your needs:
var fsButton = document.getElementById('fsbutton'),
container = document.getElementById('canvas');
if (window.fullScreenApi.supportsFullScreen) {
fsButton.style.display = 'block';
fsButton.addEventListener('click', function() {
if (fsButton.className == 'fs-off') {
window.fullScreenApi.requestFullScreen(container);
} else {
window.fullScreenApi.cancelFullScreen(container);
}
}, true);
container.addEventListener(fullScreenApi.fullScreenEventName, function() {
if (fullScreenApi.isFullScreen()) {
container.style.width = container.style.height = '100%';
fsButton.className = 'fs-on';
} else {
fsButton.className = 'fs-off';
}
}, true);
} else {
// no fullscreen support - do nothing
}

I'd advise using something like https://github.com/sindresorhus/screenfull.js/
This wraps most of the browser quirks in a cleaner interface.

Related

How to trigger a JavaScript function only when the user scrolls the page

I need to add positon: fixed to .box only if the user scrolls the page.
The important point here is that the element with the class .box get generated only after the user scrolls the page.
This is what I came up with:
window.addEventListener('scroll', () => {
const myDiv = document.querySelector('.box')
if (myDiv) {
if (myDiv.style.position !== 'fixed') {
myDiv.style.position = 'fixed'
}
}
})
The problem with my code is that the scroll event is now going to fire a million events all the time and kill performance.
What would be the right way to achieve the above without firing scroll event again and again.
After the box has been inserted into the DOM, you can query the box and add the fixed position style to it.
If you're doing this often then here is a more general function that will (given a selector, e.g. .box) wait for an element to be inserted into the DOM:
function waitForElement(selector) {
let element = null;
let handled = false;
let nextFrame;
return (callback) => {
function tick() {
element = document.querySelector(selector);
if (element === null) {
nextFrame = requestAnimationFrame(tick);
}
if (element !== null && !handled) {
handled = true;
callback(element);
}
}
cancelAnimationFrame(nextFrame);
requestAnimationFrame(tick);
};
}
Usage:
const waitForBox = waitForElement(".box");
waitForBox((box) => {
box.style.position = "fixed";
});
Demo:
// Demo section, ignore this
const plane = document.querySelector(".plane");
const box = document.createElement("div");
box.className = "box";
box.style.width = "100px";
box.style.height = "100px";
box.style.background = "crimson";
function waitForElement(selector) {
let element = null;
let handled = false;
let nextFrame;
return (callback) => {
function tick() {
element = document.querySelector(selector);
if (element === null) {
nextFrame = requestAnimationFrame(tick);
}
if (element !== null && !handled) {
handled = true;
callback(element);
}
}
cancelAnimationFrame(nextFrame);
requestAnimationFrame(tick);
};
}
// A function to call inside scroll callback that will
// wait for the .box element to be found in the DOM
const waitForBox = waitForElement(".box");
let boxAppended = false;
document.addEventListener("scroll", () => {
// Appending the box to the DOM, ignore this
if (!boxAppended) {
boxAppended = true;
plane.appendChild(box);
}
// Callback will be fired once when the .box
// element is found in the DOM
waitForBox((box) => {
box.style.position = "fixed";
});
});
body {
padding: 0;
height: 100vh;
overflow: auto;
}
.plane {
height: 200vh;
}
<div class="plane"></div>
You can for example use requestAnimationFrame() or setTimeout() to prevent the scroll event from firing too many times.
Here is an example of how to do this with requestAnimationFrame() from the Mozilla scroll event documentation:
let lastKnownScrollPosition = 0;
let ticking = false;
function doSomething(scrollPos) {
// Do something with the scroll position
}
document.addEventListener('scroll', function(e) {
lastKnownScrollPosition = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(function() {
doSomething(lastKnownScrollPosition);
ticking = false;
});
ticking = true;
}
});

Vanilla JS Swipe Function Now Overrides Links/Expected Behaviour

I have written a detect swipe left or right script; it has one unintended consequence. Links that reside within the swipeable container are no longer clickable.
I was wondering if anyone had any solutions they could point me to or any thoughts to experiment with my code.
I have tried:
Adding/toggling with event.preventDefault() within the Pointer Events
Looking at other get gesture type scripts, they include a distance threshold. If it is does not meet the threshold, I remove all event listeners. Adding this creates an unexpected result of working, only if you click the button twice.
Find the clickable elements within the container such as a span a , add an incremental click event listener then change the window location ref. Kinda works, but when you swipe near the span; the selection takes over and swipe actions stop. Experimenting with css touch-action: none or user-select:none have not improved the experience. It feels like I am breaking the default behaviour and reinventing the wheel.
link to a demo here on JS Bin
JS code here
window.addEventListener('load', () => {
let test = new getSwipeX({
elementId: 'container'
});
// previous attempt - adding a click on top of the browser default
let grabBtn = document.getElementsByClassName('button')[0];
grabBtn.addEventListener('click', (e) => {
window.location.href = 'www.google.co.uk'
});
})
function getSwipeX({elementId}) {
this.e = document.getElementsByClassName(elementId)[0];
this.initialPosition = 0;
this.lastPosition = 0;
this.threshold = 200;
this.diffInPosition = null;
this.diffVsThreshold = null;
this.gestureState = 0;
this.getTouchStart = (event) => {
event.stopPropagation();
if (window.PointerEvent) {
this.e.setPointerCapture(event.pointerId);
}
return this.initalTouchPos = this.getGesturePoint(event);
}
this.getTouchMove = (event) => {
event.stopPropagation();
return this.lastPosition = this.getGesturePoint(event);
}
this.getTouchEnd = (event) => {
event.stopPropagation();
if (window.PointerEvent) {
this.e.releasePointerCapture(event.pointerId);
}
this.doSomething();
this.initialPosition = 0;
}
this.getGesturePoint = (event) => {
this.point = event.pageX
return this.point;
}
this.whatGestureDirection = (event) => {
this.diffInPosition = this.initalTouchPos - this.lastPosition;
this.diffVsThreshold = Math.abs(this.diffInPosition) > this.threshold;
(Math.sign(this.diffInPosition) > 0) ? this.gestureState = 'L' : (Math.sign(this.diffInPosition) < 0) ? this.gestureState = 'R' : this.gestureState = 'N';
return [this.diffInPosition, this.diffVsThreshold, this.gestureState];
}
this.doSomething = (event) => {
let [gestureDelta,gestureThreshold,gestureDirection] = this.whatGestureDirection();
// USE THIS TO DEBUG
console.log(gestureDelta,gestureThreshold,gestureDirection);
if (gestureThreshold) {
(gestureDirection == 'L') ? console.log('Left') : console.log('Right');
} else {
this.e.removeEventListener('pointerdown', this.getTouchStart, true);
this.e.removeEventListener('pointermove', this.getTouchMove, true);
this.e.removeEventListener('pointerup', this.getTouchEnd, true);
this.e.removeEventListener('pointercancel', this.getTouchEnd, true);
}
}
if (window.PointerEvent) {
this.e.addEventListener('pointerdown', this.getTouchStart, true);
this.e.addEventListener('pointermove', this.getTouchMove, true);
this.e.addEventListener('pointerup', this.getTouchEnd, true);
this.e.addEventListener('pointercancel', this.getTouchEnd, true);
}
}

How to drag an Openseadragon canvas with mouse middle wheel button

I am using Openseadragon library with fabricjs overlay. I have a case where I want to drag the canvas but instead of mouse primary button, I want to drag it with middle mouse button press. Could anyone please help me get the desired behavior?
OpenSeadragon doesn't have a flag for that, but you can easily build it using the MouseTracker. Here's an example (coded from memory and not tested, but it should give you the idea).
var drag;
var mouseTracker = new OpenSeadragon.MouseTracker({
element: viewer.container,
nonPrimaryPressHandler: function(event) {
if (event.button === 1) { // Middle
drag = {
lastPos: event.position.clone()
};
}
},
moveHandler: function(event) {
if (drag) {
var deltaPixels = drag.lastPos.minus(event.position);
var deltaPoints = viewer.viewport.deltaPointsFromPixels(deltaPixels);
viewer.viewport.panBy(deltaPoints);
drag.lastPos = event.position.clone();
}
},
nonPrimaryReleaseHandler: function(event) {
if (event.button === 1) {
drag = null;
}
}
});
EDIT: I had a bug in the example code above; fixed.
Expanding on #iangilman 's answer...
To improve the user experience when the middle button is released outside the MouseTracker's element while dragging, causing the nonPrimaryReleaseHandler to never get called, the pointer can be captured...something like this:
var trackerElement = viewer.container;
var drag;
function capturePointer(event) {
if (OpenSeadragon.MouseTracker.havePointerCapture) {
if (OpenSeadragon.MouseTracker.havePointerEvents) {
// Can throw InvalidPointerId
try {
trackerElement.setPointerCapture(event.originalEvent.pointerId);
} catch () {
//
}
} else {
trackerElement.setCapture(true);
}
}
}
function releasePointer(event) {
if (OpenSeadragon.MouseTracker.havePointerCapture) {
if (OpenSeadragon.MouseTracker.havePointerEvents) {
// Can throw InvalidPointerId
try {
trackerElement.releasePointerCapture(event.originalEvent.pointerId);
} catch () {
//
}
} else {
trackerElement.releaseCapture();
}
}
}
var mouseTracker = new OpenSeadragon.MouseTracker({
element: trackerElement,
nonPrimaryPressHandler: function(event) {
if (event.button === 1) { // Middle
capturePointer(event);
drag = {
lastPos: event.position.clone()
};
}
},
moveHandler: function(event) {
if (drag) {
var deltaPixels = drag.lastPos.minus(event.position);
var deltaPoints = viewer.viewport.deltaPointsFromPixels(deltaPixels);
viewer.viewport.panBy(deltaPoints);
drag.lastPos = event.position.clone();
}
},
nonPrimaryReleaseHandler: function(event) {
if (event.button === 1) {
releasePointer(event);
drag = null;
}
}
});

javascript code written in separate to the html markup

what's wrong with this code, i have included the file but when i run it doesn't give the result i intended(customized design of video player),any one who is aware about javascript can help.......
function dofirst() {
//get values of buttons
var movie = document.getElementById("movie");
var playOrpause = document.getElementById("playOrpause");
var mute = document.getElementById("mute");
var fullscreen = document.getElementById("fullscreen");
//get values of sliders
var seekbar = document.getElementById("seekbar");
var myvolume = document.getElementById("volume");
//add the event listeners to buttons
playOrpause.addEventListener('click', playme, false);
mute.addEventListener('click', mute_me, false);
fullscreen.addEventListener('click', scree_full, false);
//add the event listener to sliders
seekbar.addEventListener('change', change_me, false);
seekbar.addEventListener('timeupdate', update_me, false);
seekbar.addEventListener('mousedown', mous_down, false);
seekbar.addEventListener('mouseup', mous_up, false);
myvolume.addEventListener('change', volume_up, false);
}
//the functions of play button
function playme() {
if (movie.paused == true) {
movie.play();
//update button status
playOrpause.innerHTML = 'pause';
else {
movie.pause();
//update button status
playOrpause.innerHTML = 'play';
}
}
}
//the functions of mute button
function mute_me() {
if (movie.muted == false) {
movie.muted = true;
//update button status
mute.innerHTML = 'unmute';
} else {
movie.muted = false;
//update button status
mute.innerHTML = 'mute';
}
}
//the functions of fullscreen button
function scree_full() {
if (movie.requestFullscreen) {
movie.requestFullscreen();
}
//for mozilla firefox browser
else if (movie.mozRequestFullscreen) {
movie.mozRequestFullscreen();
}
//for google chrome browsers
else if (movie.webkitRequestFullscreen) {
movie.webkitRequestFullscreen();
}
}
//the functions for seekbar
function change_me() {
//calculate current time of the video
var time = movie.duration * (seekbar.value / 100);
//update the current time of the video
movie.currentTime = time;
}
//update the seekbar when the video plays
function update_me() {
var size = movie.currentTime * (100 / movie.duration);
//update the size of the seekbar
seekbar.value = size;
}
//pause the video when the seekbar is dragged
function mous_down() {
movie.pause();
}
//play the video when the seekbar is dropped
function mous_up() {
movie.play();
}
//the function for the volume bar
function volume_up() {
movie.volume = myvolume.value;
}
window.addEventListener("load", dofirst, false);
Make sure that there are no syntax errors in your code....For instance, you seem to be using 'mous_up()' instead of 'mouse_up()'. From my own experience, javascript code doesn't run when there are any syntax errors.

Problems filling a container with video via Jquery

I am able to create a full background video using jquery on a new page, but I am having issues filling an existing container on a page with the video. I am looking to be able to use this code to add the video to a couple of different containers on a single page. Such as a section and the hero unit. Although for now I am just trying to figure out how to add it to the hero unit and I can go from there. I tried replacing all videobg class and videobg_wrapper classes with the hero class, although it still isn't working.
Can anyone assist me? Any help would be appreciated. Here is a jfiddle with the jquery and css class.
Jfiddle with code: http://jsfiddle.net/NN276/12/
Fullscreen Jfiddle: http://jsfiddle.net/NN276/12/embedded/result/
Here is an attempt to change videobg to the hero class:
(function( $ ){
$.fn.videoBG = function( selector, options ) {
var options = {};
if (typeof selector == "object") {
options = $.extend({}, $.fn.videoBG.defaults, selector);
}
else if (!selector) {
options = $.fn.videoBG.defaults;
}
else {
return $(selector).videoBG(options);
}
var container = $(this);
// check if elements available otherwise it will cause issues
if (!container.length)
return;
// container to be at least relative
if (container.css('position') == 'static' || !container.css('position'))
container.css('position','relative');
// we need a width
if (options.width == 0)
options.width = container.width();
// we need a height
if (options.height == 0)
options.height = container.height();
// get the wrapper
var wrap = $.fn.videoBG.wrapper();
wrap.height(options.height)
.width(options.width);
// if is a text replacement
if (options.textReplacement) {
// force sizes
options.scale = true;
// set sizes and forcing text out
container.width(options.width)
.height(options.height)
.css('text-indent','-9999px');
}
else {
// set the wrapper above the video
wrap.css('z-index',options.zIndex+1);
}
// move the contents into the wrapper
wrap.html(container.clone(true));
// get the video
var video = $.fn.videoBG.video(options);
// if we are forcing width / height
if (options.scale) {
// overlay wrapper
wrap.height(options.height)
.width(options.width);
// video
video.height(options.height)
.width(options.width);
}
// add it all to the container
container.html(wrap);
container.append(video);
return video.find("video")[0];
}
// set to fullscreen
$.fn.videoBG.setFullscreen = function($el) {
var windowWidth = $(window).width(),
windowHeight = $(window).height();
$el.css('min-height',0).css('min-width',0);
$el.parent().width(windowWidth).height(windowHeight);
// if by width
if (windowWidth / windowHeight > $el.aspectRatio) {
$el.width(windowWidth).height('auto');
// shift the element up
var height = $el.height();
var shift = (height - windowHeight) / 2;
if (shift < 0) shift = 0;
$el.css("top",-shift);
} else {
$el.width('auto').height(windowHeight);
// shift the element left
var width = $el.width();
var shift = (width - windowWidth) / 2;
if (shift < 0) shift = 0;
$el.css("left",-shift);
// this is a hack mainly due to the iphone
if (shift === 0) {
var t = setTimeout(function() {
$.fn.videoBG.setFullscreen($el);
},500);
}
}
$('body > .hero').width(windowWidth).height(windowHeight);
}
// get the formatted video element
$.fn.videoBG.video = function(options) {
$('html, body').scrollTop(-1);
// video container
var $div = $('<div/>');
$div.addClass(hero')
.css('position',options.position)
.css('z-index',options.zIndex)
.css('top',0)
.css('left',0)
.css('height',options.height)
.css('width',options.width)
.css('opacity',options.opacity)
.css('overflow','hidden');
// video element
var $video = $('<video/>');
$video.css('position','absolute')
.css('z-index',options.zIndex)
.attr('poster',options.poster)
.css('top',0)
.css('left',0)
.css('min-width','100%')
.css('min-height','100%');
if (options.autoplay) {
$video.attr('autoplay',options.autoplay);
}
// if fullscreen
if (options.fullscreen) {
$video.bind('canplay',function() {
// set the aspect ratio
$video.aspectRatio = $video.width() / $video.height();
$.fn.videoBG.setFullscreen($video);
})
// listen out for screenresize
var resizeTimeout;
$(window).resize(function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
$.fn.videoBG.setFullscreen($video);
},100);
});
$.fn.videoBG.setFullscreen($video);
}
// video standard element
var v = $video[0];
// if meant to loop
if (options.loop) {
loops_left = options.loop;
// cant use the loop attribute as firefox doesnt support it
$video.bind('ended', function(){
// if we have some loops to throw
if (loops_left)
// replay that bad boy
v.play();
// if not forever
if (loops_left !== true)
// one less loop
loops_left--;
});
}
// when can play, play
$video.bind('canplay', function(){
if (options.autoplay)
// replay that bad boy
v.play();
});
// if supports video
if ($.fn.videoBG.supportsVideo()) {
// supports webm
if ($.fn.videoBG.supportType('webm')){
// play webm
$video.attr('src',options.webm);
}
// supports mp4
else if ($.fn.videoBG.supportType('mp4')) {
// play mp4
$video.attr('src',options.mp4);
// $video.html('<source src="'.options.mp4.'" />');
}
// throw ogv at it then
else {
// play ogv
$video.attr('src',options.ogv);
}
}
// image for those that dont support the video
var $img = $('<img/>');
$img.attr('src',options.poster)
.css('position','absolute')
.css('z-index',options.zIndex)
.css('top',0)
.css('left',0)
.css('min-width','100%')
.css('min-height','100%');
// add the image to the video
// if suuports video
if ($.fn.videoBG.supportsVideo()) {
// add the video to the wrapper
$div.html($video);
}
// nope - whoa old skool
else {
// add the image instead
$div.html($img);
}
// if text replacement
if (options.textReplacement) {
// force the heights and widths
$div.css('min-height',1).css('min-width',1);
$video.css('min-height',1).css('min-width',1);
$img.css('min-height',1).css('min-width',1);
$div.height(options.height).width(options.width);
$video.height(options.height).width(options.width);
$img.height(options.height).width(options.width);
}
if ($.fn.videoBG.supportsVideo()) {
v.play();
}
return $div;
}
// check if suuports video
$.fn.videoBG.supportsVideo = function() {
return (document.createElement('video').canPlayType);
}
// check which type is supported
$.fn.videoBG.supportType = function(str) {
// if not at all supported
if (!$.fn.videoBG.supportsVideo())
return false;
// create video
var v = document.createElement('video');
// check which?
switch (str) {
case 'webm' :
return (v.canPlayType('video/webm; codecs="vp8, vorbis"'));
break;
case 'mp4' :
return (v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'));
break;
case 'ogv' :
return (v.canPlayType('video/ogg; codecs="theora, vorbis"'));
break;
}
// nope
return false;
}
// get the overlay wrapper
$.fn.videoBG.wrapper = function() {
var $wrap = $('<div/>');
$wrap.addClass('videoBG_wrapper')
.css('position','absolute')
.css('top',0)
.css('left',0);
return $wrap;
}
// these are the defaults
$.fn.videoBG.defaults = {
mp4:'',
ogv:'',
webm:'',
poster:'',
autoplay:true,
loop:true,
scale:false,
position:"absolute",
opacity:1,
textReplacement:false,
zIndex:0,
width:0,
height:0,
fullscreen:false,
imgFallback:true
}
})( jQuery );
$(document).ready(function() {
$('body').videoBG({
position:"fixed",
zIndex:0,
mp4:'http://www.pete.dj/video/video.mp4',
ogv:'http://www.pete.dj/video/video.ogv',
webm:'http://www.pete.dj/video/video.webm',
opacity:1,
fullscreen:true,
});
})
Not the complete solution as they all don't play immediately, but somewhere to start.
$('.hero').each(function() {
$(this).videoBG({
position:"relative",
zIndex:0,
mp4:'http://www.pete.dj/video/video.mp4',
ogv:'http://www.pete.dj/video/video.ogv',
webm:'http://www.pete.dj/video/video.webm',
opacity:1,
fullscreen:false,
});
});
Working JSFiddle: http://jsfiddle.net/NN276/15/
Also, the creator provides the following warning on his plugin site.
Don't abuse this code... Don't use it too often, too many video
instances will slow down the browser.
Please bear in mind bandwidth usage for both you, and your visitors

Categories