Change Active Menu Item on Page Scroll? - javascript

I want to activate the menu item when I get to it's corresponding section. I got inspired by this previous SO question: Change Active Menu Item on Page Scroll? .
but the difference is that in my menu I have a little image over each menu item, that shows only if I hover the menu item, and hides when don't.
HTML
<nav>
<ul id="pics">
<li id="text-what"><img src="images/what.png" id="pic-what" class="vishid"><p>item1</p></li>
<li id="text-training"><img src="images/training.png" id="pic-training" class="vishid"><p>item2</p></li>
<li id="text-testi"><img src="images/trait.png" id="pic-testi" class="vishid"><p>item3</p></li>
<li id="text-contact"><img src="images/contact.gif" id="pic-contact" class="vishid"><p>item4</p></li>
</ul>
</nav>
CSS
.vishid{
visibility: hidden;
}
.visvis{
visibility:visible;
}
JAVASCRIPT (to show and hide images when hovering items)
$(document).ready(function(){
$("#text-what").hover(function(){
$("#pic-what").addClass('visvis');
},function(){
$("#pic-what").removeClass('visvis');
});
$("#text-training").hover(function(){
$("#pic-training").addClass('visvis');
},function(){
$("#pic-training").removeClass('visvis');
});
$("#text-testi").hover(function(){
$("#pic-testi").addClass('visvis');
},function(){
$("#pic-testi").removeClass('visvis');
});
$("#text-contact").hover(function(){
$("#pic-contact").addClass('visvis');
},function(){
$("#pic-contact").removeClass('visvis');
});
});
I want to show the image when I am at it's corresponding section. How can I do that with javascript?

There is a lot going on here. Your HTML should technically be corrected. href's should not encapsulte LI's. Instead your href should be set to block - width and height 100% - within the LI. Let's also move the class of .vishid to the parent LI. That way if you want it to effect anything else - besides just the images - in the future, it would be easy to add. So that would look like:
<nav>
<ul id="pics">
<li id="text-what" class="vishid"><img src="images/what.png" id="pic-what"><p>item1</p></li>
<li id="text-training" class="vishid"><img src="images/training.png" id="pic-training"><p>item2</p></li>
<li id="text-testi" class="vishid"><img src="images/trait.png" id="pic-testi"><p>item3</p></li>
<li id="text-contact" class="vishid"><img src="images/contact.gif" id="pic-contact"><p>item4</p></li><
</ul>
</nav>
Then you need to adjust your CSS to correct for the "non-block" level href.
#pics li a {
display: block;
width: 100%;
height: 100%;
}
.vishid img {
visibility: hidden;
}
.visvis img {
visibility: visible;
}
Finally, I am going to assume that you are using "articles" in your HTML for the sections. Doesn't have to be, but that is what my example will assume.
var clickScroll = false,
triggerHighlight = 80; // distance from the top to trigger action
$(window).scroll(function () {
var y = $(this).scrollTop(),
yCatch = y + triggerHighlight;
// Let's wrap in a variable check. Set this to tru is clicking on navigation
// false if simply scrolling
if (!clickScroll) {
$('article').each(function (i) {
var whichArticle = $(this).attr('id');
if ($(this).position().top < yCatch) {
var currentArticle = "#" + whichArticle;
adjustSubNav(currentArticle);
}
});
}
});
function adjustSubNav(l) {
$('#pics a').each(function (i) {
if ($(this).attr('href') == l) { // Add active class to the corresponding menu item
$(this).parent('li').removeClass('vishid').addClass('visvis');
} else {
$(this).parent('li').removeClass('visvis').addClass('vishid');
}
});
}

Related

How do I animate moving a list item to the top?

