Javascript - detecting scroll attempt when window.pageYOffset is already 0 - javascript

I have a div that is on the page load pushed down out of the view and then exposed later and pushed up to the top-bar and takes over the rest of the screen on click function by changing the top style in my function like this:
$('#drawer').animate({
top: 92
}, 500);
I would like to when that div is visible, to detect if the user is scrolling up and when it attempts to scroll up that the div slides down by the amount of pixels the user is scrolling up. I am not sure how to exactly do that.
In my script scroll-event.js I have tried to setup a function where I would on document ready first declare global variable drawerIsUp = false;, check if it is false or true and then execute the scrolling part. But, since when the drawer is up the window.pageYOffset is 0, how can I keep adding pixels to the div top style if the user tries to scroll but the window.pageYOffset is 0 already?
$(document).ready(function (){
drawerDiv = this.getElementById("drawer");
topBar = this.getElementById("top-bar");
drawerIsUp = false;
scrollDrawer = function () {
if (drawerIsUp) {
console.log('entered');
function winScroll() {
drawerDiv.style.top = window.pageYOffset + "px";
}
winScroll();
$(window).bind({scroll: winScroll});
}
}
});
And this is the html:
<div id="app">
<div id="bg">
</div>
#section('topBar')
#include('customer.layouts.partials.top-bar')
#show
<div id="main-section">
#section('header')
#include('customer.layouts.partials.header')
#show
#section('carousel')
#include('customer.layouts.partials.carousel', ['function' => 'drawer', 'carouselPadding' => ''])
#show
</div>
<div id="drawer">
<div id="magazine-detail">
</div>
<div id="magazine-detail-carousel">
#section('magazine-detail-carousel')
#include('customer.layouts.partials.carousel', ['function' => 'magazineDetail', 'carouselPadding' => 'carouselPadding'])
#show
</div>
</div>
</div>
So, the drawer gets over the main-section on click, and then when it is over I should move it down if the user scrolls up from it.
The css for the drawer and top bar:
#main-section {
height: calc(100vh - 92px);
overflow-y: scroll;
width: 100%;
position: fixed;
overflow-y: scroll;
top:77px;
}
#drawer {
z-index: 5;
position: fixed;
top: 100vh;
height: calc(100vh - 92px);
max-height: 100%;
overflow-y: scroll;
width: 100%;
background-color: $white;
padding-left: 15px;
padding-right: 15px;
}
.top-bar {
position: fixed;
top: 0;
}

You used drawer inside winResize function. But drawer was declared inside $(document).ready so winResize could not recognize what you meant by drawer.
$(document).ready(function (){
drawer = this.getElementById("drawer");
topBar = this.getElementById("top-bar");
drawerIsUp = false;
function winResize() {
drawer.style.top = topBar.offsetHeight + "px";
}
winResize();
$(window).bind({resize: winResize, scroll: winScroll});
});
Learn more about variable scopes
https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/

Related

How to control the scroll behaviour when there is a long event attached to it

I have a div with a scroll event on it. The scroll event is a sync function that takes some time to be computed. I see that the navigators might react to this situation with 2 different behaviours.
1- The scroll is freezed until the scroll event ends and then it will trigger the next scroll event.
2- I can scroll fluidly, but the scroll event is triggered once the previous event is done.
I would like to know how can I control and decide which one of the two scenarios the user will face.
codepen: https://codepen.io/xmorelll/pen/wvdmbYq
const container = document.getElementById("container");
const text = document.getElementById("text");
container.addEventListener('scroll', (event) => {
//sleep 1Second to simulate that the program is busy.
let a = new Date().getTime();
while(new Date().getTime() < a + 1000){};
text.style.top = container.scrollTop + "px";
});
#container {
height: 300px;
width: 300px;
border: solid 1px black;
overflow: auto;
}
#content {
height: 1000px;
width: 100%;
background-image: linear-gradient(red, yellow);
position: relative;
}
#text {
position: absolute;
left: 0;
}
<div id="container">
<div id="content">
<span id="text">Hola</span>
</div>
</div>

How do I tell when the top or bottom of an element scrolls to the top of the page using vanilla javascript

