Javascript Sticky Element onscroll avoid jumping - javascript

I'm trying to fix an element when I scroll down.
The code works fine but as you can see at the following link, the bar with the 3 yellow buttons jumps when it is about to reach the Top!
There seems to be some template css class causing this problem,
but I can't figure out which one
This is the code
var fixmeTop = $('.pulsanti').offset().top;
$(window).scroll(function() {
var currentScroll = $(window).scrollTop();
if (currentScroll >= fixmeTop) {
$('.pulsanti').css({
position: 'fixed',
top: '0',
zIndex: '1020'
});
} else {
$('.pulsanti').css({
position: 'relative'
});
}
});
thank you everyone

I think the problem is that fixmeTop is assigned before some elements are loaded:
var fixmeTop = $('.pulsanti').offset().top;
My chrome console outputs this:
> fixmeTop
< 2314.3374633789062
> $('.pulsanti').offset().top;
< 3207.5623779296875
You can see that the fixmeTop variable is not the real element position.
Maybe you need to assign it in body.onload()?
Update
After executing fixmeTop = $('.pulsanti').offset().top; in the chrome console after the page is loaded, I can verify that the element is sticking smoothly to the top of the page.
Adding this code fragment will create a handler function to be called when the window's load event fires. That will happen after everything (divs, images) is loaded. This way the fixmeTop will contain the true element position.
window.onload = function () {
fixmeTop = $('.pulsanti').offset().top;
}
You can keep the variable declaration in the original script, or remove it and add var here.
Also, bear in mind that it will not work until everything on the page is loaded and that it will not be valid when anything on the page moves - eg. page resize, divs rearranged. You would have to add listeners for every such event to adjust the value accordingly. Something like this may be useful:
function updateFixmeTop() {
fixmeTop = $('.pulsanti').offset().top;
}
window.addEventListener("resize", updateFixmeTop);
edit: changed element body to window in event listener
Hope it helps!
Update2
Let's say you added the code for resize event listener and scrolled past the point where the element should stick to the top. If you were to resize window fixmeTop would be assigned a value that corresponds to the element being on top of the page, and not the original element position.
To fix this you may want to add a dummy element without any margin or padding:
<div id="elementJustBeforeFixmeTop"></div> <! -- dummy element -->
<div class="pulsanti"> <! -- sticky element -->
...
</div>
And refer to its position instead of the sticky element
fixmeTop = $('#elementJustBeforeFixmeTop').offset().top;
This way you will store the scroll position at which you want the element to stick and it will not be different if the element is already at the top.
You may want to check if your page doesn't change its layout somewhere else and also update the fixmeTop value there to ensure it's always pointing at the right element.

Related

Keep scroll position inside div after reload php file with ajax

