How to preserve scroll-chain on overflow scrollable element on mobile? - javascript

I have this fixed height scrollable ul element which is for a video playlist. Scrolling works fine but when the user scrolls thru all the content they get stuck on the playlist container element. Basically, the scroll chain breaks and you can't scroll up unless you tap on one of the other elements on the page which is no easy job due to the playlist container taking all of the space on the screen and it doesn't have any padding around it.
What I need is, I need the browser to stop focusing on the ul element and shift the scroll event from element to window so they wouldn't get stuck scrolling on the ul element.
Playlist element's CSS:
.plyr-playlist-wrapper ul {
padding: 0;
margin: 0;
max-height: 28em;
min-height: 28em;
overflow-y: scroll;
-webkit-overflow-scrolling: touch; // this is the problem
}

You can solve this problem using JS (see below), but you should also consider restructuring your page so that you prevent this issue altogether. For example, you could choose not to limit the height of your ul element and still keep your video in focus when scrolling (e.g. by making it sticky).
Here is a possible solution based on JS (which needs some additional finetuning). It works by detecting when you have scrolled to the top of your ul element and bringing the video back into view.
document.getElementById("suggestions").onscroll = (e) => {
if (e.target.scrollTop == 0) {
document.getElementById("video").scrollIntoView({
behavior: "smooth"
});
}
}
#container {
margin-top: 5em;
}
#video {
height: 10em;
background: blue;
}
#suggestions {
overflow-y: scroll;
height: 28em;
margin: 0;
padding: 0;
background: red;
}
.suggestion {
height: 10em;
margin: 12px;
background: yellow;
}
<div id="container">
<div id="video">
</div>
<ul id="suggestions">
<li class="suggestion">
</li>
<li class="suggestion">
</li>
<li class="suggestion">
</li>
<li class="suggestion">
</li>
</ul>
</div>

Turns out momentum-based smooth scrolling was the problem. So I had to remove it and the problem was solved. You lose the smooth scrolling function but who cares.
// setting it to 'touch' will give you smooth scrolling but it will prob break the scroll chaining
-webkit-overflow-scrolling: auto;

Related

Highlight active menu items as page scrolls divs (Sidebar onscroll menu)

This JSFiddle by Gaurav Kalyan works well in Chrome, but in Safari and Firefox it activates the wrong menu item. Instead of highlighting the menu item clicked, it highlights the menu item before. So, for example, if you click on "Punkt 4", "Punkt 3" is highlighted instead. I haven’t been able to fix this. Can someone help? I've been trying to solve this for two weeks.
HTML
<section id="main">
<div class="target" id="1">TARGET 1</div>
<div class="target" id="2">TARGET 2</div>
<div class="target" id="3">TARGET 3</div>
<div class="target" id="4">TARGET 4</div>
</section>
<aside id="nav">
<nav>
Punkt 1
Punkt 2
Punkt 3
Punkt 4
</nav>
</aside>
CSS
* {
margin: 0;
padding: 0;
}
#main {
width: 75%;
float: right;
}
#main div.target {
background: #ccc;
height: 400px;
}
#main div.target:nth-child(even) {
background: #eee;
}
#nav {
width: 25%;
position: relative;
}
#nav nav {
position: fixed;
width: 25%;
}
#nav a {
border-bottom: 1px solid #666;
color: #333;
display: block;
padding: 10px;
text-align: center;
text-decoration: none;
}
#nav a:hover, #nav a.active {
background: #666;
color: #fff;
}
JavaScript
$('#nav nav a').on('click', function(event) {
$(this).parent().find('a').removeClass('active');
$(this).addClass('active');
});
$(window).on('scroll', function() {
$('.target').each(function() {
if($(window).scrollTop() >= $(this).offset().top) {
var id = $(this).attr('id');
$('#nav nav a').removeClass('active');
$('#nav nav a[href=#'+ id +']').addClass('active');
}
});
});
This works fine as is if the viewport height (the inner height of the browser window) is <= 400px. That is because when you click on the a link in the nav element, with an href of #4, the default browser behavior kicks in and the element with id="4" is scrolled to the top (as much as is possible).
When the viewport is the same height or smaller than the element being scrolled to, then when your scroll handler gets triggered, the if($(window).scrollTop() >= $(this).offset().top) condition evaluates as true, because the scrollTop will be exactly equal to the offset().top of the #4 div.
However, when the viewport is bigger than the content div (in your case, > 400px), when the browser tries to scroll the last div into view, it can completely do so whilst still displaying part of the bottom half of the previous div. Which means that the 3rd div will pass your scroll handler if check, not your fourth. (The offset top of the last div will not be <= the scrollTop of the window).
So what's the solution?
I would make it so that each target div is at least the same height as the viewport. You can achieve this on modern browsers using min-height: 100vh; (100% of the viewport height). That means when the last one is scrolled into view, it will completely fill the viewport, and the correct div will pass your scroll logic check correctly.
See here for a working fork.
Bonus tip
There is a number of things you can do to improve performance of this code. Cache the creation of jQuery variables, avoid the repeated work happening 4 times on every scroll event (which can happen very often), etc. It works okay for now, but it may become a bottleneck later.

