Image highlight (hover state) on active section while scrolling? - javascript

I have these icons above each section on my page (the largish circular icons, please see example: http://pftest.fhero.net) with colored hover states... what I would really love to do is have them change to the active hover states as the user scrolls to each section (preferably with a simple fade transition) - much like the effect of highlighting the active links/section in the navigation.
There are many tutorials, plugins and questions on this site and so forth for highlighting active sections in a navigation however, but doesn't seem to be much that I can find relating to applying the effect to another div or image on the page...
I'm definitely not any kind of jQuery expert but I'm wondering if one of the myriad of scripts/plugins available which are typically used for highlighting active states in navigation could simply be adapted to this scenario somehow to achieve the same effect? Perhaps even the one I am currently using on my page?
Here is the script I'm using for highlighting the active section in the navigation on my page:
/* Scroll Navigation highlight */
$("#work-section1").parent().addClass('active');
var main = main = $('#mainmenu ul');
$('.scroll').click(function(event) {
event.preventDefault();
var full_url = this.href,
parts = full_url.split('#'),
trgt = parts[1],
target_offset = $('#'+trgt).offset(),
target_top = target_offset.top;
$('html, body').animate({scrollTop:target_top}, 500);
/* Remove active class on any li when an anchor is clicked */
main.children().removeClass();
/* Add active class to clicked anchor's parent li */
$(this).parent().addClass('active');
});
$(window).scroll(function(event) {
if($("#work-section").offset().top < $(window).scrollTop() + $(window).outerHeight()){
$("#work-section1").parent().addClass('active');
$("#about-section1").parent().removeClass('active');
$("#footer-section").parent().removeClass('active');
$("#services-section1").parent().removeClass('active');
$("#process-section1").parent().removeClass('active');
}
if($("#about-section").offset().top < $(window).scrollTop() + $(window).outerHeight()) {
$("#about-section1").parent().addClass('active');
$("#work-section1").parent().removeClass('active');
$("#footer-section1").parent().removeClass('active');
$("#services-section1").parent().removeClass('active');
$("#process-section1").parent().removeClass('active');
}
if($("#services-section").offset().top < $(window).scrollTop() + $(window).outerHeight()){
$("#services-section1").parent().addClass('active');
$("#about-section1").parent().removeClass('active');
$("#work-section1").parent().removeClass('active');
$("#footer-section1").parent().removeClass('active');
$("#process-section1").parent().removeClass('active');
}
if($("#process-section").offset().top < $(window).scrollTop() + $(window).outerHeight()){
$("#process-section1").parent().addClass('active');
$("#about-section1").parent().removeClass('active');
$("#work-section1").parent().removeClass('active');
$("#footer-section1").parent().removeClass('active');
$("#services-section1").parent().removeClass('active');
}
if($("#footer-section").offset().top < $(window).scrollTop() + $(window).outerHeight()){
$("#footer-section1").parent().addClass('active');
$("#about-section1").parent().removeClass('active');
$("#work-section1").parent().removeClass('active');
$("#services-section1").parent().removeClass('active');
$("#process-section1").parent().removeClass('active');
}
});
and the HTML:
<nav id="mainmenu" name="mainmenu">
<ul>
<li><a class="scroll" id="work-section1" href="#work-section">Works</a></li>
<li><a class="scroll" id="about-section1" href="#about-section">About</a></li>
<li><a class="scroll" id="services-section1" href="#services-section">Services</a></li>
<li><a class="scroll" id="process-section1" href="#process-section">Process</a></li>
<li><a class="scroll" id="footer-section1" href="#footer-section">Contact</a></li>
</ul>
</nav>
<section id="about-section" data-anchor-offset="90">
<section id="work-section" data-anchor-offset="90">
...ect...
Could this somehow be adapted to accomplish the effect I am looking for? Or any other/better methods, or plugins I should be looking at?
I should add that the icons use the sprites method which could make the CSS side of things a little trickier, although I would be willing to change them to non-sprite images if necessary...

You could use a small little function for this, that checks if a element is on screen. I set up a little JSFiddle for you: http://jsfiddle.net/LHrkB/1/
Code:
function isElementVisible(elementToBeChecked)
{
var TopView = $(window).scrollTop();
var BotView = TopView + $(window).height();
var TopElement = $(elementToBeChecked).offset().top;
var BotElement = TopElement + $(elementToBeChecked).height();
return ((BotElement <= BotView) && (TopElement >= TopView));
}
$(window).scroll(function () {
isOnView = isElementVisible(".inview");
if(isOnView){
//What to do when element is visible
$(".inview").css({"background":"#ccc"});
}else{ // If not visible
}
});
Ok, so i have changed the JSFiddle a bit, now it uses a fadeIn on a invisible element when it comes into view: http://jsfiddle.net/LHrkB/2/
Ok, i changed the JSFiddle once again. When you scroll in the results pane, and you play around with it a bit you can see the element change class as it comes on screen and also when it goes away again. I commented the JS so you can see what it does and where it does it. http://jsfiddle.net/LHrkB/4/

Thanks to the help of Veritas87 (who is super awesome), managed to get it all working with the following code:
function isElementVisible(elementToBeChecked)
{
var TopView = $(window).scrollTop();
var BotView = TopView + $(window).height();
var TopElement = $(elementToBeChecked).offset().top;
var BotElement = TopElement + $(elementToBeChecked).height();
return ((BotElement <= BotView) && (TopElement >= TopView));
}
$(window).scroll(function () {
isOnView = isElementVisible(".about-icon");
if(isOnView){
//What to do when element is visible
$('.about-icon').addClass('about-icon-active');
}else{ // If not visible
$('.about-icon').removeClass('about-icon-active');
}
isOnView = isElementVisible(".works-icon");
if(isOnView){
//What to do when element is visible
$('.works-icon').addClass('works-icon-active');
}else{ // If not visible
$('.works-icon').removeClass('works-icon-active');
}
isOnView = isElementVisible(".services-icon");
if(isOnView){
//What to do when element is visible
$('.services-icon').addClass('services-icon-active');
}else{ // If not visible
$('.services-icon').removeClass('services-icon-active');
}
isOnView = isElementVisible(".process-icon");
if(isOnView){
//What to do when element is visible
$('.process-icon').addClass('process-icon-active');
}else{ // If not visible
$('.process-icon').removeClass('process-icon-active');
}
});
with the "...icon-active" classes of course containing the style for the icon hover states.

Related

How to keep class active when scrolling with javascript?

I need help figuring out what is going on with my javascript. I have some code that is supposed to make the nav links have an active class when you are on that part of the page, but it's only sort of working for a couple links and it also flickers as you scroll rather than staying active the whole time you're on that part of the page. See the JSFiddle for an example https://jsfiddle.net/7szpuqsr/ -- if you scroll slowly you can see how "home" becomes active for a moment. I am trying to get each link to have the active class when you click it and also while you are on that entire part of the page.
I also have a javascript sticky nav bar and smooth scrolling working so I don't know if possibly any of that is getting in the way? Thanks in advance for help.
Here's the Javascript I'm trying to use for the active class:
var sections = $('section')
, nav = $('nav')
, nav_height = nav.outerHeight();
$(window).on('scroll', function () {
var cur_pos = $(this).scrollTop();
sections.each(function() {
var top = $(this).offset().top - nav_height,
bottom = top + $(this).outerHeight();
if (cur_pos >= top && cur_pos <= bottom) {
nav.find('a').removeClass('active');
sections.removeClass('active');
$(this).addClass('active');
nav.find('a[href="#'+$(this).attr('id')+'"]').addClass('active');
}
});
});
nav.find('a').on('click', function () {
var $el = $(this)
, id = $el.attr('href');
$('html, body').animate({
scrollTop: $(id).offset().top - nav_height
}, 500);
return false;
});
Something like this? I couldn't reproduce the flickering visually on my machine but I can see the class being removed/added constantly on scroll
https://jsfiddle.net/7szpuqsr/1/
Main changes, I added a class to your sections, you have too many sections but with the way your code is meant to work, it's much easier to add a class to the sections, example below
<section id="home" class="section">
var sections = $('.section') to get the section class
updated this part of the js to check for active class
if (cur_pos >= top && cur_pos <= bottom) {
if(!$(this).hasClass("active")) {
nav.find('a').removeClass('active');
sections.removeClass('active');
$(this).addClass('active');
nav.find('a[href="#'+$(this).attr('id')+'"]').addClass('active');
}
}
You can also cache the $(this) into a variable inside of the section loop like
var $this = $(this);
then just use $this for the rest of the loop
here is the doc for hasClass https://api.jquery.com/hasclass/

Problems with scrollTop animation

I have three divs, each one with some hidden content. When you click on a div, its content is being displayed by sliding down. And at the same time, I'm using scrollTop to make the browser scroll to the top of the block thats been clicked on. The HTML looks like this:
<div class="blocks block1"></div>
<div class="content block1_content"></div>
<div class="blocks block2"></div>
<div class="content block2_content"></div>
<div class="blocks block3"></div>
<div class="content block3_content"></div>
However, im having problems with parts of the scrollTop animation. Here is the JS:
$('.blocks').on("click", function() {
if (!$(this).hasClass('expanded')) {
collapseExpandedFunction();
$('html, body').animate({
scrollTop: $(this).offset().top
}, 500);
$(this).addClass('expanded');
$(this).next().slideDown();
} else if ($(this).hasClass('expanded')) {
collapseExpandedFunction();
}
});
collapseExpandedFunction = function() {
$('.blocks.expanded').removeClass('expanded');
$('.content').slideUp();
};
I made an jsfiddle to easier demonstrate the problem: https://jsfiddle.net/ss53ckyk/3/
Explaination:
If you start toggle the green block and then toggle the red or blue, it's all good. The greens content is being hidden, while the red/blue is displayed and scrolled to the top of the block.
The problem is if you start from the top and moving down. If you first toggle the red one and then either blue or green, the browser won't scroll down correctly.
Another thing i'd like is to make the slideDown happen after the scrollTop animation is done.
Hopefully someone can help me out!
EDIT:
There should only be one blocks content visible at a time. For example, if you click the red once and then the blue, the content of the red should slide up, while the blue is showing.
The asynchronous nature of javascript is causing your issues; namely .slidUp() function. When this event is triggered, it fires an asynchronous event, which is non-blocking to the rest of the function. Therefore, the animate top will take a snapshot of the DOM at a particular moment in time while the .slidUp() function is actioning. Replacing the .slidUp() and .slidDown() with .show() and .hide() to resolve this, but this doesn't provide the responsiveness you require. One thought would be to capture the offsets of each previous content div and use that in the scrollTop function.
EDIT :
Based on the edit, you need to make few adjustments in your code which calculates the scrolltop position based on scrollposition and adding margin based on the corresponding .content div
$(document).ready(function() {
var addMargin = false;
$('.blocks').on("click", function() {
if (!$(this).hasClass('expanded')) {
collapseExpandedFunction();
var doc = document.documentElement;
var ele = this;
var nextEle = $(ele).next();
$(this).addClass('expanded');
var margin = 0;
var scrollTo = 0;
if($(ele).hasClass('expanded'))
{
if(addMargin)
margin = $(nextEle).css('height').replace('px','');
scrollTo = $(ele).offset().top - margin;
}
if(doc.scrollTop != 0 && doc.scrollTop > scrollTo && addMargin)
{
console.log(margin);
scrollTo = scrollTo + 200;
}
$('html, body').animate({
scrollTop: scrollTo
}, 500, function()
{
$(ele).next().slideDown();
});
} else if ($(this).hasClass('expanded')) {
collapseExpandedFunction();
}
});
collapseExpandedFunction = function()
{
$('.blocks.expanded').removeClass('expanded');
$('.content').slideUp();
addMargin = false;
};
window.onscroll = function (e) {
addMargin = true;
}
});
Here's the working code : https://jsfiddle.net/ss53ckyk/13/

highlighting current section on scroll - gap between sections

I have a page with different sections that can be scrolled to or accessed by clicking the corresponding link in the navbar. A demo of the page is on codepen http://codepen.io/meek/pen/NNprYb?editors=1010
I am using the following code to keep track of which section the user is scrolling over and highlighting it in the navbar
$(window).on("scroll", function() {
var currentPos = $(window).scrollTop();
$('.nav li a').each(function() {
var sectionLink = $(this);
var section = $(sectionLink.attr('href'));
if(section.position().top <= currentPos && section.position().top + section.height() > currentPos) {
$('.nav li').removeClass('active');
sectionLink.parent().addClass('active');
}
else {
sectionLink.parent().removeClass('active');
}
});
});
It works fine, but there is a gap where scrolling through between home and about sections, none of the links are highlighted. I have a feeling this might be because of the space the navbar occupies between home and about, but I'm not sure how to overcome this. Ideally, I'd like to have that gap also highlight "about".
I've tried to add this to the above code:
else if($('#nav-wrapper').position().top <= currentPos && $('#nav-wrapper').position().top + $('#nav-wrapper').height() > currentPos) {
$('.nav li').removeClass('active');
$('#temp').addClass('active');
}
but it doesn't work. Help appreciated
Solution:
Adjust your if statement in your scroll event to match the following.
if(section.position().top <= currentPos && sectionLink.offset().top + section.height() > currentPos)
The key being, the second instance of section.position().top changes to sectionLink.offset().top

Update scroll position on resize of window

I'm currently using a combination of smooth scroll and IDs/anchor tags to scroll to content on my site. The code below is getting the ID of the next 'section' in the DOM, and adding it's ID as the 'view next section' href, so once it's clicked, it'll scroll to the top of that div. Then, it iterates through, updating the href with the next ID each time etc until the last section is seen and it scrolls back to the top. Pretty straightforward.
The only problem is that the 'sections' are fullscreen images, so as it's scrolling to the top of the next section, if you resize the browser, the top position of that section (where we scrolled to) has moved, and means the position is lost.
I've created a JSFiddle. You can see this happening after you click the arrow to visit the next section then resize the window: http://jsfiddle.net/WFQ9t/3/
I'm wanting to keep this top position fixed at all times so even if you resize the browser, the scroll position is updated to reflect this.
Thanks in advance,
R
var firstSectionID = $('body .each-section').eq(1).attr('id');
$('.next-section').attr('href', '#' + firstSectionID);
var i = 1;
$('.next-section').click(function() {
var nextSectionID = $('body .each-section').eq(i).attr('id');
i++;
$('.next-section').attr('href', '#' + nextSectionID);
var numberOfSections = $('body .each-section').length;
var lastSectionID = $('body .each-section').eq(numberOfSections).attr('id');
if ($('.next-section').attr('href') == '#' + lastSectionID ) {
$('.next-section').attr('href', '#introduction');
i = 1;
}
});
Ok, Please check out this fiddle: http://jsfiddle.net/WFQ9t/9/
The few things I did were:
Made some global variables to handle the screen number (which screen you're on and also the initial window height. You will use this when the screen loads and also when you click on the .next-session arrow.
var initWinHeight = $(window).height();
var numSection = 0;
Then I tossed those variables into your resizeContent() function
resizeContent(initWinHeight, numSection)
so that it will work on load and resize
I made the body move around where it needs to, to accomodate for the movement of the divs (I still don't understand what divs are moving when the regular animation happens).
$('body').css({
top: (((windowHeight - initWinHeight)*numSection)*-1) + "px"
});
Then in your click function, I add 1 to the section number, reset the initial window height and then also reset the body to top:0. The normal animation you have already puts the next section at the top of the page.
numSection++;
initWinHeight = $(window).height();
$('body').css({top:"0px"}, 1000);
Finally, I reset the numSections counter when you reach the last page (You might have to make this 0 instead of 1)
numSection = 0;
The fiddle has all of this in the correct places, these are just the steps I took to change the code.
Here is a solution that i found, but I dont use anchor links at this point, i use classes
Here is my HTML code:
<section class="section">
Section 1
</section>
<section class="section">
Section 2
</section>
<section class="section">
Section 3
</section>
<section class="section">
Section 4
</section>
And here is my jQuery/Javascript code,
I actually used a preety simple way:
$('.section').first().addClass('active');
/* handle the mousewheel event together with
DOMMouseScroll to work on cross browser */
$(document).on('mousewheel DOMMouseScroll', function (e) {
e.preventDefault();//prevent the default mousewheel scrolling
var active = $('.section.active');
//get the delta to determine the mousewheel scrol UP and DOWN
var delta = e.originalEvent.detail < 0 || e.originalEvent.wheelDelta > 0 ? 1 : -1;
//if the delta value is negative, the user is scrolling down
if (delta < 0) {
next = active.next();
//check if the next section exist and animate the anchoring
if (next.hasClass('section')) {
var timer = setTimeout(function () {
$('body, html').animate({
scrollTop: next.offset().top
}, 800);
next.addClass('active')
.siblings().removeClass('active');
clearTimeout(timer);
}, 200);
}
} else {
prev = active.prev();
if (prev.length) {
var timer = setTimeout(function () {
$('body, html').animate({
scrollTop: prev.offset().top
}, 800);
prev.addClass('active')
.siblings().removeClass('active');
clearTimeout(timer);
}, 200);
}
}
});
/*THE SIMPLE SOLUTION*/
$(window).resize(function(){
var active = $('.section.active')
$('body, html').animate({
scrollTop: active.offset().top
}, 10);
});

localscroll active link url recognition

Via localscroll and scrollto i built a horizontal sliding page (Environment) also adds hashes to the url. example: when clicking a link in the nav from localScroll it adds the hash in the url (index.html#media).
Now via Localscroll there is no active link support and i wanted to add that and found some solutions here: Stackoverflow
i was happy but then i tried to reload the page and when the user is, for example in the media section, there is no support for recognition of the url and it doesnt add the selected class to the nav link that is currently selected in the url.
so i did more research and found that site: Website
great but it didnt work for my horizontal setup.
i barely started my knowledge about javascript and i even don't know how to jquery at all so i modified the script with my logic and went so far:
i adds the class, even when i scroll via the mousewheel it nicely add the class to all nav links. BUT: it doesn't remove them.
<script style="text/javascript">
$(function(){
var sections = {},
_width = $(window).width(),
i = 0;
// Grab positions of our sections
$('.section').each(function(){
sections[this.name] = $(this).offset().left;
});
$(document).scroll(function(){
var $this = $(this),
pos = $this.scrollLeft();
for(i in sections){
if(sections[i] > pos && sections[i] < pos + _width){
$('#nav ul li a').removeClass('selected');
$('#nav_' + i).addClass('selected');
}
}
});
});
$(".scroll").click(function(event){
event.preventDefault();
var full_url = this.href;
var parts = full_url.split("#");
var trgt = parts[1];
var target_offset = $("#"+trgt).offset();
var target_top = target_offset.left;
$('html, body').animate({scrollLeft:target_top}, 800);
});
</script>
How to update the links so it removes the selected class?
Edit: i think i'm close
i think it needs changes in the for header
if(sections[i] > pos && sections[i] < pos + _width)
What are the needed changes to assign the class "selected" to the current viewed slide

Categories