nick
nick

Reputation: 309

Implementing a "current-position" indicator on a page using HTML and JavaScript

So, I want to add something like this to my webpage:

nav

So, I have an idea about how to get it done. Here are a few questions:

Would this "indicator" be implemented by just using a div to house the circles that indicate where you are at? If so, how can I make this div always be at the same spot and always visible on the page, even when scrolling?

To indicate you are at a current position, will I have to set ranges of pixels for each "circle indicator" and get the current-position using JavaScript?

Lastly, is there a way to highlight/fill-in circles using CSS or JavaScript? Or will I need to use images of empty/filled circles?

Upvotes: 3

Views: 3079

Answers (2)

Woodrow Barlow
Woodrow Barlow

Reputation: 9127

The Sidebar

How can I make this div always be at the same spot and always visible on the page, even when scrolling?

By setting the CSS property position:fixed. Here is the specification for that value, according to the Mozilla Developers' Network:

fixed
Do not leave space for the element. Instead, position it at a specified position relative to the screen's viewport and don't move it when scrolled. When printing, position it at that fixed position on every page.


The Circles

Is there a way to highlight/fill-in circles using CSS or JavaScript? Or will I need to use images of empty/filled circles?

This can be done with pure CSS. Just create a div for each circle and give it a border, to start with. That border is square, but by defining the border-radius, we can round out the corners. Round them out enough, and it becomes a circle. To "fill it in", just change the background color to match the border.


The Scroll Math

To indicate you are at a current position, will I have to set ranges of pixels for each "circle indicator" and get the current-position using JavaScript?

Yes. I prefer to work with jQuery rather than raw Javascript, and I did this by overriding the $(window).scroll function (which triggers any time the page is scrolled) to calculate first the total height of my page, then my position, and, from that, my position as a percentage.


The Final Product

To answer this question, I referred to the work done in a public pen on code.io by user 'ellenbrook'. The original pen detected the scroll percentage and did a show/hide menubar at a certain point. Instead, I'll be filling in circles on a sidebar.

First step was to create the sidebar, with fillable circles. The html is simple, one div for the sidebar, five divs for the circles.

The jQuery is the part I modified from the above work. It detects a scroll event, calculates how far down you've scrolled (as percentage), then toggles the "filled" class for the appropriate circles.

This example uses five designated points on the page, corresponding to five circles. They are defined by percentage scrolled. In this example, each circle represents 20% of the total page. All of these factors can be modified.

try it out on jsfiddle

$(window).scroll(function(){
    
    // calculate how far we've scrolled down, by percentage
    var scrollTo = $(window).scrollTop(),
        docHeight = $(document).height(),
        windowHeight = $(window).height();
    scrollPercent = (scrollTo / (docHeight-windowHeight)) * 100;
    scrollPercent = scrollPercent.toFixed(1);

    $('#c1').removeClass('filled');
    $('#c2').removeClass('filled');
    $('#c3').removeClass('filled');
    $('#c4').removeClass('filled');
    $('#c5').removeClass('filled');
    
    if(scrollPercent < 20) {
        $('#c1').addClass('filled');
    } else if(scrollPercent < 40) {
        $('#c2').addClass('filled');
    } else if(scrollPercent < 60) {
        $('#c3').addClass('filled');
    } else if(scrollPercent < 80) {
        $('#c4').addClass('filled');
    } else {
        $('#c5').addClass('filled');
    }
    
}).trigger('scroll');
body {
    /* this is just to force the scrollbar to show up */
    /* it can be removed on a page that has actual content */
    height: 10000px;
}

#position {
    right: 0;
    top: 50%;
    margin-top: -75px;
    height: 150px;
    width: 25px;
    background-color: #ccc;
    position: fixed;
}

#position .circle {
    width: 20px;
    height: 20px;
    margin-left: 1px;
    margin-top: 2.5px;
    margin-bottom: 8px;
    background-color: transparent;
    border: 1px solid #fff;
    border-radius: 10px;
}