Scrolling Nav Sticks to Top

My problem is along the lines of these previous issues on StackOverflow but with a slight difference.
Previous issues:
Stopping fixed position scrolling at a certain point?
Sticky subnav when scrolling past, breaks on resize
I have a sub nav that starts at a certain position in the page. When the page is scrolled the sub nav needs to stop 127px from the top. Most of the solutions I have found need you to specify the 'y' position of the sub nav first. The problem with this is that my sub nav will be starting from different positions on different pages.
This is the JS code i'm currently using. This works fine for one page but not all. Plus on mobile the values would be different again.
var num = 660; //number of pixels before modifying styles
$(window).bind('scroll', function () {
if ($(window).scrollTop() > num) {
$('.menu').addClass('fixed');
} else {
$('.menu').removeClass('fixed');
}
});
I'm looking for a solution that stops the sub nav 127px from the top no matter where on the page it started from.
You can use position: sticky and set the top of the sub-nav to 127px.
See example below:
body {
margin: 0;
}
.main-nav {
width: 100%;
height: 100px;
background-color: lime;
position: sticky;
top: 0;
}
.sub-nav {
position: sticky;
width: 100%;
height: 50px;
background-color: red;
top: 100px;
}
.contents {
width: 100%;
height: 100vh;
background-color: black;
color: white;
}
.contents p {
margin: 0;
}
<nav class="main-nav">Main-nav</nav>
<div class="contents">
<p>Contents</p>
</div>
<nav class="sub-nav">Sub-nav</nav>
<div class="contents">
<p>More contents</p>
</div>
Please see browser support for sticky here
You should change your code to the below, should work fine:
$(window).bind('scroll', function () {
if ($(window).scrollTop() > $(".menu").offset().top) {
$('.menu').addClass('fixed');
} else {
$('.menu').removeClass('fixed');
}
});
Maybe you can try this:
Find navigation div (.menu)
Find the top value of the .menu (vanilla JS would be menuVar.getBoundingClientRect().top, not sure how jQuery does this).
Get top value of browserscreen.
Calculate the difference - 127px.
When the user scrolls and reaches the top value of the menu -127px -> addClass('fixed').

In-page anchors not working properly in combination with "scroll-then-fix" JS navbar code

