jQuery's scroll function not working accurately - javascript

I wasn't sure what to make of the title for this problem, as it is quite complicated. To demonstrate, I've created a fiddle:
http://jsfiddle.net/2W5Jd/
Basically, I'm making a website containing different sections with different background colors. The designer thought it would be a good idea to change the color of the logo when you scroll down to another section, as if the section is "masking" the logo (see fiddle, it's hard to explain).
The problem however, as you can see in the fiddle, is that when you scroll fast enough, the logo stops resizing. Does anyone have any idea how to work around this?
As posting a link to jsfiddle.net must be accompanied by code, here's the js (I've simplified it a bit from the actual website version):
var $logo = $("#logo");
$(window).scroll(function(){
scrollTop = $(document).scrollTop();
$logo.css("top", scrollTop + "px");
if ( scrollTop + 180 >= 600 ) {
$logo.find(".second").css("height", (scrollTop + 180) - 600 + "px");
}
});

add an else clause to your if
else {
$logo.find(".second").css("height", (scrollTop + 180) - 600 + "px");
}
see update fiddle: http://jsfiddle.net/hbrunar/2W5Jd/1/
if you don't need to do anything else in the if part, then skip it completely:
http://jsfiddle.net/hbrunar/2W5Jd/2/

Related

What's the most accurate way of measuring scroll?

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

Transform on Scroll - as smooth as on medium.com

i'm pretty sure a few of you guys know the website medium.com articles page.
For example, here: Click
There is a very "easy" effect on this page when you scroll down – opacity and transform changes. Not a big deal at first sight. I've implemented the same effect probably more then 10 times on websites already.
But if you take a closer look at it, you can see how absolutely perfect smooth this is. Seems like the text is perfectly gliding above the surface. (Im checking it on the latest Chrome)
I was keen if this is just imagination, and quickly built up the same thing to check this. So i came up with this code:
var windowTop;
var limit = 420;
function parallax(){
parallaxElem.css({
"opacity": (1 - (windowTop / limit)),
"-webkit-transform":"translate3d(0," + (100 * (windowTop / limit)) + "px,0)",
"-ms-transform":"translate3d(0," + (100 * (windowTop / limit)) + "px,0)",
"transform":"translate3d(0," + (100 * (windowTop / limit)) + "px,0)"
});
}
$(window).on('scroll', function(){
windowTop = $(window).scrollTop();
parallax();
});
And it's by far not as smooth as the code on the Medium website.
Anyone any idea, what they are doing to get this super smooth scroll transform effect? I just can't find it out – their code is too complex/too compressed for me, to get any information out of it.
Thank's a lot for any answer in advance!
Regards
Mark
The biggest improvement is to go from this:
$(window).on('scroll', function(){
windowTop = $(window).scrollTop();
parallax();
});
to:
$(window).on('scroll', function(){
window.requestAnimationFrame(parallax);
});
Store windowTop inside of the parallax function. There's no point in making that an out of scope variable.
Additionally, although you don't have it in your sample code "parallaxElem" should be an out of scope variable, because you don't want to do a DOM search for the element on every scroll.

Freeze element (position:fixed) for specific scroll range

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.

bootstrap tooltip\popover - solving inconsistent placement to the left