#position .filled {
    background-color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="position">
    <div id="c1" class="circle filled"> </div>
    <div id="c2" class="circle"> </div>
    <div id="c3" class="circle"> </div>
    <div id="c4" class="circle"> </div>
    <div id="c5" class="circle"> </div>
</div>

Upvotes: 2

CaptainBli
CaptainBli

Reputation: 4201

You have tagged this as jQuery. So I will answer this with that in mind.

I have done it textually but you should be able to change the images out quite simply.

		var navLinks = ["Top", "Releases", "Reviews", "Battle", "Rankings"];
		var idLinks = ["Top", "Releases", "Reviews", "Battle", "Rankings"];

		function scrollup() {
			$("html, body").animate({ scrollTop: 0 }, "slow");
		}
		function checkscroll() {
			var scroll_top = $(document).scrollTop();
			//use the value from scroll_top to determine if we are near the rightNav links and then we can highlight the rightNav links.
			var rellink = $('a[name="Releases"]');
			var revlink = $('a[name="Reviews"]');
			var batlink = $('a[name="Battle"]');
			var ranlink = $('a[name="Rankings"]');
			//alert(batlink.position().top);
			if (scroll_top > ($('#Rankings').position().top - 280)) {
				$("#TopLink").removeClass("current");
				$("#RelLink").removeClass("current");
				$("#RevLink").removeClass("current");
				$("#BatLink").removeClass("current");
				$("#RanLink").addClass("current");
			} else if (scroll_top > ($('#Battle').position().top - 155)) {
				$("#TopLink").removeClass("current");
				$("#RelLink").removeClass("current");
				$("#RevLink").removeClass("current");
				$("#BatLink").addClass("current");
				$("#RanLink").removeClass("current");
			} else if (scroll_top > ($('#Reviews').position().top - 155)) {
				$("#TopLink").removeClass("current");
				$("#RelLink").removeClass("current");
				$("#RevLink").addClass("current");
				$("#BatLink").removeClass("current");
				$("#RanLink").removeClass("current");
			} else if (scroll_top > (($('#Releases').position().top - 155))) {
				$("#TopLink").removeClass("current");
				$("#RelLink").addClass("current");
				$("#RevLink").removeClass("current");
				$("#BatLink").removeClass("current");
				$("#RanLink").removeClass("current");
			} else {
				$("#TopLink").addClass("current");
				$("#RelLink").removeClass("current");
				$("#RevLink").removeClass("current");
				$("#BatLink").removeClass("current");
				$("#RanLink").removeClass("current");
			}

			if (scroll_top <= 100) {
				$('#fixedback').fadeOut(500);
			} else {
				$('#fixedback').fadeIn(500);
			}
		}
		$("a[href='#previous']").click(function () {
			//get the next item.
			var item = $("li[class='current']").attr("loc");
			if (item <= 1) {
				$("html, body").animate({ scrollTop: 0 }, "slow");
			} else {
				var which1 = idLinks[item - 1]; //navLinks[item];
				$("html, body").animate({ scrollTop: ($('#' + which1 + '').position().top - 150) }, "slow");
			}
			return false;
		});
		$("a[href='#next']").click(function () {
			//get the next item.
			var item = parseInt($("li[class='current']").attr("loc"));
			if (item >= 3) {
				$("html, body").animate({ scrollTop: ($('#Rankings').position().top - 150) }, "slow", function () {
					$("#TopLink").removeClass("current");
					$("#RelLink").removeClass("current");
					$("#RevLink").removeClass("current");
					$("#BatLink").removeClass("current");
					$("#RanLink").addClass("current");
				});
			} else {
				item = 1 + item;
				var which1 = idLinks[item]; //navLinks[item];
				//alert(item + " - " + which1 + "-" + navLinks[0]);
				$("html, body").animate({ scrollTop: ($('#' + which1 + '').position().top - 150) }, "slow");
				//$("html, body").animate({ scrollTop: ($('a[name="' + which1 + '"]').position().top) }, "slow");
				checkscroll();
			}
			return false;
		});

		$("a[href='#Top']").click(function () {
			$("html, body").animate({ scrollTop: 0 }, "slow");
			checkscroll();
			return false;
		});
		$("a[href='#Releases']").click(function () {
			$("html, body").animate({ scrollTop: ($('#Releases').position().top - 150) }, "slow");
			checkscroll();
			return false;
		});
		$("a[href='#Reviews']").click(function () {
			//alert($('a[name="Reviews"]').position().top);
			$("html, body").animate({ scrollTop: ($('#Reviews').position().top - 148) }, "slow");
			checkscroll();
			return false;
		});
		$("a[href='#Battle']").click(function () {
			$("html, body").animate({ scrollTop: ($('#Battle').position().top - 150) }, "slow");
			checkscroll();
			return false;
		});
		$("a[href='#Rankings']").click(function () {
			$("html, body").animate({ scrollTop: ($('#Rankings').position().top - 150) }, "slow", function () {
				$("#TopLink").removeClass("current");
				$("#RelLink").removeClass("current");
				$("#RevLink").removeClass("current");
				$("#BatLink").removeClass("current");
				$("#RanLink").addClass("current");
			});
			return false;
		});

		$(window).scroll(function () {
			checkscroll();
		});
		$(document).scroll(function () {
			checkscroll();
		});