I use this nice little JavaScript to make my navigation bar (which is normally sitting 230px down from the top) stick to the top of the page once the page is scrolled down that 230 px. It then gives the "nav" element a "fixed" position.
$(document).ready(function() {
$(window).bind('scroll', function() {
if ($(window).scrollTop() > 230) {
$('nav').addClass('fixed');
} else {
$('nav').removeClass('fixed');
}
});
});
nav {
width: 90%;
display: flex;
justify-content: center;
max-width: 1400px;
height: 85px;
background-color: rgba(249, 241, 228, 1);
margin: auto;
border-top-left-radius: 0em;
border-top-right-radius: 0em;
border-bottom-left-radius: 2em;
border-bottom-right-radius: 2em;
}
.fixed {
position: fixed;
border-top: 0;
top: 0;
margin: auto;
left: 0;
right: 0;
z-index: 4;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<nav>
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
</ul>
</nav>
Now, the problem: i have positioned the corresponding anchor targets
within the page and have given them some "padding-top" to account for the fixed navbar (about 90px), so that they don't disappear behind the bar when the page jumps to them after clicking.
.anchor {
padding-top: 90px;
}
<a class="anchor" id="three">
This works fine AS LONG AS the navbar is already fixed to the top.
But if you click on a link while the navbar is still in its original mid-page position (e.g. the first click the user will do), it just disregards the offset i gave the anchor target and jumps to a weird position where the anchor target is hidden behind the navbar (and not even aligned with the top of the page)!
If i THEN click on the link again (now in the fixed bar on top of the page), it corrects itself and displays the page as i want to. But that first click always misses - i can't figure out why! Please help
EDIT: WORKING DEMO here: http://www.myway.de/husow/problem/problem.html
1st Add a new class name spacebody to your first div with class="space"
<nav>
...
</nav>
<div class="space spacebody">
</div>
2nd JS use the following should fix your problem:
$(document).ready(function() {
$(window).bind('scroll', function() {
if ($(window).scrollTop() > 230) {
$('nav').addClass('fixed');
$('.spacebody').css('margin-top', '85px');
} else {
$('nav').removeClass('fixed');
$('.spacebody').css('margin-top', '0px');
}
});
});
Reason Why?
because when your nav is not fixed, it has a height of 85px, when you scroll down it has no height which is 0 height. Then everything below move up by 85px causing your to go below the target of ONE or TWO etc. It is not you are missing the first click, it is when the nav are not fixed and the click you will be scroll more down by 85px. If you scroll to top and click you will miss again.
You can easily see this if you change your CSS for nav with background-color: transparent;
With the code above should fix it when you nav become fixed to add a margin-top as 85px to the div below so they keep the same height as you clicked.

SuperScrollorama: Reversing animation

This is my first post, but I'm a long-time viewer. Hope someone can help - this one's been driving me crazy and I've tried and tried to find an answer but to no avail.
Basically, I'm updating a friend's website with a few stylistic scrolling elements. I chose SuperScrollorama as it looks completely amazing, although I'm ready to accept that it's complete overkill for what I'm trying to achieve. I'm really interested in SuperScrollorama anyway, so I suppose I partly chose it just so I could try to use it!
Anyway, the idea I'm trying to implement involves:
Single page website, where section one is simply a centred, large (650px width) image and a navigation menu <ul> centred and fixed to the top of the page containing 6 <li> elements.
On scrolling down, I want the image to shrink to 250px width, and I want to make a space of 250px between the third and fourth <li> for the shrunken image to scroll into and then remain for the duration of the scroll. I'm trying to create the space by adding a margin-right to the third <li>. (I appreciate this may give me undesirable side-effects, one being that it won't be completely centred. Suggestions welcome here too! The problem that I'm about to describe, however, I also experienced when trying other methods based around floating and using two separate divs so the margin-right itself doesn't seem to be the root cause)
Now, I've managed to get the image to shrink and attach itself to the top of the page without too much trouble.
The problem that I'm having is with the margin-right. On page load, the <ul> appears centred and uniformly spread, as I want it to. The margin-right value on the third <li> is 0. However, the value jumps from 0 to 89px following the tiniest of scroll events. Continuing the scroll works as desired, but on scrolling back up, the margin returns to 89px and then stops.
Here's the code that I've got:
HTML:
<body>
<nav>
<ul>
<li>HOME</li>
<li>PROGRAMME</li>
<li id="grow_margin">TICKETS</li>
<li>MENU</li>
<li>VENUE</li>
<li>FAQ</li>
</ul>
</nav>
<header id="fix-it">
<h1><img id="scale-it" src="/images/logo.png" />TITLE</h1>
</header>
CSS:
html {
height: 100%;
width: 100%;
}
body {
height: 100%;
width: 100%;
}
nav {
position: fixed;
top: 0;
width: 100%;
height: 5%;
text-align: center;
}
nav ul {
list-style: none;
height: 100%;
}
nav ul li {
display: inline-block;
height: 100%;
}
nav ul li a {
color:#bbbbbb;
text-decoration: none;
margin: 8px;
}
header {
width: 100%;
position: fixed;
}
header h1 {
width: 100%;
text-align: center;
margin: 0;
padding: 0;
}
And the JS:
<script>
$(document).ready(function() {
var controller = $.superscrollorama({
reverse: true
});
var windowHeight=window.innerHeight;
var scrollDuration = windowHeight;
controller.addTween('#grow_margin',
TweenMax.to( $('#grow_margin'), .25, {css:{'margin-right':'250px'}, immediateRender:true}), scrollDuration);
controller.addTween('#scale-it',
TweenMax.fromTo( $('#scale-it'), .25, {css:{width:'650px'}, immediateRender:true, ease:Quad.easeInOut}, {css:{width:'250px'}, ease:Quad.easeInOut}),
scrollDuration);
controller.addTween('#fix-it',
TweenMax.fromTo( $('#fix-it'), .25, {css:{top:'40%'}, immediateRender:true, ease:Quad.easeInOut}, {css:{top:'4'}, ease:Quad.easeInOut}),
scrollDuration);
});
</script>
Any help would be so much appreciated! It's driving me absolutely crazy!
Thank you!
Rob