I have a web page with two 100% height divs like this...
<style>
html, body{
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
}
#wrapper{
height:100%;
width:100%;
overflow: auto;
}
.scroll-item{
height: 100%;
width: 100%;
}
</style>
...
<body>
<div id="wrapper">
<div id="test1"
class="scroll-item">Simple Test 1</div>
<div id="test2"
class="scroll-item">Simple Test 2</div>
</div>
</body>
Now I want to "select" the one that is currently scrolled to. This means that the top of the element has reached the top of the browser but the bottom has not. This is where I am getting confused here is the JS...
<script type="module">
const body = document.getElementsByTagName("body")[0];
const handleScroll = function(info){
const items = body.getElementsByClassName("scroll-item");
for(let i = 0; i < length; i++){
const item = items[i];
// TODO How do I tell if it is there
}
}
body.addEventListener("wheel",handleScroll);
</script>
I have tried using the bounding box but I cannot figure out how to get that to work correctly.
How do I tell when the top or bottom of the element reaches the top of the browser (given possible offset for navbar)?
You can use getBoundingClientRect().
It gives you the DOMRect object containing the size and coordinates of an element.
...
if (item.getBoundingClientRect().top < 0) {
// items top has reached beyond window top
}
if (item.getBoundingClientRect().bottom > window.innerHeight) {
// items bottom is beyond window bottom
}
...
For advanced usage, see IntersectionObserver, which detects an elements visibility inside the viewport.
Use the wrapper to get current position and listen scroll event, also, is better to listen scroll instead of wheel event.
// Use the wrapper to get and listen scroll
const wrapper = document.querySelector('#wrapper')
const handleScroll = function(event) {
const top = wrapper.scrollTop;
document.querySelectorAll('.scroll-item').forEach((item, index) => {
// Calculate item bottom position
const bottom = item.offsetTop + item.offsetHeight;
// Is the scroll between item top and bottom?
if(top >= item.offsetTop && top < bottom) {
console.log(`Item ${index} is active`);
}
});
}
// scroll event is more accurate than wheel
wrapper.addEventListener("scroll", handleScroll);
html, body{
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
}
#wrapper{
height:100%;
width:100%;
overflow: auto;
}
.scroll-item{
height: 100%;
width: 100%;
}
<body>
<div id="wrapper">
<div id="test1"
class="scroll-item">Simple Test 1</div>
<div id="test2"
class="scroll-item">Simple Test 2</div>
</div>
</body>

How to scroll MULTIPLE scrollbars at the same time

When I scroll the wrapper div the 2 images should also scroll together with it but it seems only the 2 images sync together but not the wrapper
The 2 images should sync with the body's scrollbar
$(function(){
$('.linked').scroll(function(){
$('.linked').scrollTop($(this).scrollTop());
})
})
#left { width: 300px; height: 400px; overflow: scroll; float: left; }
#right { width: 300px; height: 400px; overflow: scroll; float: left; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<div id="left" class="linked">
<img src="http://upload.wikimedia.org/wikipedia/commons/5/51/Eiffel_Tower_(72_names).jpg">
</div>
<div id="right" class="linked">
<img src="http://upload.wikimedia.org/wikipedia/commons/5/51/Eiffel_Tower_(72_names).jpg">
</div>
This might help
let l = document.getElementById(".left");
let r = document.getElementById(".right");
l.addEventListener("scroll", function () {
r.scrollTop = l.scrollTop;
});
l.addEventListener("scroll", function () {
r.scrollTop = l.scrollTop;
});
If I understood correctly you're question is how to sync a set of scroll bars including the window scroll bar? This is possible and I will demonstrate for education purposes only however this is not something I would personally recommend.
Manipulating the browser window scroll bar can create a poor user experience as it interferes with the expected behavior of the scroll bar. Additionally, scrollbar manipulation can cause unexpected behavior and may not work consistently across different browsers and devices, leading to a fragmented user experience.
This example does not take into consideration the dimensions of the browser window and elements with scroll bars so be warned that it might not work as expected.
// Pseudo code
// 1. Query the scroll bars you want to sync
// 2. Add event listener to each of them
// 3. In the event listener, set the scroll position of the other scroll bar(s) to the same value
// Step 1
const scrollBarContainers = [
...document.querySelectorAll('.scrollable'),
document // Include document for window scrolling
];
// Step 2
scrollBarContainers.forEach(container => {
container.addEventListener('scroll', syncScrolls);
});
// Step 3
function syncScrolls(e) {
scrollBarContainers.forEach(container => {
if (container !== e.target) {
// The document element doesn't directly have a scrollLeft property.
// It's on the scrollingElement. The other elements have it directly.
if (container.scrollingElement) {
container.scrollingElement.scrollLeft = e.target.scrollLeft;
} else if (e.target.scrollingElement) {
container.scrollLeft = e.target.scrollingElement.scrollLeft;
} else {
container.scrollLeft = e.target.scrollLeft;
}
}
});
}
.container {
height: 100px;
border: 1px solid gray;
width: 900px;
margin-bottom: 1em;
overflow: auto;
}
span {
display: inline-block;
width: 1200px;
height: 50px;
background-color: red;
}
<div class="container scrollable"><span></span></div>
<div class="container scrollable"><span></span></div>

keep the background image fixed position and centered