I have a div with it's own scroll bar which is being reloaded by AJAX (php file). When I scroll inside this div and reload it, the inner scrollbar gets sent back to the top. I would like for the scroll bar to remain at the position where I originally scrolled to.
<style>
#div2 {
height: 400px;
width: 100%;
overflow-y:scroll;
}
</style>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
setInterval(function () {
$('#div1').load('shownic8.php');
},7000);
</script>
<div id="div1">
</div>
Here is the code from "shownic8.php" file
<div id="div2">
...
</div>
Can you help me keep the position of my scroll bar? Thank you very much.
Check https://api.jquery.com/scrolltop/
Before .load() store current scroll position:
var pos = $('#your-container').scrollTop();
Use .load() callback (http://api.jquery.com/load/) to restore scroll position:
$('#your-container').scrollTop(pos);
Using your code:
setInterval(function () {
var scrollTarget = $('#div1');
var pos = scrollTarget.scrollTop();
scrollTarget.load('shownic8.php', function() {
$('#div1').scrollTop(pos);
});
},7000);
You can either use DOM element's scrollTop property or jQuery function of the same name.
But I don't advice you to do so because saving and restoring scroll position you couldn't avoid a slight blinking effect every time you reload contents.
So, instead, I would recommend to update items that actually change instead of reloading the whole contents, so the scrollTop property gets never changed.
Of course, (to do it the right way) it implies modifying your shownic8.php page (or implementing another different route instead) to return some structured data and use them to fill or update your div contents.
On the other hand, you can try another, slightly dirty, approach to hide that blinking efect (replacing it by a less obvious side effect). That is:
Instead of loading your contents directly into #div1 element, create a new div inside it (appending through .append() or .appendTo()) and load into it.
After that (at least in reloading operations), remove previous contents so that new content climbs up to the top position (and not altering scroll position).
Example: (untested)
setInterval(function () {
var prevContents = $("#div1>*");
$('<div></div>')
.load('shownic8.php')
.appendTo('#div1')
;
prevContents.remove();
},7000);

Update position of div while scrolling

For another stack question I have tried to write a short script. It should track the position of a div .list_item while scrolling and apply the .offset().top to another div.
To test if the function is fired I have written a console.log inside my code, which was never seen again. Why does my function do not fire while scrolling?
$(document).ready(function() {
// fire function everytime the window is scrolled
$(window).scroll(function(){
// set element to relate to
var list_items = $('div.list_item');
// get each position
list_items.each(function() {
// store offset().top inside var
var list_item_position = $(this).offset().top;
// select previous dropdown_list item
$(this).prev().find('ul.dropdown_list').css({
// apply offset top
top: list_item_position + "px"
});
});
// write to console to track changes
console.log('positions updated');
}); // .scroll
}); // document.ready
Suggestions appreciated!
JSFIDDLE DEMO
As pointed out by #Carlos Delgado in the comments, $(window).scroll tracks, if the windows is being scrolled, while setting the first line to $('#wrapper').scroll tracks, if the #wrapper is being scrolled, which works perfect.
Thanks for pointing this out and the other helpful comments!

How to hide a button that is affixed when it 'floats' over a DIV?

I have a button back-to-top that is affixed to the left side of the screen - it uses scrollTop to slide-scroll to the top of the page when it's clicked. When the page the loads, the button is visible and does not cover anything that is readable etc.
When a user scrolls down the page, the button goes over certain DIVs that have text content. When the button goes into such a DIV I want it to hide using .hide(). Can't get it to work, here's what I have:
var p = $('a.back-to-top');
var position = p.position();
if(position == $('#about-me')){
$('a.back-to-top').hide();
}
Is if(position == $('#about-me')) the correct way to check if the button's position is in the #about-me DIV? Or, should I create a variable similar to position for the DIV?
EDIT: A messy but simple fiddle
You will need to do this check inside of a callback .. probably $(window).scroll so that it is checked each time the window scrolls; otherwise, it is only checked when the page loads.
I don't think you want to use position either as that is position relative to parent. Instead, you probably want .offset. This returns an object with top and left members. An == comparison does not make sense, especially to a jQuery object. You want to use:
$(window).on('scroll', function () {
var offset = $("a.back-to-top").offset().top;
var within = $("#about-me").offset().top;
if (offset >= within && offset <= within + $("#about-me").height()) {
$("a.back-to-top").hide();
}
else {
$("a.back-to-top").show();
}
});​
The offset of .back-to-top changes with scrolling if it has a fixed position, but the offset of the static block does not change, so you can do this comparison.
See it in action: http://jsfiddle.net/QnhgF/
http://api.jquery.com/position/ - position() method returns a position object which has .left and .top properties. So basically, you can't compare position to some object returned by a selector. Instead, you should compare the "top" property values of both elements.
For example you have:
var p = $('a.back-to-top');
var position = p.position();
Also get this:
var aboutMePosition = $('#about-me').position();
And then you can compare:
aboutMePosition.top and position.top whichever way you need.

ScrollIntoView() causing the whole page to move

I am using ScrollIntoView() to scroll the highlighted item in a list into view.
When I scroll downwards ScrollIntoView(false) works perfectly.
But when I scroll upwards, ScrollIntoView(true) is causing the whole page to move a little which I think is intended.
Is there a way to avoid the whole page move when using ScrollIntoView(true)?
Here is the structure of my page
#listOfDivs {
position:fixed;
top:100px;
width: 300px;
height: 300px;
overflow-y: scroll;
}
<div id="container">
<div id="content">
<div id="listOfDivs">
<div id="item1"> </div>
<div id="item2"> </div>
<div id="itemn"> </div>
</div>
</div>
</div>
listOfDivs is coming from ajax call. Using mobile safari.
Fixed it with:
element.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })
see: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
You could use scrollTop instead of scrollIntoView():
var target = document.getElementById("target");
target.parentNode.scrollTop = target.offsetTop;
jsFiddle: http://jsfiddle.net/LEqjm/
If there's more than one scrollable element that you want to scroll, you'll need to change the scrollTop of each one individually, based on the offsetTops of the intervening elements. This should give you the fine-grained control to avoid the problem you're having.
EDIT: offsetTop isn't necessarily relative to the parent element - it's relative to the first positioned ancestor. If the parent element isn't positioned (relative, absolute or fixed), you may need to change the second line to:
target.parentNode.scrollTop = target.offsetTop - target.parentNode.offsetTop;
var el = document.querySelector("yourElement");
window.scroll({top: el.offsetTop, behavior: 'smooth'});
I had this problem too, and spent many hours trying to deal with it. I hope my resolution may still help some people.
My fix ended up being:
For Chrome: changing .scrollIntoView() to .scrollIntoView({block: 'nearest'}) (thanks to #jfrohn).
For Firefox: apply overflow: -moz-hidden-unscrollable; on the container element that shifts.
Not tested in other browsers.
Play around with scrollIntoViewIfNeeded() ... make sure it's supported by the browser.
in my context, he would push the sticky toolbar off the screen, or enter next to a fab button with absolute.
using the nearest solved.
const element = this.element.nativeElement;
const table = element.querySelector('.table-container');
table.scrollIntoView({
behavior: 'smooth', block: 'nearest'
});
I've added a way to display the imporper behavior of the ScrollIntoView - http://jsfiddle.net/LEqjm/258/
[it should be a comment but I don't have enough reputation]
$("ul").click(function() {
var target = document.getElementById("target");
if ($('#scrollTop').attr('checked')) {
target.parentNode.scrollTop = target.offsetTop;
} else {
target.scrollIntoView(!0);
}
});
jQuery plugin scrollintoview() increases usability
Instead of default DOM implementation you can use a plugin that animates movement and doesn't have any unwanted effects. Here's the simplest way of using it with defaults:
$("yourTargetLiSelector").scrollintoview();
Anyway head over to this blog post where you can read all the details and will eventually get you to GitHub source codeof the plugin.
This plugin automatically searches for the closest scrollable ancestor element and scrolls it so that selected element is inside its visible view port. If the element is already in the view port it doesn't do anything of course.
Adding more information to #Jesco post.
Element.scrollIntoViewIfNeeded() non-standard WebKit method for Chrome, Opera, Safari browsers.
If the element is already within the visible area of the browser window, then no scrolling takes place.
Element.scrollIntoView() method scrolls the element on which it's called into the visible area of the browser window.
Try the below code in mozilla.org scrollIntoView() link. Post to identify Browser
var xpath = '//*[#id="Notes"]';
ScrollToElement(xpath);
function ScrollToElement(xpath) {
var ele = $x(xpath)[0];
console.log( ele );
var isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);
if (isChrome) { // Chrome
ele.scrollIntoViewIfNeeded();
} else {
var inlineCenter = { behavior: 'smooth', block: 'center', inline: 'start' };
ele.scrollIntoView(inlineCenter);
}
}
Just to add an answer as per my latest experience and working on VueJs. I found below piece of code ad best, which does not impact your application in anyways.
const el = this.$el.getElementsByClassName('your_element_class')[0];
if (el) {
scrollIntoView(el,
{
block: 'nearest',
inline: 'start',
behavior: 'smooth',
boundary: document.getElementsByClassName('main_app_class')[0]
});
}
main_app_class is the root class
your_element_class is the element/view where you can to scroll into
And for browser which does not support ScrollIntoView() just use below library its awesome
https://www.npmjs.com/package/scroll-into-view-if-needed
I found (in Chrome) I could more reliably scroll my element to the top of my parent div (without moving the page) if I scrolled from the bottom up to my element rather than from the top down to my element. Otherwise while my element would scroll into view, it would sometimes still be lower than desired within the div.
To achieve this, I am scrolling in two steps:
myScrollableDiv.scrollTop = myScrollableDiv.scrollHeight which instantly scrolls to the bottom of my scrollable div
(as per other answers here) Scroll my the element into view with animation:
myElementWithinTheScrollingDiv.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
})
Using Brilliant's idea, here's a solution that only (vertically) scrolls if the element is NOT currently visible. The idea is to get the bounding box of the viewport and the element to be displayed in browser-window coordinate space. Check if it's visible and if not, scroll by the required distance so the element is shown at the top or bottom of the viewport.
function ensure_visible(element_id)
{
// adjust these two to match your HTML hierarchy
var element_to_show = document.getElementById(element_id);
var scrolling_parent = element_to_show.parentElement;
var top = parseInt(scrolling_parent.getBoundingClientRect().top);
var bot = parseInt(scrolling_parent.getBoundingClientRect().bottom);
var now_top = parseInt(element_to_show.getBoundingClientRect().top);
var now_bot = parseInt(element_to_show.getBoundingClientRect().bottom);
// console.log("Element: "+now_top+";"+(now_bot)+" Viewport:"+top+";"+(bot) );
var scroll_by = 0;
if(now_top < top)
scroll_by = -(top - now_top);
else if(now_bot > bot)
scroll_by = now_bot - bot;
if(scroll_by != 0)
{
scrolling_parent.scrollTop += scroll_by; // tr.offsetTop;
}
}
ScrollIntoView() causes page movement. But the following code works fine for me and move the screen to the top of the element:
window.scroll({
top: document.getElementById('your-element')?.offsetParent.offsetTop,
behavior: 'smooth',
block: 'start',
})
i had the same problem, i fixed it by removing the transform:translateY CSS i placed on the footer of the page.
FWIW: I found (in Chrome 95, and Firefox 92 (all Mac)) that using:
.scrollIntoView({ behavior:'smooth', block:'center'});
on a scrollable list of options would scroll the body element a little, so I opted to use:
.scrollIntoView({ behavior:'smooth', block:'nearest'});
and select an option past the one I wanted centered (e.g. in a scrollable elem with 5 lines/options viewable, I selected the 2nd option past the one I wanted centered, thereby centering the desired element.

Have a div cling to top of screen if scrolled down past it [duplicate]

This question already has answers here:
How can I make a div stick to the top of the screen once it's been scrolled to?
(22 answers)
Closed 10 years ago.
I have a div which, when my page is first loaded, is about 100px from the top (it holds some buttons etc. for the page).
When a user scrolls past it, I would like the div to "follow" the user in that it attaches to the top of the screen. When the user returns to the top of the page, I want it back in its original position.
Visualization - xxxxx is the div:
Default (page load) User vertically scrolled well past it
--------- ---------
| | |xxxxxxx| < after div reaches top of screen when
|xxxxxxx| | | page is scrolled vertically, it stays
| | | | there
--------- ---------
The trick is that you have to set it as position:fixed, but only after the user has scrolled past it.
This is done with something like this, attaching a handler to the window.scroll event
// Cache selectors outside callback for performance.
var $window = $(window),
$stickyEl = $('#the-sticky-div'),
elTop = $stickyEl.offset().top;
$window.scroll(function() {
$stickyEl.toggleClass('sticky', $window.scrollTop() > elTop);
});
This simply adds a sticky CSS class when the page has scrolled past it, and removes the class when it's back up.
And the CSS class looks like this
#the-sticky-div.sticky {
position: fixed;
top: 0;
}
EDIT- Modified code to cache jQuery objects, faster now.
The trick to make infinity's answer work without the flickering is to put the scroll-check on another div then the one you want to have fixed.
Derived from the code viixii.com uses I ended up using this:
function sticky_relocate() {
var window_top = $(window).scrollTop();
var div_top = $('#sticky-anchor').offset().top;
if (window_top > div_top)
$('#sticky-element').addClass('sticky');
else
$('#sticky-element').removeClass('sticky');
}
$(function() {
$(window).scroll(sticky_relocate);
sticky_relocate();
});
This way the function is only called once the sticky-anchor is reached and thus won't be removing and adding the '.sticky' class on every scroll event.
Now it adds the sticky class when the sticky-anchor reaches the top and removes it once the sticky-anchor return into view.
Just place an empty div with a class acting like an anchor just above the element you want to have fixed.
Like so:
<div id="sticky-anchor"></div>
<div id="sticky-element">Your sticky content</div>
All credit for the code goes to viixii.com
There was a previous question today (no answers) that gave a good example of this functionality. You can check the relevant source code for specifics (search for "toolbar"), but basically they use a combination of webdestroya's solution and a bit of JavaScript:
Page loads and element is position: static
On scroll, the position is measured, and if the element is position: static and it's off the page then the element is flipped to position: fixed.
I'd recommend checking the aforementioned source code though, because they do handle some "gotchas" that you might not immediately think of, such as adjusting scroll position when clicking on anchor links.
Use position:fixed; and set the top:0;left:0;right:0;height:100px; and you should be able to have it "stick" to the top of the page.
<div style="position:fixed;top:0;left:0;right:0;height:100px;">Some buttons</div>

Categories

Resources