Sticky sidebar flickers during scroll - javascript

I created this sidebar which sticks when the bottom of the div reaches it's bottom. However, it seems to flicker when I scroll. Could you help what am I doing wrong?
HTML
<div id="header"></div>
<div id="stickymain">
<div class="side" id="stickyside">
<p>
This is the best we could do and there's nothing more one could expect from here to carry from onwards. I think there's nothing better too.
</p>
<p>
This is the best we could do and there's nothing more one could expect from here to carry from onwards. I think there's nothing better too. This is the best we could do and there's nothing more one could expect from here to carry from onwards. I think there's nothing better too. This is the best we could do and there's nothing more one could expect from here to carry from onwards. I think there's nothing better too.11
</p>
</div>
<div class="main"></div>
<div class="main"></div>
<div class="main"></div>
<div class="main"></div>
<div class="main"></div>
</div>
<div id="footer"></div>
<script>
CSS
body {
margin: 0;
padding: 10px;
}
#header {
height: 100px;
margin: 0 0 10px;
background: red;
}
#content {
position: relative;
float: left;
width: 100%;
height: auto;
margin: 0 -110px 0 0;
}
.side {
float: right;
width: 100px;
/* min-height: 500px; */
margin: 0 0 0 10px;
background: linear-gradient(red, yellow);
}
.main {
height: 600px;
margin: 0 110px 10px 0;
background: lightgray;
}
#footer {
clear: both;
height: 100px;
background: orange;
}
JS
jQuery(document).ready(function() {
jQuery.fn.stickyTopBottom = function(){
var options = {
container: jQuery('#stickymain'),
top_offset: 0,
bottom_offset: 0
};
console.log(options);
let jQueryel = jQuery(this)
let container_top = options.container.offset().top
let element_top = jQueryel.offset().top
let viewport_height = jQuery(window).height()
jQuery(window).on('resize', function(){
viewport_height = jQuery(window).height()
});
let current_translate = 0
let last_viewport_top = document.documentElement.scrollTop || document.body.scrollTop
jQuery(window).scroll(function(){
var viewport_top = document.documentElement.scrollTop || document.body.scrollTop
let viewport_bottom = viewport_top + viewport_height
let effective_viewport_top = viewport_top + options.top_offset
let effective_viewport_bottom = viewport_bottom - options.bottom_offset
let element_height = jQueryel.height()
let is_scrolling_up = viewport_top < last_viewport_top
let element_fits_in_viewport = element_height < viewport_height
let new_translation = null
if (is_scrolling_up){
if (effective_viewport_top < container_top)
new_translation = 0
else if (effective_viewport_top < element_top + current_translate)
new_translation = effective_viewport_top - element_top
}else if (element_fits_in_viewport){
if (effective_viewport_top > element_top + current_translate)
new_translation = effective_viewport_top - element_top
}else {
let container_bottom = container_top + options.container.height()
if (effective_viewport_bottom > container_bottom)
new_translation = container_bottom - (element_top + element_height)
else if (effective_viewport_bottom > element_top + element_height + current_translate)
new_translation = effective_viewport_bottom - (element_top + element_height)
}
if (new_translation != null){
current_translate = new_translation;
console.log('i am here at css');
jQueryel.css('transform', ('translate(0, '+current_translate+'px)'));
}
last_viewport_top = viewport_top
});
}
jQuery('#stickyside').stickyTopBottom();
});
Except for the flickering issue when I scroll, everything else is working just the way I want. I'm on Mac using Chrome, Firefox and Safari.
CodePen Demo

Instead of using only javascript to do all the work, you can use css properties to help you.
You can just change the position property of your side bar when reaching a certain point of the viewport:
$(window).scroll(function() {
var sTop = $(this).scrollTop();
var footerTop = $('#footer').offset().top;
var sideHeight = $('.side').height();
var headerHeight = $('#header').height();
if(sTop > headerHeight + (sideHeight/2)) {
$('.side').css({
position:'fixed',
bottom:'10px'
});
} else {
$('.side').css({
position:'absolute',
bottom:'inherit'
});
}
});
See this PEN
I hope this one it's ok! :)

