Hide Sticky Div Once Scrolling Past Next Parent Div - javascript

I'm trying to hide a "sticky" div once it scrolls past the next parent div. I've currently successfully have it so it appears after scrolling "y > 100" but I'm having a lot of trouble getting the "Sticky Note" to disappear after scrolling past #break.
Example below.
http://codepen.io/anon/pen/BojKBx
$(document).scroll(function() {
var y = $(this).scrollTop();
if (y > 100) {
$('.bottomMenu').fadeIn();
} else {
$('.bottomMenu').fadeOut();
}
});
.bottomMenu {
display: none;
position: fixed;
bottom: 0;
width: 50%;
height: 60px;
border-bottom: 1px solid #000;
z-index: 1;
margin: 0 auto;
left: 50%;
margin-left: -500px;
text-align: center;
}
#header {
font-size: 50px;
text-align: center;
height: 60px;
width: 100%;
background-color: red;
}
#container {
height: 2500px;
}
#break {
text-align: center;
font-size: 30px;
margin-bottom: 300px;
width: 100%;
background-color: yellow;
}
#footer {
height: 60px;
background-color: red;
width: 100%;
bottom: 0;
}
<div id="header">Home</div>
<div class="bottomMenu">
<h2>Sticky Note</h2>
</div>
<div id="container"></div>
<div id="break">Should Not Be Seen After This Point</div>
<div id="footer"></div>

You can get Y position of a div (its vertical offset starting from the top of the page), and then add condition to show sticky note only when you're below the required "Y" coordinate, and above the required div. Example:
http://codepen.io/anon/pen/EVPKyP
Javascript code:
$(document).scroll(function () {
var bodyRect = document.body.getBoundingClientRect(),
elemRect = document.getElementById("break").getBoundingClientRect(),
offset = elemRect.top - bodyRect.top - window.innerHeight;
var y = $(this).scrollTop();
if (y > 100 && y < offset) {
$('.bottomMenu').fadeIn();
} else {
$('.bottomMenu').fadeOut();
}
});
Sources:
Retrieve the position (X,Y) of an HTML element
screen width vs visible portion

Related

position sticky using javascript makes div goes outside the parent

