jQuery: Using scroll() and offset() to change background - javascript

I am designing a website where the background sits in a div that has a negative z-index with position:fixed. I then have section divs that scroll over it. My goal is to change the background image when each section's top position is passed by the scrollTop function. My jQuery code currently creates an array of each sections top position using:
var secTops = [];
$('section').each(function(i) {
var t = $(this).offset();
secTops.push(t.top);
});
I then thought I would create a variable upon scroll() that was the scrollTop() position like so:
$(window).scroll(function() {
var winTop = $(this).scrollTop();
});
But here is where I am stuck. The best I can come up with (which doesn't work right) is this:
for (i = 0; i < $('section').length; i++) {
var pos = secTops[i];
if (winTop < pos) {
$('#background').css('background', bgFront + (i+1) + bgBack);
} else {
$('#background').css('background', bgFront + (i+2) + bgBack);
};
};
But this isn't right. You can disregard the second half of my .css() function. I've created variables and labeled my images appropriately, so i know that works. Right now, the for loop runs through the entire iteration and is stuck at the full section.length and thus only flips between 2 background images. I need this to constantly check my winTop variable against the top positions of my sections and change the background accordingly. I could do this with a lot of if/then, or maybe even a lengthy switch, but there has to be a cleaner way to do this. Can anyone help me out here?
Here's a JSFiddle that uses colors instead of images but shows the same problems. http://jsfiddle.net/kyleshevlin/5N5WU/1/

this has no chance to work. you need to change it to something like this (this is kinda pseudocode, just to give you a picture:
sections = [];
$(document).ready(function() {
$('section').each(function() {
sections.push($(this))
});
})
$(window).scroll(function() {
var s = $(window).scrolTop();
var currentIndex;
for ( var i = 0; i < sections.length; i++) {
if (( s > sections[i].offset().top) && ( s <= sections[i+1].offset().top)) {
currentIndex = i;
}
}
$('#background').css('background', bgFront + (i+1) + bgBack);
})

Related

Menu fixed on single page - change color

i want to create a menu for a single page website with link to div from page.
The menu look like this:
<li>Home</li>
...
<div id="home-link"></div>
I want to change color of link from menu when i am in area of home-link div. How is possible to make that thing?
Thanks for answers and for your help.
Have a nice day.
You will need JavaScript for this if the link element is not a child of #home-link.
Something like this:
$('#home-link').on('hover', function () {
$('li a').css('color', '#bada55');
});
This assumes you are using jQuery, but similar approach with other frameworks would work as well.
If I assume from the question that if you are hovering on #home-link and color of anchor should change, then
$('#home-link').hover(function () {
$('li a').css('color', 'red');
});
or if I assume that if the id is present in your page and you want to change the color of the anchor then
if($('#home-link').length){
$('li a').css('color', 'red');
}
I found myself with (what I believe is) the same question: how can I change which navigation link is highlighted to reflect the area I've scrolled to, whether I get there by using the link or just by scrolling down the page?
Here's a page with a very helpful tutorial.
The theory:
We create an array of all our nav a href’s. We then use some calculations using the scroll function. We find the section id, calculate it’s height, see if it’s greater or less than the value from the window top, and if the window falls in between, we add a class nav-active to the list item in question. We create a conditional also, because if the top of a section is not reached and the page can’t scroll anymore, we want to highlight this section.
And the relevant jQuery code:
/**
* This part handles the highlighting functionality.
* We use the scroll functionality again, some array creation and
* manipulation, class adding and class removing, and conditional testing
*/
var aChildren = $("nav li").children(); // find the a children of the list items
var aArray = []; // create the empty aArray
for (var i=0; i < aChildren.length; i++) {
var aChild = aChildren[i];
var ahref = $(aChild).attr('href');
aArray.push(ahref);
} // this for loop fills the aArray with attribute href values
$(window).scroll(function(){
var windowPos = $(window).scrollTop(); // get the offset of the window from the top of page
var windowHeight = $(window).height(); // get the height of the window
var docHeight = $(document).height();
for (var i=0; i < aArray.length; i++) {
var theID = aArray[i];
var divPos = $(theID).offset().top; // get the offset of the div from the top of page
var divHeight = $(theID).height(); // get the height of the div in question
if (windowPos >= divPos && windowPos < (divPos + divHeight)) {
$("a[href='" + theID + "']").addClass("nav-active");
} else {
$("a[href='" + theID + "']").removeClass("nav-active");
}
}
if(windowPos + windowHeight == docHeight) {
if (!$("nav li:last-child a").hasClass("nav-active")) {
var navActiveCurrent = $(".nav-active").attr("href");
$("a[href='" + navActiveCurrent + "']").removeClass("nav-active");
$("nav li:last-child a").addClass("nav-active");
}
}
});

Change margin top of div based on window scroll

I have a bar at the top of my page that is position fixed. When the user scrolls to a certain point I want the bar to start moving up as if it was relatively or absolutely positioned.
Right now the css of the bar changes from fixed to absolutely positioned but of course this sets the div straight to the top of the page.
I have been looking at this for ages and cannot get my head around how I would push the bar up one pixel at a time for every pixel scrolled past the _triggerOffset
Can anyone enlighten me?
function banner(){
var _barOffset = $('#top-bar').outerHeight(),
_navOffset = $('#navigation').offset().top,
_triggerOffset = _navOffset-_barOffset;
$(window).scroll(function() {
var _scroll = $(window).scrollTop();
if (_scroll >= _triggerOffset) {
$('#top-bar').css({'position':'absolute'});
}
});
}
banner();
I have done a fiddle.
Check this fiddle
Working Demo
$(document).ready(function() {
var postionToTriggerMove = 500;
var positioninitial = $(window).scrollTop();
var positioninitialLine = $(".line").offset().top;
$(window).scroll(function() {
var _scroll = $(window).scrollTop();
if(_scroll > positioninitial) {
if(_scroll >= (postionToTriggerMove - 5) && _scroll <= (postionToTriggerMove + 5) )
{
var topBarPostion = $(".line").offset().top;
$('.line').css({'position':'absolute',"top":topBarPostion});
}
}
else {
if(_scroll >= (postionToTriggerMove - 5) && _scroll <= (postionToTriggerMove + 5) )
{
var topBarPostion = $(".line").offset().top;
$('.line').css({'position':'fixed',"top":positioninitialLine});
}
}
positioninitial = _scroll;
});
});
You could try something like the below:
function banner(){
var _barOffset = $('#top-bar').outerHeight(),
_navOffset = $('#navigation').offset().top,
_triggerOffset = _navOffset-_barOffset;
$(window).scroll(function() {
var _scroll = $(window).scrollTop();
if (_scroll >= _triggerOffset) {
$('#top-bar').css({'position':'absolute','top':_triggerOffset - (_scroll-_triggerOffset)});
}
});
}
banner();
This code is highly untested, however what we are doing is initially setting the element to an absolute position and defining the top of this element as the _triggerOffset, then we take the difference between the current scroll and the triggerOffset and subtract this from the top position to make the bar move up the more you scroll down.
Not sure if that's what you had in mind, but I'd look at a solution like this. You might want to add some conditions in there to ensure that top never goes below 0 or the nav will go off the screen.
Thanks, had a play around with both examples and they worked pretty good.
In the end I tweaked my code and instead of making the bar position top 0px I made it position top with the pixels equal to the offset distance. Don't know why I didn't think of this before.
On another note I am using Shinov's code for anoher project as I quite like it :)
Thanks
function banner(){
var _barOffset = $('#top-bar').outerHeight(),
_navOffset = $('#navigation').offset().top,
_triggerOffset = _navOffset-_barOffset;
$(window).scroll(function() {
var _scroll = $(window).scrollTop();
if (_scroll >= _triggerOffset) {
$('#top-bar').css({'position':'absolute', 'top':_triggerOffset+'px'});
}else if (_scroll <= _triggerOffset){
$('#top-bar').css({'position':'fixed', 'top':'0px'});
}
});
}

Getting Coordinates of an element on page scroll

I am having this problem where i have a set of 6 UL's having a common class x.Each of them consist of a specific section of the page.Now i have 6 menus that are related to each of the section.What i have to do is highlight the menu when its related section is in users view.
For this i thought that may be jQuery position(); or offset(); could have helped but they give the top and left of the element.I also tried using jQuery viewport plugin but apparently view port is big it can show more than one UL at a time hence i cant apply element specific logic here.I am not familliar to this but does anything changes of an element on scrolling?If yes then how to access it?
Please share your views.
Regards
Himanshu Sharma.
Is very easy to do it using jQuery and a dummy fixed HTML block that helps you find the current position of the viewport.
$(window).on("scroll load",function(){
var once = true;
$(".title").each(function(ele, index){
if($(this).offset().top > $("#viewport_helper").offset().top && once){
var index = $(this).index(".title");
$(".current").removeClass('current')
$("#menu li").eq(index).addClass('current')
once = false;
}
});
})
Check out a working example: http://jsfiddle.net/6c8Az/1/
You could also do something similar with the jQuery plugin, together with the :first selector:
$(window).on("scroll load",function(){
$(".title:in-viewport:first").each(function(){
var index = $(this).index(".title");
$(".current").removeClass('current')
$("#menu li").eq(index).addClass('current')
});
})
You can get the viewport's width and height via $(document).width() and $(document).height()
You can get how many pixels user scrolls via $(document).scrollTop() and $(document).scrollLeft
Combining 1 and 2, you can calculate where the viewport rectangle is
You can get the rectangle of an element using $(element).offset(), $(element).width() and $(element).height()
So the only thing left to you is to determine whether the viewport's rectangle contains (or interacts) the elements's rectangle
So the whole code may look like:
/**
* Check wether outer contains inner
* You can change this logic to matches what you need
*/
function rectContains(outer, inner) {
return outer.top <= inner.top &&
outer.bottom >= inner.bottom &&
outer.left <= inner.left &&
outer.right >= inner.right;
}
/**
* Use this function to find the menu related to <ul> element
*/
function findRelatedMenu(element) {
return $('#menu-' + element.attr('id'));
}
function whenScroll() {
var doc = $(document);
var elem = $(element);
var viewportRect = {
top: doc.scrollTop(),
left: doc.scrollLeft(),
width: doc.width(),
height: doc.height()
};
viewportRect.bottom = viewportRect.top + viewportRect.height;
viewportRect.right = viewportRect.left + viewportRect.width;
var elements = $('ul.your-class');
for (var i = 0; i < elements.length; i++) {
var elem = $(elements[i]);
var elementRect = {
top: elem.offset().top,
left: elem.offset().left,
width: elem.width(),
height: elem.height()
};
elementRect.bottom = elementRect.top + elementRect.height;
elementRect.right = elementRect.left + elementRect.width;
if (rectContains(viewportRect, elementRect)) {
findRelatedMenu(elem).addClass('highlight');
}
}
}
$(window).on('scroll', whenScroll);
Let's see if i understood well. You have a page long enough to scroll, and there is an element that when it appears in the viewport, you wanna do something with it. So the only event that's is triggered for sure on the time the element gets in the viewport is the 'scroll'. So if the element is on the page and the scroll is on the viewport, what you need to do is bind an action to the scroll event to check if the element is in the view each time the event is trigger. Pretty much like this:
$(window).scroll(function() {
check_element_position();
});
Now, in order for you to know if the element is in the viewport, you need 3 things. The offset top of that element, the size of the viewport and the scroll top of the window. Should pretty much look like this:
function check_element_position() {
var win = $(window);
var window_height = win.height();
var element = $(your_element);
var elem_offset_top = element.offset().top;
var elem_height = element.height();
var win_scroll = win.scrollTop();
var pseudo_offset = (elem_offset_top - win_scroll);
if (pseudo_offset < window_height && pseudo_offset >= 0) {
// element in view
}
else {
// elem not in view
}
}
Here, (elem_offset_top - win_scroll) represent the element position if there was no scroll. Like this, you just have to check if the element offset top is higher then the window viewport to see if it's in view or not.
Finally, you could be more precise on you calculations by adding the element height (variable already in there) because the code i just did will fire the event even if the element is visible by only 1 pixels.
Note: I just did that in five minutes so you might have to fix some of this, but this gives you a pretty darn good idea of what's going on ;)
Feel free to comment and ask questions

Find DOM elements at top and bottom of scrolling div with jQuery

I have a scrolling div containing list items. I have this boilerplate scroll event defined
$("#scrollingDiv").scroll(function(e) {
});
Inside of this scroll event function, how can I figure out which elements are at the top and bottom of the currently visible area?
You could try computing the positions of the list items with respect to the scrolling <div> and then scan the positions to see which ones match up with the scrollTop of the <div>.
Something like this perhaps:
var base = $('#scrollingDiv').offset().top;
var offs = [ ];
$('li').each(function() {
var $this = $(this);
offs.push({
offset: $this.offset().top - base,
height: $this.height()
});
});
$("#scrollingDiv").scroll(function() {
var y = this.scrollTop;
for(var i = 0; i < offs.length; ++i) {
if(y < offs[i].offset
|| y > offs[i].offset + offs[i].height)
continue;
// Entry i is at the top so do things to it.
return;
}
});
Live version (open your console please): http://jsfiddle.net/ambiguous/yHH7C/
You'd probably want to play with the fuzziness of the if to get something that works sensibly (1px visible hardly makes an element the top one) but the basic idea should be clear enough. Mixing in the height of #scrollingDiv will let you see which <li> is at the bottom.
If you have a lot of list items, then a linear search might not be what you want but you should be able to solve that without too much effort.

How to change the left attribute on page resize (jQuery)

I'm having slight troubles with my code. What I'm trying to do is make these element's css property 'left' update according to the difference of it's current left value, and the amount the page resizes. This way, when the page resizes and the background moves over, the elements will move too. Take a look at the code below and I'll describe the issue:
$(window).resize(function() {
var docWidth = $(window).width();
if (docWidth < 1000) {
var difference = 1000-docWidth;
$('#headNav a,#icons div').each(function() {
var left = $(this).position().left;
var newLeft = left - difference;
$(this).css({ 'left' : newLeft });
});
}
});
So the issue that I'm getting is the elements are being given left values of wild numbers, while the value of the variable 'newLeft' is the reasonable, desired value. The each function I think is collecting the sums of these values and running them for each element x amount of times that the elements found exist (so if there's 5 elements it runs 5 times, I mean.) What I want is this code to execute uniquely for each element, but just once each, not each element 10 times! (that's how many elements are in the html).
So my question is, how can this be achieved? I hope I explained myself well enough, this was tough to iterate. Any help is extremely appreciated. Thank you!
Here's a fun trick: Include += in your .css() call:
$(this).css({left: "+=" + difference});
jQuery does the math for you to get the new value.
Try this:
$(window).resize(function() {
var docWidth = $(window).width();
if (docWidth < 1000) {
var difference = 1000-docWidth;
$('#headNav a,#icons div').each(function(iconInst) {
var left = $("#" + iconInst).position().left;
var newLeft = left - difference;
$("#" + iconInst).css({ 'left' : newLeft });
});
}
});

Categories

Resources