Related

how to make a box reach the edge of a bigger box

javascript beginner here! so i'm trying to do a box(that is inside a larger box) move from the top to the edge of the box. Here's the code:
var boxcont = document.getElementById("boxcont");
var boxbtn = document.getElementById("boxbtn");
boxbtn.addEventListener("click", function() {
var loc = 0;
var timebox = setInterval(boxmove, 5);
function boxmove() {
if (loc == 320) {
clearInterval(timebox);
} else {
loc++;
boxcont.style.top = loc + "px";
boxcont.style.left = loc + "px";
}
}
});
#movebox {
width: 300px;
height: 350px;
background-color: grey;
}
#boxcont {
width: 30px;
height: 30px;
background-color: indianred;
position: relative;
}
<div id="movebox">
<div id="boxcont"></div>
</div>
<button id="boxbtn">Move the box</button>
The problem is that the small box doesn't exactly ends up at the edge, it goes more to the right. I tried doing
boxcont.style.left = (loc - 0.5) + "px";
but doesn't work. pretty sure the solution is simple but as a newbie here it's confusing me :p. Oh and i also tried doing ++ to the 0.5 and Number(0.5) so it reads it as a decimal but still doesn't work!
the big gray box is not set to the correct height and width that corresponds with the small red box's movement. You have it going down 1 and to the right 1 every 5 however, your actually going across a rectangle, not a square. set your width and height the same for the gray box and slightly adjust the stopping point to a little bit less.
var boxcont = document.getElementById("boxcont");
var boxbtn = document.getElementById("boxbtn");
boxbtn.addEventListener("click", function() {
var loc = 0;
var timebox = setInterval(boxmove, 5); // every five milliseconds
function boxmove() {
if (loc == 290) {
clearInterval(timebox);
} else {
loc++;
boxcont.style.top = loc + "px";
boxcont.style.left = loc + "px";
}
}
});
#movebox {
width: 300px;
height: 350px;
background-color: grey;
}
#boxcont {
width: 30px;
height: 30px;
background-color: indianred;
position: relative;
}
<div id="movebox" style = "height: 320px; width: 320px">
<div id="boxcont" ></div>
</div>
<button id="boxbtn">Move the box</button>
if (loc == 270) {
instead of
if (loc == 320) {
Gets you there.
300px is the width of the containing div and the moving div is 30px wide so 300-30=270px
var boxcont = document.getElementById("boxcont");
var boxbtn = document.getElementById("boxbtn");
boxbtn.addEventListener("click", function() {
var loc = 0;
var timebox = setInterval(boxmove, 5);
function boxmove() {
if (loc == 270) {
clearInterval(timebox);
} else {
loc++;
boxcont.style.top = loc + "px";
boxcont.style.left = loc + "px";
}
}
});
#movebox {
width: 300px;
height: 350px;
background-color: grey;
}
#boxcont {
width: 30px;
height: 30px;
background-color: indianred;
position: relative;
}
<div id="movebox">
<div id="boxcont"></div>
</div>
<button id="boxbtn">Move the box</button>

PointerEvents: Detect touching "through" an element