I have a menu on the left that I want to be always sticky, I'm using javascript for that for IE11 support.
The problem I'm having is that the right div goes to the left when it's sticky and doesn't keep it's position, the second issue is that the .content div width grows when the right div is sticky.
For the javascript part, I don't know how to make the right div to stop when it reaches the footer.
EDIT:
I managed to solve the second issue, the code is updated, I also tried to add a right value for the right div so it sticks in its initial vertical position, but that's not working because it changes when the screen gets resized.
How can I solve this?
Edit 2:
For the javascript issue I found this post which helped me resolve my issue:
Make sticky/fixed element stop at footer
var sticky = document.getElementsByClassName("sticky-element")[0];
var stickyAnchor = sticky.parentNode;
var state = false;
function getAnchorOffset() {
return stickyAnchor.getBoundingClientRect().top;
}
updateSticky = function (e) {
if (!state && (getAnchorOffset() < 0)) {
sticky.classList.add("is-sticky");
sticky.parentElement.classList.add("has-sticky");
state = true;
} else if (state && (getAnchorOffset() >=0 )) {
sticky.classList.remove("is-sticky");
sticky.parentElement.classList.remove("has-sticky");
state = false;
}
}
window.addEventListener('scroll', updateSticky);
window.addEventListener('resize', updateSticky);
updateSticky();
.main-wrapper {
margin: 48px 48px 0 48px;
max-width: 1366px;
}
.wrapper {
width: 100%;
display: flex;
justify-content: space-between;
position: relative;
}
.wrapper.has-sticky .content{
margin-right: calc(199px + 72px);
}
.content {
flex: 0 1 1040px;
width: calc(1040px - 72px);
min-width: 1%;
margin-right: 72px;
height: 1200px;
background-color: #e6e9f0;
}
.nav-menu {
position: static;
flex: 0 1 199px;
width: 199px;
min-width: 199px;
color: white;
height: 300px;
background-color: #04246a;
right: 10%;
}
footer {
background-color: yellow;
height: 300px;
margin-top: 50px;
}
.is-sticky {
top: 0;
position: fixed;
}
<div class="main-wrapper">
<div class="wrapper">
<div class="content">
Main content
</div>
<div class="nav-menu sticky-element">
<nav>
Side content
</nav>
</div>
</div>
<footer>
Footer content
</footer>
</div>
Are you looking for this?
The problem on your code is that whenever you set the position of your right div to fixed it then looks for its relative parent and jumps to the upper left position inside the parent. In your case, the parent div was the .wrapper, that's why it keeps on jumping to the left side and overlaps your main content div.
I added a parent container for the .nav-menu so it will still be in the same position when scrolling. With this, your .nav-menu element won't be using the .wrapper as its main parent. This will create a smooth scroll without noticing any change in position.
Happy coding!
var sticky = document.getElementsByClassName('sticky-element')[0];
var stickyAnchor = sticky.parentNode;
var state = false;
function getAnchorOffset() {
return stickyAnchor.getBoundingClientRect().top;
}
updateSticky = function (e) {
if (!state && getAnchorOffset() < 0) {
sticky.classList.add('is-sticky');
state = true;
} else if (state && getAnchorOffset() >= 0) {
sticky.classList.remove('is-sticky');
state = false;
}
};
window.addEventListener('scroll', updateSticky);
window.addEventListener('resize', updateSticky);
updateSticky();
.main-wrapper {
margin: 48px 48px 0 48px;
max-width: 80%;
}
.wrapper {
width: 100%;
display: flex;
justify-content: space-between;
position: relative;
}
.content {
flex: 0 1 80%;
width: calc(80% - 24px);
min-width: 1%;
margin-right: 24px;
height: 1200px;
background-color: #e6e9f0;
}
.nav-container {
flex-grow: 1;
width: 20%;
min-width: 200px;
position: relative;
display: block;
}
.nav-menu {
color: white;
width: 100%;
min-width: inherit;
height: 300px;
background-color: #04246a;
}
.is-sticky {
top: 0;
position: fixed;
width: calc(20% - 97px);
}
<div class="main-wrapper">
<div class="wrapper">
<div class="content">Main content</div>
<div class="nav-container">
<div class="nav-menu sticky-element">
<nav>Side content</nav>
</div>
</div>
</div>
</div>
var sticky = document.getElementsByClassName("sticky-element")[0];
var stickyAnchor = sticky.parentNode;
var state = false;
function getAnchorOffset() {
return stickyAnchor.getBoundingClientRect().top;
}
updateSticky = function (e) {
if (!state && (getAnchorOffset() < 0)) {
sticky.classList.add("is-sticky");
state = true;
} else if (state && (getAnchorOffset() >=0 )) {
sticky.classList.remove("is-sticky");
state = false;
}
}
window.addEventListener('scroll', updateSticky);
window.addEventListener('resize', updateSticky);
updateSticky();
.main-wrapper {
margin: 48px 48px 0 48px;
max-width: 80%;
}
.wrapper {
width: 100%;
display: flex;
justify-content: space-between;
position: relative;
}
.content {
flex: 0 1 80%;
width: calc(80% - 24px);
min-width: 1%;
margin-right: 24px;
height: 1200px;
background-color: #e6e9f0;
}
.nav-menu {
position: static;
flex: 0 1 20%;
width: 20%;
min-width: 20%;
color: white;
height: 300px;
background-color: #04246a;
}
.is-sticky {
top: 0;
right:5%;
position: fixed;
}
<div class="main-wrapper">
<div class="wrapper">
<div class="content">
Main content
</div>
<div class="nav-menu sticky-element">
<nav>
Side content
</nav>
</div>
</div>
</div>

On horizontal scroll of overflown div, fill in progress bar