I have a list and would like to move the item to the top when it's clicked on. Simultaneously, all other items should move down to make space.
That's what I have so far:
<ul id="list">
<li id="one">item-1</li>
<li id="two">item-2</li>
<li id="three">item-3</li>
<li id="four">item-4</li>
</ul>
//
$('li').on('click', function () {
$(this).css({ position : 'absolute', top : $(this).position().top });
var height = $(this).parent().children().first().height();
var list = $(this).parent();
$(this).animate({top: '0px'}, { duration: 500, queue: false });
list.children().each(function( index, element) {
var $liElement = $(element);
if($liElement != $(this))
{
$liElement.animate({top: height + 'px'}, { duration: 500, queue: false });
}
});
});
Here is the link: http://jsfiddle.net/5qgnjvdp/
I see the item moving on top but all others don't move at all. What is wrong here?
Should I use prepend() to insert the list item on top when the animation is finished?
While not really an "animation", I was working on something some time ago along similar lines. What I ended up using was a slideUp, then a slideDown to give a sense of animation without actual animation connected to the move.
$('li').click(function() {
$(this).slideToggle(500, function() {
$(this).prependTo('#list').slideToggle(500); });
});
li { list-style: none; background: #ddd;padding: 5px 10px; border: 1px solid #aaa; margin: 10px; text-transform: uppercase; font-weight: bold;color:#fff; }
#list { margin:20px; padding:0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<ul id="list" data-role="listview">
<li id="one">item-1</li>
<li id="two">item-2</li>
<li id="three">item-3</li>
<li id="four">item-4</li>
</ul>
$('li').click(function() {
$(this).slideToggle(500, function() {
$(this).prependTo('#list').slideToggle(500); });
});
fiddle update
Again, I realize this isn't really an animation beyond the slide toggles, but it conveys a sense of interactivity similar to an animation.
There are a couple of problems with your script.
When comparing $liElement != $(this) you are in the scope of the each function where this refers to the current list child, which is the same as element. Store the clicked li in a variable outside of the looping function and compare with that. Also no need to compare the $(...) objects. element != clickedLi would suffice.
When you animate the other li elements they don't have position: absolute (or relative) so you won't see their movement.
If you assign the other li elements an absolute position you move all of them to the same position, the second row in the list. You could use position relative to move them down. You should just make sure to exclude the elements that come after the original position of the clicked li.
After the animation is complete you should insert the list item as the first child of the list and then remove the absolute/relative positioning, including the top style to make sure it works a second time (and to ensure that the visible representation is in line with the elements' structure.

Adding a class to a link inside a <li> element on scrolling

Is it possible to add a class to a link inside a li element when a certain part of the page is active?
I have a one page website and would like to change the color of the link when that specific part of the page is reached via scroll.
Here's my HTML:
<header id="header">
<section class="container">
<nav>
<a class="logo" href="index.html">Logo</a>
<div id="menu">
<ul id="links">
<li>Services</li>
<li>About</li>
<li>Clients</li>
<li class="last">Contact</li>
</ul>
</div>
</nav>
</section>
</header>
And here's the CSS:
#menu li a {
color:#7a7a7a;
text-decoration: none;
font-size: 12px;
margin-right:20px;
}
#menu li.last a {
color:#7a7a7a;
text-decoration: none;
font-size: 12px;
margin-right:0px;
}
#menu li.current a {
color: #0086be;
}
What I would like to do is to add the class .current to the link inside the li element whenever that specific part of the page is reached.
I believe this is only possible with Javascript, can anyone point me the right path to achieve this?
Thanks in advance
I think you want something like scrollspy in bootstrap,
you can use it or you can find https://gist.github.com/pascaldevink/2380129 bypascaldevink
or here is the fiddle http://jsfiddle.net/ia_archiver/Kb7xq/
You will require jquery for this,
$.fn.scrollspy = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('scrollspy')
, options = typeof option == 'object' && option
if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.scrollspy.Constructor = ScrollSpy
$.fn.scrollspy.defaults = {
offset: 10
}
$(function () {
$('[data-spy="scroll"]').each(function () {
var $spy = $(this)
$spy.scrollspy($spy.data())
})
})
}(window.jQuery);
Using hover function you can achieve this.i.e. on hover of specific part of the page you add the class to the link present inside the li. e.g.
$('#specificPartOfPageId').hover(function(){
$('#links').children().children('a').addClass('current');
});
This would add .current class to every link present inside that UL element.
Hope this helps.
If I have understood correctly, I guess this is what you require: jsFiddle. The CSS and the HTML code remains the same and this is the jQuery code which I've used:
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll > 500) {
$("#links li:first-child").addClass("current");
}
if (scroll > 750) {
$("#links li:first-child").removeClass("current");
$("#links li:nth-child(2)").addClass("current");
}
var scrollBottom = $(window).scrollTop() + $(window).height();
if (scroll < 500) {
$("#links li:first-child").removeClass("current");
}
if (scroll < 750) {
$("#links li:nth-child(2)").removeClass("current");
}
});
Basically what happens is that when you scroll down to 500px, the li:first-child is automatically assigned the current class. You can modify the jQuery to suit your needs by adding more if queries as per your needs. You can target different <li>'s in your list using different child-selectors like li:first-child, li:nth-child(2) etc.

Variable menu length

