I'm using position:fixed to float some headers in my table when the user scrolls past the top, ala this method: http://css-tricks.com/persistent-headers/
Everything works great on regular pages, but whenever I have a table inside of another div or something with a fixed height and overflow:auto it blows up spectacularly.
What do I need to do to account for not just the page-wide scrolling, but also the scrolling of my container? And to account for scrolling past the 'top' of said container?
Thanks for any direction you guys can point me in.
Here's my existing code:
var mainheader = table.rows[0];
var tableHeight = table.getHeight();
var tableScroll = table.viewportOffset();
var headerHeight = mainheader.getHeight();
// TODO: If we're scrolling a subcontainer, we need to get the offset for that too! Somehow?
// If tableHeight < 1, it means our table his hidden right now, so skip it
if (tableHeight < 1)
continue;
// If we've scroll down past the very tip top of the table, but haven't yet scroll past the end of it, show our floating headers
if (tableScroll.top < 0 && tableHeight + tableScroll.top - headerHeight > 0)
{
table.floatingheader.style.display = '';
// Handle horizontal scrolling too!
table.floatingheader.style.left = (tableScroll.left + 1) + 'px'; // 1px offset for border
}
else
table.floatingheader.style.display = 'none';
NOTE: I have access to prototype.js, but do not have jQuery or any other 3rd party library. :/
I realize you're not using jQuery but you may want to look at this guys code on github and see how he implements it, then modify it for your purposes: http://webpop.github.com/jquery.pin/
Related
I'm making a slide scrolling page, and I'm trying to have it scroll like you're pulling a notecard up and with the next one right behind it.
To do this, I'm making them all fixed, and then moving their "top" position based off of scroll. But then I also need to make the body the size of the panel.
It's hard to describe what I'm doing, so here's the demo: https://codepen.io/NotDan/pen/vzraJE
Here's the particular piece of code that's causing my problem:
//what's going on here?
$(window).scroll(function(){
var panelNum = parseInt($(window).scrollTop()/$(window).height());//detemines panel number
var pixelMovement = ($(window).scrollTop())-(panelNum*$(".panel").height()); determines how many pixels to move the panel by
$('body').find(".panel:eq("+panelNum+")").css("top", -1*pixelMovement);
});
The problem is when the user scrolls quickly, the top position is not set accurately and there's some overhang. Again, hard to explain, but if you jump to the demo and scroll quickly you'll see what I mean.
Is there a more precise way of measuring scroll? Or is there a better way to do what I'm trying to? I've tried scrollmagic, and its "section wipe" feature is really close, but they bring the previous one up rather than move the current one up.
I tried making a condition to determine the panel number and everything started working.
var panelNum = 0;
var pixelMovement = 0;
$(window).scroll(function () {
pixelMovement = $(window).scrollTop() - panelNum * $(".panel").height(); // determines how many pixels to move the panel by
$("body")
.find(".panel:eq(" + panelNum + ")")
.css("top", -1 * pixelMovement);
if (Math.abs(pixelMovement) >= $(window).height()) {
panelNum++;
} else if (pixelMovement <= 0) {
panelNum--;
}
});
Here's the working demo: https://codepen.io/NotDan/pen/RYJeZq
I have seen various forms of this problem but nothing really helped me to solve the partial sticky sidebar/Bootstrap column behaviour. Let me start with the problem itself.
There is a big image close to the top of my page. Because of the page complexity, I am using Bootstrap column grid. The image spans over, let's say, 10 columns and I have left 2, belonging to the same row, on the left side to store a sidebar. This also allows me to vertically align the sidebar next to the image.
Now, the sidebar, what is now a Bootstrap column, should go sticky and should stay vertically aligned to the viewport once the scrollbar passes by. You can see in the fiddle that it kind of "jumps" instead of transitioning smoothly.
The other problem is that the sticky element/column should only remain sticky as long as its parent/container is visible. Which means that it should transition/be relative to the end of that container. Right now I have only managed to keep it sticky till the end of the page. It should stop above the red line (depicted in the fiddle).
Here is my jQuery logic so far.
$(document).ready(function(){
$(window).scroll(function(){
var elem = $("#refScroller").offset().top - ($("#refScroller").height() / 2);
var windowvalue = $(window).scrollTop();
if (elem <= windowvalue) {
$("#wannabeSticky").addClass("sticky");
}
else {
$("#wannabeSticky").removeClass("sticky");
}
});
});
I would really appreciate some ideas and hints as this has been bothering me for two days. I would love to keep the Bootstrap grid structure if possible, but feel free to give any suggestions, even those who depict the sidebar as a pure absolute div, as long as the sticky-ness works.
Thanks in advance!
EDIT: I know there is a similar problem already here, but it seems I can't make the JS logic work for my case.
So, having spent another day on it, it seems I reached a decent jQuery version that gets the job done. There is my updated fiddle.
$(document).ready(function(){
var passedMobileNavi = false;
function stickySocialNavi(reference, valueExtracted) {
var refTop = $(reference).offset().top - valueExtracted;
var scrollTop = $(window).scrollTop();
return (refTop <= scrollTop);
}
$(window).scroll(function() {
if (stickySocialNavi($("#refScroller"), $("#refScroller").height())) {
if (!passedMobileNavi) {
passedMobileNavi = true;
$("#wannabeSticky").addClass("sticky");
}
}
else {
passedMobileNavi = false;
$("#wannabeSticky").removeClass("sticky");
}
if (stickySocialNavi($("#end"), $(window).height())) {
var var1 = $(window).scrollTop(),
var2 = $("#end").offset().top,
var3 = $(window).height();
var calculateOffset = (var2 - var3) - var1;
$("#wannabeSticky").css("top", "calc(50% + " + calculateOffset + "px)");
}
else {
$("#wannabeSticky").css("top", "50%");
}
});
});
For the sticky-ness to start, I took the reference point (which is the non-moving element right next to it) and its height. The sticky element gets a fixed position as long as the scrollbar goes past the reference point's center.
As the stick element is centered, it gets additional top offset values when the end of its container is reached. It is still fixed, but its top property's value takes the scroll difference, thus slowly depicting it towards the end of the container.
I don't know if this is the most elegant, straightforward, or easy to implement/understand solution, but it worked for me.
Problem:
I'm trying to fadeIn and fadeOut div class="audioBox" once the user scrolls past the header. What I have seems to work fine, except for when the page is loaded and I'm already past the 835px height of the header/
Q: What I'm seeing is when I scroll the audioBox quickly fades in and then fades out, despite scroll >= header How do I prevent this from happening?
scripts.js
// If the reader scrolls past header, show audioBox
var scroll = $(window).scrollTop();
var header = $("header").height();
if (scroll >= header) {
$(".audioBox").fadeIn();
} else if (scroll <= header) {
$(".audioBox").fadeOut();
}
I tried implementing what you're describing in jsfiddle at http://jsfiddle.net/3wqfp2ch/1/.
I'd approach it a bit differently, based on the following ideas:
I personally prefer letting CSS take care of visual stuff via classes, and let jQuery take the simple responsibility of controlling when the classes should be added/removed. I think it makes for a better relationship between the two systems and allows each to do their thing better & more neatly.
I didn't see where you were listening for scroll events on the window, which is essential for figuring out whether a user's scroll position is before or after the header, so have included this in my code
I don't think we need multiple if conditions - there's just one question: "Is the scroll position greater than the header height?".
Here's the JS:
var headerHeight = $("header").height();
var audioBox = $('#audioBox');
$(window).on('scroll', function(){
var scrollPosition = $(window).scrollTop();
if (scrollPosition > headerHeight) {
audioBox.addClass('is-visible');
} else {
audioBox.removeClass('is-visible');
}
});
Check out my fiddle at http://jsfiddle.net/3wqfp2ch/1/ for the HTML & CSS that this relates to, and the working demo putting it all together.
I can't test whether this suffers from the same issue regarding you loading at a point already past the header height from jsfiddle unfortunately, but I wouldn't be expecting the behaviour you described using the code above.
Let me know how you get on!
Calling .fadeIn() or .fadeOut() all the time and having an overlap in the conditions might be the problem.
Try this:
// If the reader scrolls past header, show audioBox
var scroll = $(window).scrollTop();
var header = $("header").offset().top + $("header").height(); // should include the header's offset as well
if (scroll >= header) {
$(".audioBox:hidden").fadeIn();
} else if (scroll < header) {
$(".audioBox:visible").fadeOut();
}
I'm working on this site (http://styleguide.co/medhelp/) that has 5 sections. For one of the sections (Styles), I've got a sidenav I'm trying to get to stick in the visible frame only as long as users are scrolling in that section.
Here's what I've done thus far - I'm telling the section title & sidenav to stick after the top of the section has begun:
$(window).scroll(function(event) {
var sw = $('.fixed'),
pg = $('.styles'),
diff = pg[0].offsetTop - window.pageYOffset;
if (diff < 80 ) {
$('.fixed').css('position', 'fixed');
$('.fixed').css('top', '160px');
$('.styles').css('position', 'fixed');
$('.styles').css('top', '70px');
}
else {
$('.fixed').css('position', 'relative');
$('.fixed').css('top', '0px');
$('.styles').css('position', 'relative');
$('.styles').css('top', '0px');
}
});
I can't seem to figure out a good way to make the section title "Style" and the sidenav appear/disappear while I scroll to/from that section. Any advice? What could I do better? A simple solution demo in jsfiddle would really help!
Please click on this link & scroll down/up to know what I'm referring to: http://styleguide.co/medhelp/
I'm not going to give you a fiddle, but you need to determine when the next section would stick based on its offset from the top. At the moment what you are doing is:
// if difference top and element < 80 -> fix to top, else position is relative
First of all this means the condition will never be undone. What you need to do in order to continue is:
// once next contact section comes into screen
//(offset from the top of the screen <= screen height), give
var winHeight = $(window).height();
var calcTop = 80 - (winHeight - (winHeight - $('#nextSelector').offset().top);
$('.fixed').css('top', calcTop);
This will give the illusion of your text scrolling up as the new section comes up. I hope this helps. Also, when scrolling back up it doesn't re-stick, but you probably are aware of that.
What would be computationally-efficient ways to select elements touching the top edge of browser window viewport as the page is scrolled?
See attached image. Green elements are selected because they are touching the top edge.
UPDATE
An example of how I'll use this is to fade elements that are going off-screen. There may be hundreds of them on the page. Imagine a page like Pinterest. Computing offset and scrollTop for hundreds of them at the rate of scroll event, even if throttled still feels really inefficient.
This is what I came up with. I think that it could be improved upon by caching the scrollTop values, but this is pretty good. I have included the framework for caching the boxtops, but not the implementation code. I have also only implemented scrolling down to hide divs. I have left reshowing them on upscroll as an exercise for you.
When the window is scrolled we get the last hidden div. We know that everything before this div is already hidden. Then use a 'while next element is off the screen' hide it. As soon as a div isn't off the screen we abort. Thus saving time from iterating through the entire list.
http://jsfiddle.net/kkv3h/2/
//track whether user has scrolled up or down
var prevScrollTop = $(document).scrollTop();
$(document).scroll(function() {
var currentScrollTop = $(this).scrollTop();
if (currentScrollTop > prevScrollTop) {
//down
var lasthiddenbox = $('.fadeboxhidden:last');
var nextbox = (lasthiddenbox.length > 0) ? lasthiddenbox.next('.fadebox') : $('.fadebox:first');
while (nextbox.length) {
console.log('box: ' + nextbox.offset().top + ' scroll: ' + currentScrollTop);
if (nextbox.offset().top < currentScrollTop) {
nextbox.animate({ opacity: 0 }, 3000).addClass('fadeboxhidden');
}
else { return; }
nextbox = nextbox.next('.fadebox:first');
}
} else {
//up
}
prevScrollTop = currentScrollTop ;
});
//create an object to hold a list of box top locations
var boxtops = new Object;
//gather all boxes and store their top location
$('.fadebox').each( function(index) {
//you may want to dynamically generate div ids here based on index. I didn't do this
//because i was already using the id for positioning.
var divid = $(this).prop('id');
boxtops[divid] = $(this).offset().top;
//console.log(boxtops[divid]);
});
I'm thinking the best way would be that you could mark elements you want to determine hit testing with by some class, say "hit-test-visible" or something. Then, for those elements, on the scroll event, you should be able to find their offset compared to the document - see jQuery offset, and then based on the scroll value, if the offset is less than the scroll, and the offset + element height is greater than the scroll offset, then the element should be "touching" the top edge.