I have a div which contains an image. The container of this image has overflow:scroll, so that the user can scroll left or right to see the rest of the image.
I've also implemented a progress bar, which should indicate how much of the image remains to scroll. I.e. if the user has scrolled 5% to the right, it'll fill up 5% of the progress bar (and vice versa).
I can get the function working based on scrollHeight, but can't get it working based on scrollWidth.
Where am I going wrong?
window.onscroll = function() {myFunction()};
function myFunction() {
var winScroll = document.body.scrollTop || document.documentElement.scrollTop;
var width = document.documentElement.scrollLeft - document.documentElement.clientWidth;
var scrolled = (winScroll / width) * 100;
document.getElementById("myBar").style.width = scrolled + "%";
}
.imgCont {
background: black;
overflow: scroll;
position: relative;
}
.imgCont img {
width: auto;
max-width: none;
}
.progress-container {
width: 100%;
height: 8px;
background: blue;
}
.progress-bar {
height: 8px;
background: red;
width: 0%;
}
<div class="imgCont">
<img src="https://i.imgur.com/KhWo66L.png">
</div>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>
You need to add listeners on the .imgCont element and use it's scrollLeft, scrollWidth and clientWidth properties
let scrEl = document.getElementById("scr-el")
scrEl.addEventListener('scroll', event => {
let scrolled = (scrEl.scrollLeft / (scrEl.scrollWidth - scrEl.clientWidth) ) * 100
document.getElementById("myBar").style.width = scrolled + "%"
});
.imgCont {
background: black;
overflow-x: scroll;
position: relative;
}
.imgCont img {
width: auto;
max-width: none;
}
.progress-container {
width: 100%;
height: 8px;
background: blue;
}
.progress-bar {
height: 8px;
background: red;
width: 0%;
}
<div id="scr-el" class="imgCont">
<img src="https://i.imgur.com/KhWo66L.png">
</div>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>
windows.onscroll won't emit any events while you scroll horizontally because scroll is happening in the element with class imgCont.
put an id imgCont
<div class="imgCont" id="imgCont">
<img src="https://i.imgur.com/KhWo66L.png">
</div>
and call the on scroll event as
document.getElementById("imgCont").onscroll
Jquery solution setps:
subtract the visible width of the image and the real image width
var winScroll = $(".imgCont img").width() - $(".imgCont").width();
get the left scroll position
var width = $(".imgCont").scrollLeft();
get the percentage from the width and position
var scrolled = ((width / winScroll) * 100);
Check the snippet:
$(function(){
$(".imgCont").scroll(function(){
var winScroll = $(".imgCont img").width() - $(".imgCont").width();
var width = $(".imgCont").scrollLeft();
var scrolled = ((width / winScroll) * 100);
$("#myBar").width(scrolled + "%");
});
});
.imgCont {
background: black;
overflow: scroll;
position: relative;
height:200px;
}
.imgCont img {
width: auto;
max-width: none;
}
.progress-container {
width: 100%;
height: 8px;
background: blue;
}
.progress-bar {
height: 8px;
background: red;
width: 0%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="imgCont">
<img src="https://static.toiimg.com/photo/msid-67868104/67868104.jpg?1368689">
</div>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>
Another solution.
let div = document.getElementById("theDiv")
div.addEventListener('scroll', function(e) {
let inner = window.innerWidth
let left = div.scrollLeft
let sWidth = div.scrollWidth
let total = sWidth - inner
let width = 1 * left / total * 100
if (width >= 100) {
return document.getElementById("myBar").style.width = "100%";
}
document.getElementById("myBar").style.width = `${width}%`;
});
body {
margin: 0;
}
.imgCont {
background: black;
overflow: auto;
position: relative;
}
.imgCont img {
width: auto;
max-width: none;
}
.progress-container {
width: 100%;
height: 8px;
background: blue;
}
.progress-bar {
height: 8px;
background: red;
width: 0%;
}
<div class="imgCont" id="theDiv">
<img src="https://i.imgur.com/KhWo66L.png">
</div>
<div class="progress-container">
<div class="progress-bar" id="myBar"></div>
</div>

How do I check whether the right/left edge of an element is overlapping the side of it's container?

I'm trying to display a right / left navigation arrow within a container (the arrows replace the existence of a scrollbar) when the corresponding edge of the content overlaps the container's sides.
Also, when the content is scrolled all the way to the end and can't scroll any further, the arrow should disappear.
My problem is, I'm confused as to how I write the function to check whether the element's contents are overlapping one edge or the other to hide one arrow or the other.
I started writing logic like this:
function setArrows(elem){
if (elem.scrollLeft() > 0) { //scroll position is greater than zero
// show left arrow
}
if () { //scroll position is less than zero
//show right arrow
}
}
but that doesn't seem to be the right logic. It sounded simpler in my head before I went to actually write the function.
How do I check whether the right/left edge of an element is overlapping the side of it's container?
Here's a Stack Snippet:
$('#wrapper').scroll(function(){
//check edges
});
div {
padding: 0px;
margin: 0px;
}
#wrapper {
width: 500px;
height: 100px;
background-color: blue;
overflow-x: scroll;
overflow-y:hidden;
}
#content {
width: 1000px;
height: 100px;
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="wrapper">
<div id="content">
</div>
</div>
You need to check if the content width minus the scrollLeft is greater than the wrapper width. If it is show the right scroller..
Something like this
$(function() {
var content = $('#content'),
arrows = $('.arrow'),
wrapper = $('#wrapper').scroll(function() {
//check edges
// handle left arrow
if (this.scrollLeft > 0) {
arrows.filter('.left').addClass('visible');
} else {
arrows.filter('.left').removeClass('visible');
};
// handle right arrow
if (content.outerWidth() - this.scrollLeft > wrapper.width()) {
arrows.filter('.right').addClass('visible');
} else {
arrows.filter('.right').removeClass('visible');
};
});
arrows.on('click', function() {
if ($(this).is('.left')) {
wrapper[0].scrollLeft -= 100;
} else {
wrapper[0].scrollLeft += 100;
}
return false;
});
// initialize
wrapper.trigger('scroll');
});
div {
padding: 0px;
margin: 0px;
}
#wrapper {
width: 500px;
height: 100px;
background-color: blue;
overflow-x: hidden;
overflow-y: hidden;
position: relative;
}
#content {
width: 1000px;
height: 100px;
background: url('http://lorempixel.com/1000/100/abstract/2') 0 0 no-repeat;
}
#full-container {
position: relative;
display: inline-block;
}
.arrow {
position: absolute;
top: 0;
bottom: 0;
width: 40px;
background-color: black;
display: none;
z-index: 100;
cursor: pointer;
color: #fff;
text-align: center;
line-height: 100px;
}
.arrow.visible {
display: block;
}
.arrow.left {
left: 0
}
.arrow.right {
right: 0
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="full-container">
<div class="arrow left"><</div>
<div id="wrapper">
<div id="content"></div>
</div>
<div class="arrow right">></div>
</div>

Stop DIV from crossing specified position

I need to stop #first and #second div from crossing the green line. The example will explain you everything.
http://jsfiddle.net/3QdJt/1/
It works good when I'm scrolling UP but when I go down DIVs are jumping.
HTML
<div id="first"></div>
<div id="second"></div>
<div id="donotcross"></div>
CSS
#donotcross {
position: relative;
width 500px;
height: 5px;
top: 487px;
background: green;
}
#first {
position: fixed;
width: 50px;
height: 50px;
background: red;
right: 20px;
bottom: 50%;
margin-bottom: 50px;
}
#second {
position: fixed;
width: 50px;
height: 50px;
background: yellow;
right: 20px;
bottom: 50%;
margin-bottom: -50px;
}
jQuery
$(window).scroll(function () {
windowPos = $(this).scrollTop();
asd = $('#first').offset().top;
tauto = 500 - windowPos;
if(asd <= 500) {
$('#first').css('top', tauto);
$('#second').css('top', 600 - windowPos);
}
if (asd > 500 ){
$('#first').css('top', 'auto');
$('#second').css('top', 'auto');
}
});
You need to check the windowPos variable instead of checking asd > 500. The jumping is caused by you moving the boxes to be over 500. So to avoid the jumping you might want to do this
$(window).scroll(function () {
windowPos = $(this).scrollTop();
asd = $('#first').offset().top;
tauto = 500 - windowPos;
if(asd <= 500) {
$('#first').css('top', tauto);
$('#second').css('top', 600 - windowPos);
}
if (windowPos > 500 ){
$('#first').css('top', 'auto');
$('#second').css('top', 'auto');
}
});
Edited:
To achive sticky effect, I manually calculated the boxes position instead of using top:auto
$(window).scroll(function () {
var center = $(window).height() / 2;
//the 50 is the the #first and #second offset diff divide by 2
var firstBoxTop = center - 50;
var secondBoxTop = center + 50;
var windowPos = $(this).scrollTop();
if((windowPos + firstBoxTop) < 500) {
firstBoxTop = 500 - windowPos;
}
if((windowPos + secondBoxTop) < 600) {
secondBoxTop = 600 - windowPos;
}
$('#first').css('top', firstBoxTop);
$('#second').css('top', secondBoxTop);
});
It's much effective if you put everything inside one wrapper div.
<div id="wrapper">
<div id="first"></div>
<div id="second"></div>
</div>
<div id="donotcross"></div>
CSS
body {
height: 2000px;
}
#donotcross {
position: relative;
width 500px;
height: 5px;
top: 487px;
background: green;
}
#wrapper {
position: fixed;
width: 70px;
height: 130px;
background: gray;
right: 20px;
text-align: center;
top: 50%;
}
#first {
background: red;
width: 50px;
height: 50px;
margin: 10px auto;
}
#second {
background: yellow;
width: 50px;
height: 50px;
margin: 0 auto;
}
This script will work with any height and top values of #wrapper (might be dynamic), and with any height and position of #donotcross (also might be dynamic).
var start_p = $('#wrapper').offset().top - $('#wrapper').height()/2;
$('#wrapper').css('top', $('#donotcross').offset().top + $('#donotcross').height());
$(window).scroll(function () {
var windowPos = $(this).scrollTop();
var dnc = $('#donotcross').offset().top + $('#donotcross').height() - windowPos;
if (start_p >= dnc) $('#wrapper').css('top', start_p);
else $('#wrapper').css('top', dnc);
});
http://jsfiddle.net/3QdJt/3/