I've been working on a project and I've noticed some inconsistency in bootstrap's behavior that I would like to solve.
When a popover (or tooltip, whatever, they're basically the same) is nearing the edge of the screen - if it's a right-sided one, when nearing the edge - it will contract so as not to go offscreen (it only works up to a point, but that's usually enough).
This doesn't happen when the placement is to the left.
i.e.:
right placement:
Normal width:
Close to the edge:
left placement:
Normal width:
close to the edge:
These images are from a small DEMO I wrote to illustrate the problem.
I've messed around with the source code, so far to no avail. I can't seem to place my finger on what exactly causes this behavior in the first place.
Any ideas?
p.s.
I'm using Bootstrap 3.1.1. The new 3.2 does not solve the issue (and I would like to avoid upgrading at this point).
Major Update!
After some digging, I figured out that this has nothing to do with bootstrap - it's simple css - it seems that when you position an element absolutely and push it to the sides it will try and stay withing the screen.
I never knew about this behavior, and it happens automatically - but only to the the direction you're pushing - i.e. a div pushed from the left will contract when reaching the right edge of the screen and vice versa.
It just so happens that popovers are only positioned with the left assignment - which is why we're seeing the inconsistend behavior - when it's pushed to the right it contracts but not the other direction.
So the solution is to assign right instead - sounds simple?
Not so much. I took the source code and manipulated it a bit, adding these lines (somewhere arond line 250 in the jsfiddle):
if (placement == 'left' && offset.left < 0) {
var right = ( $(window).width() + 10 ) - ( offset.left + actualWidth );
offset.left = 0;
$tip.offset(offset);
$tip.css({ 'right': right });
}
Seems reasonable, right? If the offset to the left is less than 0 (i.e., it goes offscreen) then calculate the window width and remove from that the left offset and the width of the popover (actualWidth) itself and you get the distance from the right.
Then, make sure to reset the offset left and apply the right positioning. But... it only sorta works - which is to say it only works the second time around.
Check it out for yourself! Hover once, and it's misplaced, pull the mouse to the side and try again and suddenly it's positioned correctly. What the hell?
edit
Ok this seems to come up a lot, so I'll make it clear:
I know about auto placement. I don't want it. I want to control where the popover goes, letting it decide automatically is not a solution to the problem, it's merely avoiding it
Ok, I've gotten a little closer.
Once you assign the right in css, the actualWidth and actualHeight will change, so you need to update those variables. Around line 253 in your jsfiddle:
if (placement == 'left' && offset.left < 0) {
var right = ( $(window).width() + 10) - ( offset.left + actualWidth );
offset.left = 0;
$tip.offset(offset);
$tip.css({ 'right': right });
actualWidth = $tip[0].offsetWidth;
actualHeight = $tip[0].offsetHeight;
}
This works the first time you hover, but every time after that, it sets the top to be too high, so you can't read the top of the tooltip.
UPDATE:
It appears that having the right value set is messing up the positioning in the applyPlacement function. To fix this, clear the right value before setting the initial actualWidth and actualHeight (around line 225):
$tip.css({ 'right': '' });
// check to see if placing tip in new offset caused the tip to resize itself
var actualWidth = $tip[0].offsetWidth
var actualHeight = $tip[0].offsetHeight
I believe this has a lot to do with the browser/client that accesses the webpage. For instance, in order to display the tip's on the proper side (not bunched up or illegible off the the left or right), determine the offsetLeft & offsetTop of the object element with javascript and place it accordingly. You could have different pages for different resolutions.
CSS example for a screen width from 400-720 pixels:
#media screen and (min-width:400px) and (max-width:721px)
some pseudo code:
if (this.offsetLeft < 200) //if there's not enough room to display the tip
tip.offsetLeft += 200;
I think you've basically got it, it's working fine for me.
Just add in the minimum width detection so that it doesn't go too small.
if (/bottom|top/.test(placement)) {
var delta = 0
if (offset.left < 0) {
delta = offset.left * -2
offset.left = 0
$tip.offset(offset)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
}
this.replaceArrow(delta - width + actualWidth, actualWidth, 'left');
} else {
if (placement == 'left' && offset.left < 0) {
var right = ( $(window).width() + 10) - ( offset.left + actualWidth );
offset.left = 0;
$tip.offset(offset);
$tip.css({ 'right': right });
}
this.replaceArrow(actualHeight - height, actualHeight, 'top');
}

Javascript height sums don't match

I have a div, absolutely positioned, originally non-visible that is shown at the position of an element being clicked rendering its preview (top position of the preview is lined to the top of the element clicked).
When the element being clicked is positioned low, the preview is render somewhat below the original page border, and scrolling is necessary. I want to move the preview upward to have its bottom edge on the previous page bottom limit. The problem is the code I use doesn't return what is expected for the page height (it is greater than the sum of the preview height and the clicked-element top position).
Here's the code:
file 1:
jQuery('elementClicked').live('click',function(){
...
jQuery("previewDiv").setTopAtClickedElement(jQuery(this));
...
}
file 2:
jQuery.fn.setTopAtClickedElement = function (element) {
//original positioning
this.css('top', element.offset().top + 'px');
// the troublesome part where the eventual correction should be done
if (element.offset().top + this.height() > jQuery(document).height())
{
this.css('top', jQuery(document).height() - this.height() + 'px');
}
}
Similar happens when I use
Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight)
for the measure of the document height as suggested on a link
Do you have any suggestions on how I should implement this troublesome part of the code?
Please tell if I wasn't clear enough,
Thank you,
Instead of .height() Try using jQuery's outer.height() - api docs, which will take into account any padding (and optionally marign) you have on the page.
A jsfiddle or codepen will help us all out in solving your problem.

Categories