Brand new to jquery. I'm trying to make a drop-down animated menu that opens up just enough to show submenus while dropping the menu items as well. For example:
Item1
Item2
Item3
And when you mouse over Item2 you get
Item1
Item2
Subitem1
Subitem2
Item3
I know my code needs a lot more work (I'm playing/experimenting now) and my problem is when trying to use a variable the menu doesn't open up at all so the problem is somewhere with the height: variable line.
$(document).ready(function(){
//When mouse rolls over
$("li").mouseover(function(){
$(this).stop().animate({height: $('ul > li').length * 20})
});
//When mouse is removed
$("li").mouseout(function(){
$(this).stop().animate({height:'18px'})
});
});
I'd actually prefer to make separate variable lines such as
$(document).ready(function(){
var numitems = $('ul > li').length;
var totalheight = numitems * 20;
//When mouse rolls over
$("li").mouseover(function(){
$(this).stop().animate({height: totalheight})
});
//When mouse is removed
$("li").mouseout(function(){
$(this).stop().animate({height:'18px'})
});
});
try this:
$(document).ready(function(){
var numitems = $('ul > li').length;
var totalheight = (numitems * 20)+'px';
//When mouse rolls over
$("li").mouseover(function(){
$(this).stop().animate({height: totalheight},500); //don't forget to give the animation some duration
});
//When mouse is removed
$("li").mouseout(function(){
$(this).stop().animate({height:'18px'},500);
});
});
Forget about trying to store the height of the elements and multiplying up. You will run into serious problems if you do this in the future.
You can always get the height of an element with jQuery's outerHeight() property. But if you want this to happen on the mouse enter/out, you would be tidier with the .hover() function of jQuery and do the following :
var speed = 500;
$("li.top").hover(function () {
var ul = $(this).find("ul");
$(ul).stop(); // stop any current animation
// tidy up attributes that have been added
$(ul).removeClass("flattened");
$(ul).css("height", ""); // very necessary;
$(ul).css("display", ""); // very necessary;
$(ul).removeClass("show");
var _height = $(ul).outerHeight();
$(ul).addClass("flattened");
$(ul).addClass("show");
$(ul).show();
$(ul).animate({ height: _height }, speed , function () {
$(ul).removeClass("flattened");
});
}, function () {
var ul = $(this).find("ul");
$(ul).stop();
$(ul).animate({ height: "0px" }, speed , function () {
$(ul).hide();
$(ul).css("height", ""); // very necessary;
$(ul).css("display", ""); // very necessary;
$(ul).removeClass("show");
});
});
I used the following CSS to make things work in my test file:
ul li a { float: left; width: 100%; }
ul li > ul { display: none;float:left; }
ul li { float: left; width: 100%; border: solid 1px black; min-height: 0; }
.flattened { height: 0px; overflow:hidden; }
And the following HTML:
<nav>
<ul>
<li class="top">Item 1</li>
<li class="top">Item 2
<ul>
<li>SubItem 1</li>
<li>SubItem 2</li>
</ul>
</li>
<li class="top">Item 3</li>
</ul>
</nav>
Hope this works for you.

jQuery - mouseOver change div

I have a menu that on each item's mouseover event an image loaded into a second div should change, and onMouseout it should replace with the original content.
At this point I am saving the original content into a var and creating a onMouseover & onMouseOut event each for each menu item.
Jquery:
$(document).ready(function(){
var heroSwap= $('#swapspace').html();
$('#menu1').mouseover(function(){
$('#swapspace').html('<img src="img/1.JPG"></img>');
});
$('#menu1').mouseout(function(){
$('#swapspace').html(heroSwap);
});
.... ..*ETC There are 7 More of these identical except for id*.. ....
});
Question:
Is there any way to create a generic function that can process a parameter from the menu tag?
Would it be easier.. instead of swapping the innerHTML img tags to create hidden divs that are displayed/hidden by the onMouseover/onMouseout events?
Full Sample:
Sample Site
You can create a generic function easily. You can add on the #menu1 tag an attribute with the url of your image and a class name like that :
<li class='menu' data-src='img/1.JPG'></li>
And then create the function like that :
$('.menu').mouseover(function(){
$('#swapspace').html('<img src="'+$(this).data('src')+'"></img>');
});
$('.menu').mouseout(function(){
$('#swapspace').html(heroSwap);
});
As for your second question, i would simply swap the src of you img instead of changing the entire HTML.
$('#swapspace').find('img').attr('src', $(this).data('src')); //on hover
$('#swapspace').find('img').attr('src', ''); //on out
It is better that you store the image src in the data-src attribute. So that you need not use multiple variables for each image.
Then you would not need to change the html, just changing the src attribute would do the trick.
Firstly replace your li to use class instead of id's
HTML
<ul class="nav nav-stacked nav-pills">
<li class="menu" data-src="img/1.JPG"> Web Development </li>
<li class="menu" data-src="img/2.JPG"> Software Development</li>
<li class="menu" data-src="img/3.JPG"> System Support</li>
<li class="menu" data-src="img/4.JPG"> SEO</li>
<li class="menu" data-src="img/5.JPG"> Social Media Marketing </li>
<li class="menu" data-src="img/6.JPG"> Project Management</li>
</ul>
Javascript
$(document).ready(function () {
var $swapImg = $('img', '#swapspace'),
defaultImage = 'default.jpg';
$('.menu').on({
mouseover: function () {
$swapImg.attr('src', $(this).data('src'));
},
mouseout: function () {
$swapImg.attr('src', defaultImage);
}
})
});
This way you would have only one event handler for all the li's , instead of a separate handler for each menu item.
It's actually better to use css for this. It's easier too.
The background can be anything in css, url, color etc.
<div id="menu1" class="menu-item"><div class="img"></div></div>
<div id="menu2" class="menu-item"><div class="img"></div></div>
cont'd.
Simple js hover uses mouseenter and mouseleave http://api.jquery.com/hover/
$(function() {
$(".menu-item").hover(function() {
$(this).addClass("hover");
}, function() {
$(this).removeClass("hover");
});
});
Then markup your images with css:
.menu-item {
margin: 0 0 5px 0;
}
.img {
display: block;
width: 200px;
height: 30px;
background: #ddd;
}
.menu-item.hover .img {
background: #000;
}
#menu2.hover .img {
background: url("http://placehold.it/200x30") no-repeat;
}
#menu4.hover .img {
background: url("http://placehold.it/200x30") no-repeat;
}
You can preload the images as well by using sprite sheets instead of javascript loading images in the background.
http://jsfiddle.net/E6xtq/1/