Content that scrolls with page, but is inside a fixed positioned sidebar (with shadow effect)

I'm trying to develop following functionality for a sidebar. In a nutshell, Sidebar will have 100% height and will be absolutely positioned. Inside it there is content, which should scroll with the page, while sidebar is fixed. And as addition there is a shadow effect / response to show user if he can scroll down or up. So for example if there is something that can be scrolled down / up show shadow there, if not don't show shadow. I made a quick mockup, hopefully it will help you understand what happens if page is scrolled:
I made a quick jsfidle with content and sidebar, this is as far as I can get at the moment. http://jsfiddle.net/cJGVJ/3/
I assume this can't be achieved only with css and html and work cross browser, so jQuery solutions are welcome.
HTML
<div id="main"> <!-- Demo Content (Scroll down for sidebar) -->
<!-- Demo content here -->
</div>
<aside id="sidebar">
<div id="side-content-1"></div>
<div id="side-content-2"></div>
</aside>
CSS
body {
background: #f3f3f3;
margin: 0;
padding: 0;
}
#page-wrapper {
width: 90%;
margin: 0 auto;
overflow: hidden;
}
#sidebar {
width: 30%;
float: left;
background: #ffffff;
padding: 10px;
height: 100%;
position: fixed;
}
#main {
width: 60%;
float: right;
}
#side-content-1, #side-content-2 {
height: 400px;
}
#side-content-1 {
background: red;
opacity: 0.4;
}
#side-content-2 {
background: green;
opacity: 0.4;
margin-top: 10px;
}
EDIT
Bare in mind content in sidebar sums up to less than one of the page content, so once it reaches the bottom (so when bottom shadow disappears) it should stay there, while main content can be still scrolled down.
This is still a little rough, but its a start:
I went through and polished it a little more and took care of some window resizing issues.
I think this will work for you:
Updated Working Example
JS
$(window).scroll(function () {
var y = $(window).scrollTop();
var x = $(window).scrollTop() + $(window).height();
var s = $('#sidebar').height();
var o = $('#side-content-1').offset().top;
var q = $('#side-content-1').offset().top + $('#side-content-1').height();
var u = $('#side-content-2').offset().top;
if (x > s) {
$('#sidebar').css({
'position': 'fixed',
'bottom': '0',
'width': '27%'
});
$('#bottomShadow').hide();
}
if (x < s) {
$('#sidebar').css({
'position': 'static',
'width': '30%'
});
$('#bottomShadow').show();
}
if (y > o) {
$('#topShadow').show().css({
'position': 'fixed',
'top': '-2px'
});
}
if (y < o) {
$('#topShadow').hide();
}
if (y > q - 4 && y < q + 10) {
$('#topShadow').hide();
}
if (x > u - 10 && x < u + 4) {
$('#bottomShadow').hide();
}
});
var shadow = (function () {
$('#topShadow, #bottomShadow').width($('#sidebar').width());
});
$(window).resize(shadow);
$(document).ready(shadow);
CSS
body {
background: #f3f3f3;
margin: 0;
padding: 0;
}
#page-wrapper {
width: 90%;
margin: 0 auto;
overflow: hidden;
}
#sidebar {
width: 30%;
float:left;
background: #ffffff;
padding: 10px;
}
#main {
width: 60%;
float: right;
}
#side-content-1, #side-content-2 {
height: 400px;
}
#side-content-1 {
background: red;
opacity: 0.4;
}
#side-content-2 {
background: green;
opacity: 0.4;
margin-top: 10px;
}
#topShadow {
display:none;
height:2px;
box-shadow:0px 5px 4px #000;
}
#bottomShadow {
position:fixed;
bottom:-3px;
height:2px;
width:99%;
box-shadow:0px -5px 4px #000;
}
CSS Tricks website have an article on Persistent Headers where they accomplish something similar with a bit of JQuery

Categories

Resources