Using pointer events, I can't find the right event to trigger for finger-based touches on smartphones (tested with Chrome Android and Chrome Devtools with mobile emulation).
What I need: A "hover" event if you touch action passes through an element while holding the finger down moving over the screen.
That is, put your finger down outside the element, move through it, and move finger up only after completely passing through the element.
I attached a code snipped to clearify: I don't need events for the blue elements, I would only need respective "in/out" events for the red element in the snippet. The sample JS code will fire for the mouse, but on mobile it does not trigger any console.infos.
var elem = document.querySelector(".element");
elem.addEventListener("pointerover", function() {
console.clear();
console.info("pointerover triggered");
});
elem.addEventListener("pointerenter", function() {
console.clear();
console.info("pointerenter triggered");
});
elem.addEventListener("pointerleave", function() {
console.clear();
console.info("pointerleave triggered");
});
.outer {
width: 100px;
height: 100px;
border: 3px solid grey;
font-size: 12px;
color: white;
text-align:center;
touch-action: none;
}
.start {
position: relative;
top:0px;
left:0px;
width: 100px;
height: 20px;
background-color: blue;
}
.element {
position: relative;
top: 20px;
left: 0px;
width: 100px;
height: 20px;
background-color: red;
}
.end {
position: relative;
top: 40px;
right: 0;
width: 100px;
height: 20px;
background-color: blue;
}
<div class="outer">
<div class="start">Start touch here</div>
<div class="element">Move over here</div>
<div class="end">End touch here</div>
</div>
I hope that I understand you correctly. I wrote and tested for you two different solutions: pointerevents and touch events. In each move event from this events you can detect the current element with the function document.elementFromPoint().
Solution with pointerevents
Maybe you can use pointerevents – they work in Chrome Devtools with mobile emulation, but not work on my Android device (I think my device is too old). Or maybe you can use it with Pointer Events Polyfill. The Browser compatibility for pointerevents you can see here.
var elementFromPoint,
isFingerDown = false,
isThroughElemMoved = false,
elem = document.querySelector('.element'),
output = document.querySelector('#output');
document.addEventListener('pointerdown', function(e)
{
if(elem != e.target)
{
isFingerDown = true;
output.innerHTML = 'pointer-START';
}
});
document.addEventListener('pointermove', function(e)
{
elementFromPoint = document.elementFromPoint(e.pageX - window.pageXOffset, e.pageY - window.pageYOffset);
if(elem == elementFromPoint)
{
isThroughElemMoved = true;
output.innerHTML = 'pointer-MOVE';
}
});
document.addEventListener('pointerup', function(e)
{
if(isFingerDown && isThroughElemMoved && elem != elementFromPoint)
output.innerHTML = 'It is done!';
isFingerDown = isThroughElemMoved = false;
});
.outer
{
width: 100px;
height: 100px;
border: 3px solid grey;
font-size: 12px;
color: white;
text-align: center;
/*touch-action: none*/
}
.outer div{position: relative; left: 0; height: 20px}
.start{top: 0; background: blue}
.element{top: 20px; background: red}
.end{top: 40px; background: blue}
<div class="outer">
<div class="start">Start touch here</div>
<div class="element">Move over here</div>
<div class="end">End touch here</div>
</div>
<br><br>
<div id="output">info</div>
Solution with touch events
But you can use touch events too. Unfortunatelly, the events touchenter and touchleave were deleted from the specification and because of them we have to write a workaround using document.elementFromPoint() too.
The following snippet works only in the mobile emulation (tested with Chrome Devtools) or on devices which support touch events (tested with Android).
var elementFromPoint,
isFingerDown = false,
isThroughElemMoved = false,
elem = document.querySelector('.element'),
output = document.querySelector('#output');
document.addEventListener('touchstart', function(e)
{
if(elem != e.target)
{
isFingerDown = true;
output.innerHTML = 'touch-START';
}
});
document.addEventListener('touchmove', function(e)
{
var touch = e.touches[0];
elementFromPoint = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset);
if(elem == elementFromPoint)
{
isThroughElemMoved = true;
output.innerHTML = 'touch-MOVE';
}
});
document.addEventListener('touchend', function(e)
{
if(isFingerDown && isThroughElemMoved && elem != elementFromPoint)
output.innerHTML = 'It is done!';
isFingerDown = isThroughElemMoved = false;
});
.outer
{
width: 100px;
height: 100px;
border: 3px solid grey;
font-size: 12px;
color: white;
text-align: center;
/*touch-action: none*/
}
.outer div{position: relative; left: 0; height: 20px}
.start{top: 0; background: blue}
.element{top: 20px; background: red}
.end{top: 40px; background: blue}
<div class="outer">
<div class="start">Start touch here</div>
<div class="element">Move over here</div>
<div class="end">End touch here</div>
</div>
<br><br>
<div id="output">info</div>
Maybe the following links can help you:
Get element from point when you have overlapping elements?
How to find out the actual event.target of touchmove javascript
event?
Try this
<script>
var startElem = document.querySelector(".start");
var endElem = document.querySelector(".end");
var elem = document.querySelector(".element");
var started = false;
var passedThroughStart = false;
var passedThroughEnd = false;
var ended = false;
startElem.addEventListener("pointerdown", function(e){
started = true;
});
window.addEventListener("pointermove", function(e) {
var x = e.clientX;
var y = e.clientY;
var bounds = elem.getBoundingClientRect();
if( !passedThroughStart &&
x > bounds.left && x < bounds.left + bounds.width &&
y > bounds.top && y < bounds.top + bounds.height
){
passedThroughStart = true;
}
if( passedThroughStart && !passedThroughEnd &&
x > bounds.left && x < bounds.left + bounds.width &&
y > bounds.top + bounds.height
){
passedThroughEnd = true;
}
})
window.addEventListener("pointerup", function(e) {
var x = e.clientX;
var y = e.clientY;
var bounds = endElem.getBoundingClientRect();
ended = ( x > bounds.left && x < bounds.left + bounds.width && y > bounds.top && y < bounds.top + bounds.height)
if( started && passedThroughStart && passedThroughEnd && ended ){
console.log("Hooray!");
}
started = false;
passedThroughStart = false;
passedThroughEnd = false;
ended = false;
});
</script>
Alternatively use pointerenter and pointerleave rather than pointermove
elem.addEventListener('pointenter', function(e) {
passedThroughStart = true;
}
elem.addEventListener('pointleave', function(e) {
passedThroughEnd = true;
}

Slider handle needs to restricted inside the container, should not go beyond the container

In the custom slider i have created, the handle is moving beyond the container. But i want it to stay within the container limits. We could just do it simple by setting margin-left as offset in CSS. But My requirement is when the handle right end detect the container's end the handle should not be allowed to move anymore. Any help is appreciated. Thanks.
Demo Link: https://jsfiddle.net/mohanravi/1pbzdyyd/30/
document.getElementsByClassName('contain')[0].addEventListener("mousedown", downHandle);
function downHandle() {
document.addEventListener("mousemove", moveHandle);
document.addEventListener("mouseup", upHandle);
}
function moveHandle(e) {
var left = e.clientX - document.getElementsByClassName('contain')[0].getBoundingClientRect().left;
var num = document.getElementsByClassName('contain')[0].offsetWidth / 100;
var val = (left / num);
if (val < 0) {
val = 0;
} else if (val > 100) {
val = 100;
}
var pos = document.getElementsByClassName('contain')[0].getBoundingClientRect().width * (val / 100);
document.getElementsByClassName('bar')[0].style.left = pos + 'px';
}
function upHandle() {
document.removeEventListener("mousemove", moveHandle);
document.removeEventListener("mouseup", upHandle);
}
.contain {
height: 4px;
width: 450px;
background: grey;
position: relative;
top: 50px;
left: 40px;
}
.bar {
width: 90px;
height: 12px;
background: transparent;
border: 1px solid red;
position: absolute;
top: calc(50% - 7px);
left: 0px;
cursor: ew-resize;
}
<div class='contain'>
<div class='bar'></div>
</div>
You need to change
this
document.getElementsByClassName('bar')[0].style.left = pos + 'px';
to this
if(pos > 90){
document.getElementsByClassName('bar')[0].style.left = pos - 90 + 'px';
}
else{
document.getElementsByClassName('bar')[0].style.left = 0 + 'px';
}
since width of your bar is 90px I am subtracting 90.
See this updated fiddle

Div element walk with scrolling, smooth

I have just started a new website. It's important for me that the users sees this container, so I want it to scroll with the site.
I have an example for this thing: http://gruen-weiss-mannheim.de/
On this site on the left its a green container who stays with smooth scroll on the site if someone scrolls.
I hope you can help me, because I have already tried something, but in this way the container is always on the top.
Would be great If you could help me find a solution!
try {
window.onscroll = setNavPosition;
}
catch(e) {
document.documentElement.onscroll = setNavPosition;
}
function setNavPosition(){
$('.smooth').stop();
try {
if (document.body.scrollTop > document.documentElement.scrollTop) {
var targetPosition = document.body.scrollTop;
}
else {
var targetPosition = document.documentElement.scrollTop;
}
}
catch(e) {
var targetPosition = document.documentElement.scrollTop;
}
$('.smooth').animate({top: targetPosition}, 600);
}
.smooth {
height: 40px;
background-color: orange;
z-index: 2;
position: absolute;
width: 50px;
top: 50px;
}
.body {
height: 700px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="body"></div>
<div class="smooth"></div>
So every time I scroll, the container gets back to the top, not with the space in between...
If what you mean is that after the first scrolling it always stops by the edge of the viewport, it is because of this lines var targetPosition = document.body.scrollTop; and var targetPosition = document.documentElement.scrollTop;. I changed them:
try {
window.onscroll = setNavPosition;
}
catch(e) {
document.documentElement.onscroll = setNavPosition;
}
function setNavPosition(){
$('.smooth').stop();
try {
if (document.body.scrollTop > document.documentElement.scrollTop) {
var targetPosition = document.body.scrollTop + 50;
}
else {
var targetPosition = document.documentElement.scrollTop + 50;
}
}
catch(e) {
var targetPosition = document.documentElement.scrollTop;
}
$('.smooth').animate({top: targetPosition}, 600);
}
.smooth {
height: 40px;
background-color: orange;
z-index: 2;
position: absolute;
width: 50px;
top: 50px;
}
.body {
height: 700px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="body"></div>
<div class="smooth"></div>

Changing opacity when scrolling

My goal is to change the opacity of a DIV when I scroll down. It's important that the transition is smooth!
When the scrollTop of the body is 400, the opacity of the Test-div should be 1.
When the scrollTop of the body is 800, the opacity of the Test-div should be 0.
This is what I currently have, but it doesn't work.
window.addEventListener('scroll', function() {
if (document.body.scrollTop > 400) {
var currScrollPos2 = document.body.scrollTop;
document.getElementById('test').style.opacity = -currScrollPos2 / 400 + 2;
}
}
};
* {
margin: 0;
padding: 0;
}
body {
height: 2000px;
width: 100%;
}
#test {
width: 200px;
height: 200px;
background-color: red;
position: fixed;
}
<div id="test"></div>
You are close, but the body.scrollTop property does not work in all browsers.
I took the liberty of cleaning up your markup and code a little bit. You were missing a closing parenthesis at the end of you JavaScript, for example. There were also some superfluous rules in your CSS markup, that I deleted.
var test = document.getElementById('test');
window.addEventListener('scroll', function(e) {
// http://stackoverflow.com/a/28633515/962603
var scroll = window.pageYOffset || document.documentElement.scrollTop ||
document.body.scrollTop || 0;
test.style.opacity = Math.max(0, Math.min(1, -scroll / 400 + 2));
});
* {
margin: 0;
padding: 0;
}
body {
height: 2000px;
}
#test {
position: fixed;
width: 200px;
height: 200px;
background-color: red;
}
<div id="test"></div>
I had to replace document.body.scrollTop with window.pageYOffset to make it work.
See: document.body.scrollTop Firefox returns 0.
window.addEventListener('scroll', function() {
var currScrollPos2 = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
if (currScrollPos2 > 400) {
document.getElementById('test').style.opacity = -currScrollPos2 / 400 + 2;
}
}
);
* {
margin: 0;
padding: 0;
}
body {
height: 2000px;
width: 100%;
}
#test {
width: 200px;
height: 200px;
background-color: red;
position: fixed;
}
<div id="test"></div>
Just syntax error. Replace '}' by ')' at the end of your JS code.
Btw, I recommend using document.addEventListener instead of window.addEventListener
Here is correct code: https://jsfiddle.net/ye082ae9/
document.addEventListener('scroll', function(e) {
if (document.body.scrollTop > 400) {
var currScrollPos2 = document.body.scrollTop;
document.getElementById('test').style.opacity = -currScrollPos2/400 + 2;
}
});
Your code works fine, there is one little spelling error at the end. Just change }; to );
window.addEventListener('scroll', function() {
if (document.body.scrollTop > 400) {
var currScrollPos2 = document.body.scrollTop;
document.getElementById('test').style.opacity = -currScrollPos2/400 + 2;
}
}
);

Categories

Resources