Changing li class on horizontal scroll

I want to know if it's possible to change my "selected" li class based on the movements of the horizontal scroll. So when you start moving the scroll to the right, the selected li class would change to 2, 3, 4 .. and so on.
Any hints or points to start from are appreciated.
My code:
<div id="scroller">
<ul id="ulscroller">
<li value="1" class="selected">1</li>
<li value="2">2</li>
<li value="3">3</li>
<li value="4">4</li>
<li value="5">5</li>
<li value="6">6</li>
<li value="7">7</li>
<li value="8">8</li>
<li value="9">9</li>
<li value="10">10</li>
<li value="11">11</li>
<li value="12">12</li>
<li value="13">13</li>
<li value="14">14</li>
<li value="15">15</li>
<li value="16">16</li>
<li value="17">17</li>
<li value="18">18</li>
<li value="19">19</li>
<li value="20">20</li>
</ul>
</div>
CSS:
li {float:left; padding:10px; cursor: pointer;}
div#scroller {width:300px; height:70px; overflow-x: scroll;}
ul#ulscroller {list-style: none outside none; margin: 0;
padding: 0; width:655px;}
ul#ulscroller li.selected {border:1px solid #000;}
Fiddle: http://jsfiddle.net/7uCS8/
Basic idea:
var scroller = document.getElementById("scroller"),
lis = scroller.getElementsByTagName("li"),
divisionsWidth = lis[0].offsetWidth - 2,
current = 0,
scroller.onscroll = function(){
var selected = Math.floor(scroller.scrollLeft/divisionsWidth);
if (current!==selected) {
lis[current].className="";
lis[selected].className="selected";
current = selected;
}
};
It can be tweaked to change the selection, but it should be a good starting point.
I'm guessing that maybe, since the li's have a defined width you can get the current scroll in pixels with each scrolling event (easily done with jQuery) and if the scroll position is within any position to change the class name, change it. I'd rather use an id value for each li element to identify it easily with JavaScript and a class for group removing the class. Something like this:
$(document).scroll(function() {
var scrollPosition = $(document).scrollLeft();
if (scrollPosition >= 0 && scrollPosition < 300) {
$("li.scroller_children").removeClass("selected");
$("li#1").addClass("selected");
} elseif (scrollPosition >= 300 && scrollPosition < 900) {
$("li.scroller_children").removeClass("selected");
$("li#2").addClass("selected");
} elseif ....
});
I
Using jQuery you could do something like this.
$(window).scroll(function (scrolledTo) {
$("li:[value=" + scrolledTo).addClass("selected");
$("li.selected").removeClass("selected");
});
Unfortunately I do not know how to get where the window was scrolled to (scrolledTo) from the event and it doesn't appear to be in the jQuery documentation. However, you said you wanted a starting point, so I thought this would at least provide you with that.
You can try this, need some modifications...
$("#scroller").scroll(function () {
$(".selected").removeClass("selected").next().addClass("selected");
});

Categories

Resources