How to stack divs beside each other to create a carousel

I am trying to create a carousel, where clicking on any element will slide it leftwards, simultaneously sliding the right element into viewport. For that, I need to have the divs stacked side by side. I am trying it out as a float based layout (see Fiddle ).
Problem is that here clicking the red colored div slides it leftward alright, but not the green element leftwards. This is probably due to the fact that they are actually lying below another, as visible when the overflow: hidden is removed from #cont's style. How elese to stack them side by side so that sliding one leftward automatically slides the next one leftwards as well? (Creating the to-be-next element on the fly while clicking and animating it into viewport is a no-no, the element should be present in the DOM!)
I'd suggest you use a plugin, as there is more to this than you may realize. There are many plugins out there for this, here's a list to get you started: http://www.tripwiremagazine.com/2012/12/jquery-carousel.html
I modified your Javascript, HTML, and CSS to get you pointed in the right direction:
http://jsfiddle.net/nf5Dh/2/
You need a container contContent, positioned absolutely, and that container gets moved within the container div. You just float the elements in contContent to get them next to each other.
HTML:
<div id='cont'>
<div id="contContent">
<div id='i1'></div>
<div id='i2'></div>
<div id='i3'></div>
</div>
</div>
CSS:
#cont {
width: 50px;
padding-top: 10px;
background: blue;
height: 50px;
overflow: hidden;
position: relative;
}
#contContent {
height: 50px;
width: 150px;
position: absolute;
top: 0;
left: 0;
}
#contContent > div {
float: left;
display: inline-block;
height: 50px;
width: 50px;
}
#i1 { background: red; }
#i2 { background: green; }
#i3 { background: yellow; }
And the JS:
$("#contContent > div").click(function(){
$("#contContent").animate({left: "-=50px"},1000);
});
You'd probably be better off using an ul instead of all divs, this is at least more semantically correct, though not technically necessary.
<div id="carousel">
<ul id="carouselContent">
<li id="slide1"></li>
<li id="slide2"></li>
<li id="slide3"></li>
</ul>
</div>
This:
#cont {
white-space:nowrap;
overflow: hidden;
}
.pane { // or whatever the slide divs are called. get rid of the float.
float: none;
display: inline-block;
*zoom:1;
*display:inline;
}
You can use that carousel where you can generate javascript for the carousel http://caroufredsel.dev7studios.com/configuration-robot.php
I've used http://sorgalla.com/jcarousel/ for things like this in the past, that's based on postion: relative and left/right offsets. Probably easier than messing with floats.
You can try using a list item instead, and display them inline.

Categories