In my project, I need to show a small image in center of the visible part of the container, with respect to the window i.e .loader. Even when the user scrolls the page, the image should be visible in center of .loader.
I successfully implemented this but now I am facing a edgecase which is when user scrolls the page "up to the header" or "down to the footer", the small image is hiding. demo.
This is actually normal behaviour but in these edgecases, I want the image to stick to top/bottom end of the .loader container.
What I want:
Keep the small image always at center of .loader container. (I already implemented this)
when scrolled to any end of .loader container, the image should stick to that end instead of hiding behind the container.
Fiddle
A solution using just css is preferred. I am looking for browser support in IE9+, chrome and firefox.
.header {
height: 600px;
width: 650px;
background-color: grey;
}
.left-side {
height: 300px;
width: 150px;
float: left;
background-color: red;
}
.loader {
background-image: url('http://i.imgur.com/U2njI.jpg');
margin-left: 150px;
height: 1500px;
width: 500px;
background-position: 345px center;
background-repeat: no-repeat;
background-attachment: fixed;
background-color: cornflowerblue;
}
.footer {
height: 600px;
width: 650px;
background-color: silver;
}
<div class="header"></div>
<div class="left-side"></div>
<div class="loader"></div>
<div class="footer"></div>
Here is a working solution with javascript, I hope its behaviour is how you expect it to be. I'm unfortunately not able to test it on IE9 right now but it should work (DEMO):
document.addEventListener('DOMContentLoaded',function() {
var loader = document.querySelector('.loader'),
loaderRect = loader.getBoundingClientRect(),
loaderTop = loaderRect.top + document.body.scrollTop,
loaderBottom = loaderTop + loader.offsetHeight,
initialBgPos = loader.style.backgroundPosition,
imageHeight = 141;
function onScroll() {
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
if(loaderTop >= (scrollTop + (window.innerHeight - imageHeight)/2)) {
loader.style.backgroundPosition='345px ' + (loaderTop - scrollTop) + 'px';
} else if(loaderBottom <= (scrollTop + (window.innerHeight + imageHeight)/2)) {
loader.style.backgroundPosition='345px ' + (loaderBottom - scrollTop - imageHeight) + 'px';
} else {
loader.style.backgroundPosition = initialBgPos;
}
}
window.addEventListener('scroll', onScroll);
onScroll();
});
To achieve what I think you want. We have to set the position of the .loader div to fixed, then it'll always stay where it's placed, regardless of whether the user scrolls the page, the div will scroll too. In here's how to set the position of loader to fixed in CSS (you may also have to get the position of your fixed div):
.loader{
position: fixed;
left: 100px;
top: 300px;
}
Here's your upadted JSFiddle: http://jsfiddle.net/Ezhb4/4/

Positioning absolute div inside relative parent - webkit browsers

My HTML basically looks like this:
<div id="#container">
<div id="left_col">
left stuff
</div>
<div id="middle_col">
middle stuff
</div>
<div id="right_col">
<div id="anchor"></div>
<div id="floater>
The problem div
</div>
</div>
</div>
The container div is pushed 82px to the left, because I don't want the rightmost column to be used as part of the centering (there is a header navigation bar above that is the size of left_col and middle_col):
#container {
width: 1124px;
margin-left: auto;
margin-right: auto;
text-align: left;
color: #656f79;
position: relative;
left: 82px;
}
#left_col {
float:left;
width: 410px;
background-color: #fff;
padding-bottom: 10px;
}
#middle_col {
width: 545px;
float: left;
}
#right_col {
float: left;
width: 154px;
margin-left: 5px;
position:relative;
}
#floater {
width: 154px;
}
I'm using the following javascript to keep the #floater div in position as you scroll down the page:
var a = function() {
var b = $(window).scrollTop();
var d = $("#anchor").offset().top;
var c = $("#floater");
if (b > d) {
c.css({position:"fixed",top:"10px"});
} else {
c.css({position:"absolute",top:""});
}
};
$(window).scroll(a);
a();
The problem I'm having is that in WebKit based browsers, once jQuery makes the floater div's positioning fixed so it will stay 10px from the top, that "left: 82px" from #container goes out the window, causing #floater to jump 82px to the left. This doesn't happen in FF or IE. Does anybody know a solution to this?
Update: Solved
I've solved this problem by not using fixed positioning, but instead using absolute positioning. I changed the javascript to set the top CSS property of div#floater to be based on the value $(window).scrollTop() if div#anchor's top offset is greater than $(window).scrollTop(). Pretty simple.
So the a() function now looks like this:
var a = function() {
var b = $(window).scrollTop();
var d = $("#anchor").offset().top;
var c = $("#floater");
if (b > d) {
var t = b-200; //200px is the height of the header, I subtract to make it float near the top
c.css({top:t+"px"});
} else {
c.css({top:""});
}
};

Categories