#rightNav {
  color: #fff;
  font-family: Calibri, Arial;
  font-size: 14px;
  letter-spacing: 0;
  padding: 6px 0;
  position: fixed;
  margin: 0;
  background-image: url('/images/FrontPage/rightNav-bg.png');
  background-color: #000080;
  background-repeat: repeat-y;
  width: 80px;
  height: 168px;
  z-index: 8;
  top: 173px;
  right: 0
}
#rightNav a {
  color: #fff;
  text-decoration: none
}
#rightNav li {
  line-height: 20px;
  margin-left: -23px;
  color: #fff
}
#rightNav ul li.current {
  line-height: 20px;
  margin-left: -43px;
  color: #fff;
  padding: 0 0 0 20px;
  background-image: url('/images/FrontPage/rightNavSel.png');
  background-color: #0000bb;
  font-weight: bold;
  border-bottom-width: 1px;
  border-bottom-color: #b3281a;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="rightNav" style="position: fixed; width: 80px; height: 168px; z-index: 8; top: 173px; right: 0;">
		<ul style="list-style: none outside none; color: #FFFFFF">
			<li class="first">
				<a href="#previous" id="prevSection">^
				</a>
			</li>
			<li id="TopLink" class="current" loc="0"><a href="#Top" style="color: #ffffff;">Top</a>
			</li>
			<li id="RelLink" loc="1"><a href="#Releases" style="color: #ffffff;">Releases</a>
			</li>
			<li id="RevLink" loc="2"><a href="#Reviews" style="color: #ffffff;">Reviews</a>
			</li>
			<li id="BatLink" loc="3"><a href="#Battle" style="color: #ffffff;">Battle!</a>
			</li>
			<li id="RanLink" loc="4"><a href="#Rankings" style="color: #ffffff;">Rankings</a>
			</li>
			<li class="last">
				<a href="#next" id="nextSection">v
				</a>
			</li>
		</ul>
	</div>
<div style="font-family: Arial">
		<a id="Top" name="Top">Top</a><br />
		
		Lots of content
		<p>
			<br />
			<br />
			&nbsp;</p>
		<p>
			<br />
			<br />
			&nbsp;</p>
		<p>
			<br />
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<a id="Releases" name="Releases">Rel</a>
		<br>
		Other content
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<a id="Reviews" name="Reviews">Rev</a><p />
		<br>
		More Content
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<a id="Battle" name="Battle">Bat</a><p />
		<br>
		other stuff
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<a id="Rankings" name="Rankings">Rank</a><p />
		<br>
		final stuff
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
		<p>
			<br />
			&nbsp;</p>
  </div>

Upvotes: 2

Related Questions