I'm not sure if this has been answered before but is there a way to bind the scroll event to the html5 video currentTime element?
Something like:
$(window).bind("scroll", vidProgress)
.load(vidProgress);
function vidProgress() {
$('video').currentTime = **viewport scroll progress**;
};
It depends on how you define "viewport scroll progress". If you mean scroll progress as in the proportion of scrollable height that has been scrolled past, you can simply divide the scrollTop of the viewport by the scrollable height. Scrollable height, in turn, is simply the difference between the document height and the viewport height.
With this ratio in mind, you can simply multiply it by the duration of the video, accessed using .duration property, in order to navigate to the correct timestamp that is to the ratio of the scrollable height of the page.
In my fiddle example, I have set the document height to 500% that of the viewport, and positioned the video fixed relative to the viewport, in order to demonstrate how this works (otherwise the video will scroll out of view).
$(function() {
$(window)
.on('scroll', vidProgress)
.load(vidProgress);
function vidProgress() {
// Get video properties
var $v = $('video'),
duration = $v[0].duration;
// Get window properties
var $w = $(window),
scrollable = $(document).height() - $w.height();
// Do seeking
var scrollRatio = $(document).scrollTop()/scrollable;
if(isNaN(scrollRatio)) scrollRatio = 0;
$v[0].currentTime = scrollRatio*duration;
};
});
View working demo here: http://jsfiddle.net/teddyrised/kr9jmudu/
Note: If the video is extremely huge, it doesn't make sense to listen to the window's load event. Instead, listen to the loadedmetadata event of the video, so you can already start calculating the ratio.
Related
I'm working on a web project that has animations and page changes on the scroll ( specifically, scroll direction ) and I've been looking for multiple possible good and reliable solutions.
I've been detecting the scroll direction by detected the window's scrollY with the user's previously saved scrollY that I have saved in a variable. The only problem is that the scroll event doesn't fire when at the top or the bottom of page, even though the content is all absolute/fixed positioned.
I want to turn to the wheel event because of its deltaY values from the event, and it still fires when at the top of bottom of the page so I can remove the scrollbar and keep the body of the page 100vh.
The Mozilla dev docs say:
Don't confuse the wheel event with the scroll event. The default
action of a wheel event is implementation-specific, and doesn't
necessarily dispatch a scroll event. Even when it does, the delta*
values in the wheel event don't necessarily reflect the content's
scrolling direction. Therefore, do not rely on the wheel event's
delta* properties to get the scrolling direction. Instead, detect
value changes of scrollLeft and scrollTop of the target in the scroll
event.
(https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event)
And I'm also curious if the wheel event will work correctly on mobile with touch?
Here's a good example of what I'm trying to replicate: https://reed.be
There is no scrollbar, yet things still happen based on your scrolling.
CanIuse shows full compatibility of the wheel event with modern browsers, and some older versions.
see here -> https://caniuse.com/#feat=mdn-api_wheelevent
I've found a solution that references the wheel event (How to determine scroll direction without actually scrolling), though my question still applies -
How reliable is the wheel event across devices and browsers, including mobile?
I am limited to my own current version browsers and android devices for testing.
You can fool the browser by setting the additional height on the body to match the content width and setting the overflow to scroll. Then use some basic script, to set the scrollLeft property of your container to equal the window scrollY.
You will need to set the height of the body equal to the total width of the panels.
body {
height: 400vh; // 4 panels of 100vw each
...
}
.panel {
width: 100vw;
...
}
JS
const viewPort = document.querySelector('#viewport');
let lastScroll = 0;
window.addEventListener('scroll', (e) => {
let scrollY = window.scrollY;
// scroll the container by and equal amount of your window scroll
viewPort.scrollLeft = scrollY;
lastScroll = scrollY;
});
Rough JSFiddle Demo
I have a list of images on a page. As I scroll through the page I would like to show some options in a naviationbar of the image currently in the viewport. Therefore I need to get the image element currently in the viewport, is this possible ?
Jakob
Who says there's just one image in viewport? What would you like to do when there are many?
But otherwise you can always get the scroll position of your container with images as well as your images' top offset to see which one is currently in-view.
So these values will get you to your result
container scroll position
container visible client height
images' top offset
Using these values will make it possible to locate all images in the view regardless whether they're fully or partially displayed (at the top or bottom).
This is a simplified JSFiddle that gives red border around the first fully-in-the-view image. The code does this:
// get top positions and references to all images
var pos = $("img").map(function(){
var $this = $(this);
return {
el: $this,
top: $this.offset().top
};
}).get();
// provide document scrolling
$(document).on("scroll", function() {
$("img").removeClass("first-in-view");
var scroll = $(this).scrollTop();
var i = 0;
while(pos[i].top < scroll) i++;
pos[i].el.addClass("first-in-view");
}).scroll();
This should be optimised to only toggle class when it needs to. Otherwise we have flickering in every scroll. But it demonstrates how this can be done and you can get going from here.
IMPORTANT
It is utterly important that you attach your image position determining process on document load event and not the usually use DOM ready, because you have to wait for the document to load in order for your images to have final positions.
I'm working on a mobile web app, and in my page I have a div element with its width set to 100%.
I need to set the height of this div so that the height is correct for a set aspect ratio. So for example, if the screen was sized to 300 pixels wide and the ratio was 3:2, my script should grab the width of the div (which at this point should be 300px) and set the height to 200px.
On first load, this works perfectly. However, if I rotate the screen of my phone to landscape, the width of the div obviously changes, so I need to reset its height in order to keep the correct ratio.
My problem is that I can't find an event which fires after the elements are resized. There is an orientationchange event built into jQuery Mobile, which helpfully fires when the screen is rotated from portrait to landscape and vice-versa:
$(window).bind('orientationchange', function (e) {
// Correctly alerts 'landscape' or 'portrait' when orientation is changed
alert(e.orientation);
// Set height of div
var div = $('#div');
var width = div.width();
// Shows the *old* width, i.e the div's width before the rotation
alert(width);
// Set the height of the div (wrongly, because width is incorrect at this stage)
div.css({ height: Math.ceil(width / ratio) });
});
But this event seems to fire before any of the elements in the page have resized to fit the new layout, which means (as mentioned in the comments) I can only get the pre-rotation width of the div, which is not what I need.
Does anyone know how I can get the div's new width, after things have resized themselves?
A few methods for you to try:
(1) Set a timeout inside your orientationchange event handler so the DOM can update itself and the browser can draw all the changes before you poll for the new dimension:
$(window).bind('orientationchange', function (e) {
setTimeout(function () {
// Get height of div
var div = $('#div'),
width = div.width();
// Set the height of the div
div.css({ height: Math.ceil(width / ratio) });
}, 500);
});
It won't make too big of a difference but note that Math.ceil takes a lot longer to complete (relatively) than Math.floor since the latter only has to drop everything after the decimal point. I generally just pass the browser the un-touched float number and let it round where it wants to.
(2) Use the window.resize event instead to see if that updated fast enough for you:
$(window).bind('resize', function (e) {
// Get height of div
var div = $('#div'),
width = div.width();
// Set the height of the div
div.css({ height: Math.ceil(width / ratio) });
});
On a mobile device this will fire when the orientation changes since the size of the browser view-port will also change.
(3) If you are updating the size of this <div> element because it holds an image, just apply some CSS to the image to make it always be full-width and the correct aspect ratio:
.my-image-class {
width : 100%;
height : auto;
}
I was wondering how sites like Facebook, with their timeline feature, float a certain element (usually a menu bar, or sometimes a social plugin, etc) when the user has scrolled past a point such that the top of the element is off the screen, etc.
This could be seen as a more general JavaScript (jQuery?) event firing when the user has scrolled to a certain element, or scrolled down a certain number of pixels.
Obviously it would require toggling the CSS property from:
#foo { position: relative; }
to
#foo { position: fixed; }
Or with jQuery, something like:
$('#foo').css('position', 'fixed');
Another way I have seen this implemented is with blogs, where a popup will be called when you reach the bottom, or near the bottom of a page. My question is, what is firing that code, and could you link or provide some syntax/ semantics/ examples?
Edit: I'm seeing some great JS variants coming up, but as I am using jQuery, I think the plugin mentioned will do just nicely.
Take a look at this jsfiddle: http://jsfiddle.net/remibreton/RWJhM/2/
In this example, I'm using document.onscroll = function(){ //Scroll event } to detect a scroll event on the document.
I'm then calculating the percentage of the page scrolled based on it's height. (document.body.scrollTop * 100 / (document.body.clientHeight - document.documentElement.clientHeight)).
document.body.scrollTop being the number of pixels scrolled from the top, document.body.clientHeight being the height of the entire document and document.documentElement.clientHeight being the visible portion of the document, a.k.a. the viewport.
Then you can compare this value to a target percentage, an execute JavaScript. if(currentPercentage > targetPercentage)...
Here's the whole thing:
document.onscroll = function(){
var targetPercentage = 80;
var currentPercentage = (document.body.scrollTop * 100 / (document.body.clientHeight - document.documentElement.clientHeight));
console.log(currentPercentage);
if(currentPercentage > targetPercentage){
document.getElementById('pop').style.display = 'block';
// Scrolled more than 80%
} else {
document.getElementById('pop').style.display = 'none';
// Scrolled less than 80%
}
}
If you prefer jQuery, here is the same example translated into everybody's favorite library: http://jsfiddle.net/remibreton/8NVS6/1/
$(document).on('scroll', function(){
var targetPercentage = 80;
var currentPercentage = $(document).scrollTop() * 100 / ($(document).height() - $(window).height());
if(currentPercentage > targetPercentage){
$('#pop').css({display:'block'});
//Scrolled more than 80%
} else {
$('#pop').css({display:'none'});
//Scrolled less than 80%
}
});
An idea would be to handle the window.scroll event and determine if the user has scrolled to the bottom of the page. Here is an example:
http://chrissilich.com/blog/load-more-content-as-the-user-reaches-the-bottom-of-your-page-with-jquery/
Hope it helps!
There is a jquery plugin that might help you in the right direction.
http://imakewebthings.com/jquery-waypoints/
I just answered basically the same question here. In that case it was a table and its header, and the basic idea is like this:
function placeHeader(){
var $table = $('#table');
var $header = $('#header');
if ($table.offset().top <= $(window).scrollTop()) {
$header.offset({top: $(window).scrollTop()});
} else {
$header.offset({top: $table.offset().top});
}
}
$(window).scroll(placeHeader);
Here's a demo.
Quoting myself:
In other words, if the top of the table is above the scrollTop, then
position the header at scrollTop, otherwise put it back at the top of
the table. Depending on the contents of the rest of the site, you
might also need to check if you have scrolled all the way past the
table, since then you don't want the header to stay visible.
To answer your question directly, it is triggered by checking the scrollTop against either the position of an element, or the height of the document minus the height of the viewport (for the scrolled to bottom use case). This check is done every time the scroll event is fired (bound using $(window).scroll(...)).
The Background:
I tried to solve the StackOverflow question yet another HTML/CSS layout challenge - full height sidebar with sticky footer on my own using jQuery. Because the sidebar in my case may be longer than the main content it matches the case of comment 8128008. That makes it impossible to have a sidebar longer than the main content and having a sticky footer without getting problems when shrinking the browser window.
The status quo:
I have a html page with a div, which is automatically stretched to fill the screen. So if there is empty space below the element, I stretch it downwards:
But if the browser viewport is smaller than the div itself, no stretching is done but the scrollbar shows up:
I've attached jQuery to the window's resize event to resize the div, if the browser window is not to small and remove any resizing in the other case. This is done by checking if the viewport is higher or smaller than the document. If the viewport is smaller than the document, it seems like the content is larger than the browser window, why no resizing is done; in the other case we resize the div to fill the page.
if ($(document).height() > $(window).height()) {
// Scrolling needed, page content extends browser window
// --> No need to resize the div
// --> Custom height is removed
// [...]
} else {
// Window is larger than the page content
// --> Div is resized using jQuery:
$('#div').height($(window).height());
}
The Problem:
Up to now, everything runs well. But if I shrink the browser window, there are cases, where the div should be resized but the document is larger than the window's height, why my script assumes, that no resizing is needed and the div's resizing is removed.
The point is actually, that if I check the document's height using Firebug after the bug appeared, the height has just the value is was meant to have. So I thought, the document's height is set with a little delay. I tried to run the resize code delayed a bit but it did not help.
I have set up a demonstration on jsFiddle. Just shrink the browser window slowly and you'll see the div "flickering". Also you can watch the console.log() output and you will notice, that in the case of "flickering" the document's height and the window's height are different instead of being equal.
I've noticed this behavior in Firefox 7, IE 9, Chrome 10 and Safari 5.1. Can you confirm it?
Do you know if there is a fix? Or is the approach totally wrong? Please help me.
Ok -- wiping my old answer and replacing...
Here's your problem:
You are taking and comparing window and document height, without first taking into consideration the order of events here..
Window loads
Div grows to window height
Window shrinks
Document height remains at div height
Window height is less than div height
At this point, the previously set height of the div is keeping document height greater than the window height, and this logic is misinterpreted:
"Scrolling needed, no need to extend the sidebar" fires, erroneously
Hence the twitch.
To prevent it, just resize your div along with the window before making the comparison:
(function () {
var resizeContentWrapper = function () {
console.group('resizing');
var target = {
content: $('#resizeme')
};
//resize target content to window size, assuming that last time around it was set to document height, and might be pushing document height beyond window after resize
//TODO: for performance, insert flags to only do this if the window is shrinking, and the div has already been resized
target.content.css('height', $(window).height());
var height = {
document: $(document).height(),
window: $(window).height()
};
console.log('height: ', height);
if (height.document > height.window) {
// Scrolling needed, no need to externd the sidebar
target.content.css('height', '');
console.info('custom height removed');
} else {
// Set the new content height
height['content'] = height.window;
target.content.css('height', height['content']);
console.log('new height: ', height);
}
console.groupEnd();
}
resizeContentWrapper();
$(window).bind('resize orientationchange', resizeContentWrapper);
})(jQuery);
Per pmvdb's comment, i renamed your $$ to "target"
$(window).bind('resize',function(){
$("#resizeme").css("height","");
if($("#resizeme").outerHeight() < $(window).height()){
$("#resizeme").height($(window).height());
$("body").css("overflow-y","hidden");
}else{
$("body").css("overflow-y","scroll");
}
});
Maybe I am misunderstanding the problem, but why are you using Javascript? This seems like a layout (CSS) issue. My solution without JS: http://jsfiddle.net/2yKgQ/27/