I recently stumbled upon a news website with a catchy reading list, and I knew on the spot I needed to get down to work to replicate that effect. You can find a live example of the original concept at the URL below:
http://www.thedailybeast.com/articles/2014/09/21/tea-leoni-isn-t-hillary-clinton-but-madam-secretary-may-be-her-benghazi.html
In case the URL is no longer valid as you're reading this, here is a screenshot of it.
Basically, there are 5 articles per page. As you scroll the page, the reading list updates so that you can see the progress of your reading and how much is left. Note that the whole pagelet (the "Read THIS.list" box) has a fixed position and thus moves with the scrolling of the page.
What do we need?
As usual, I recommend using jQuery (I've become addicted to it!) as it on the one hand highly simplifies development in JS, and on the second hand it will guarantee we can retrieve consistent height/offset values for all browsers at once.
Else than that, not much is actually needed, aside from a working brain :-) I went into this little project with the only picture in mind of what I wanted to achieve in the end, but basically, I just opened Notepad++ and started coding with no preparation. It took me about 1 hour to get the result below:
http://manuthommes.be/readthis/
How is it done?
Since the code is not obfuscated and all in clear JS, you can have a look as much as you want on the actual page, but here are a few hints/points of attention.
1. CSS
- readThis is the ID of the whole ReadThis pagelet. It has a fixed position and will be positionned on the right of the page.
- progBar is a class used to style the default progress bars (as they appear when the page loads and when no scrolling has happened yet)
- filled is a class used to define the style of the progress bars being filled in (when the scrolling happens)
- hdline is a class applying to the labels within the progress bars
- section gives some styling to the articles ...all in all, nothing fancy!
2. Javascript
Quick hook to make sure scrolling the page or clicking on a header does what we want. Might not be fully needed, but several times the script happens to not execute properly, so I just wanted to make myself certain I was in control of everything.
Note that if you want to implement smooth scrolling for local anchors, you anyway have to hook <a> elements to prevent the default behaviour of jumping from A to B instantly.
The above function will be called to round up or down the values retrieved from the scrolling, in case the user jumps too fast. Say for example that your first progress bar is filled in up to 348px out of 350 and the user suddenly move the scroll bar very fast, the actual script will not be able to cater for that and a gap will be visible. What we're doing here is correcting the low and high values to make sure that past a certain value, the progress will be fully filled in (or not at all). You can just comment out the content of the function to see what it does...but don't forget to leave the return value.
And now the core of the script:
// tAS = position of the #a element on the page (article 1)
// tAE = height of the article
// m = end position of the article (start position + height, since all of these are px values)
// A, B, C and D relate to the various article
// quick rules of three : 'nwidth' (length in pixels of the filling of the progress bar) = 300 (length of the progress bar by default) divided by 'h', multiplied by 'sp'
// we then define the width of the "filled" class with the value of 'nwidth'
// in a nutshell: if and only if the scrolling position of the page is somewhere after the beginning of the article and the end of the article, we capture it and apply a simple rule of three: start position of article up to end of the article = 100% of the progress bar (meaning 300px), thus you need to recalculate a proper scaled value for the progress bar.
// ...again, this piece will be repeated for the 4 articles...but surely there's a way a cleaner way to do this in order to place everything in a single loop :-) Else just mind the use of variables and do not get mixed up!
To do
- if the page is too short and there is no extra space after the last article, the last progress bar won't be filled in completely; this happens because a progress bar will be fully filled in only when the top of the screen has passed the offset position of the end of the article -> if the scrolling stops as still 50% of the article is visible, only 50% of the progress will be filled in.
- implement smooth scrolling
- clean the code to have only one function (avoid repeating pieces of code for all articles); basically, you just need to put a proper ID/class to your various HTML elements, then use a for loop to get all variables in an array, and from that point on it's almost easier since you won't get confused with plenty of different variables :-)
That's all, folks!
http://www.thedailybeast.com/articles/2014/09/21/tea-leoni-isn-t-hillary-clinton-but-madam-secretary-may-be-her-benghazi.html
In case the URL is no longer valid as you're reading this, here is a screenshot of it.
Basically, there are 5 articles per page. As you scroll the page, the reading list updates so that you can see the progress of your reading and how much is left. Note that the whole pagelet (the "Read THIS.list" box) has a fixed position and thus moves with the scrolling of the page.
What do we need?
As usual, I recommend using jQuery (I've become addicted to it!) as it on the one hand highly simplifies development in JS, and on the second hand it will guarantee we can retrieve consistent height/offset values for all browsers at once.
Else than that, not much is actually needed, aside from a working brain :-) I went into this little project with the only picture in mind of what I wanted to achieve in the end, but basically, I just opened Notepad++ and started coding with no preparation. It took me about 1 hour to get the result below:
http://manuthommes.be/readthis/
How is it done?
Since the code is not obfuscated and all in clear JS, you can have a look as much as you want on the actual page, but here are a few hints/points of attention.
1. CSS
#readThis{
position:fixed;
width:300x;
height:100px;
right:50px;
}
.progBar{
width:300px;
height:40px;
background:transparent;
margin:10px;
border-top:1px solid #000;
}
.filled{
background:#ccc;
width:0px;
height:40px;
float:left;
}
.hdline{width:350px;}
.section{width:1000px; background:#eee; margin:15px;}
- readThis is the ID of the whole ReadThis pagelet. It has a fixed position and will be positionned on the right of the page.
- progBar is a class used to style the default progress bars (as they appear when the page loads and when no scrolling has happened yet)
- filled is a class used to define the style of the progress bars being filled in (when the scrolling happens)
- hdline is a class applying to the labels within the progress bars
- section gives some styling to the articles ...all in all, nothing fancy!
2. Javascript
jQuery(document).ready(function(){
$(window).scroll(function(){ calcScroll(); });
$(".filled a").click(function(event){
event.preventDefault();
$(window).scrollTop(($("div#"+$(this).attr("data")).offset().top)+1);
});
});
Quick hook to make sure scrolling the page or clicking on a header does what we want. Might not be fully needed, but several times the script happens to not execute properly, so I just wanted to make myself certain I was in control of everything.
Note that if you want to implement smooth scrolling for local anchors, you anyway have to hook <a> elements to prevent the default behaviour of jumping from A to B instantly.
function rndwidth(n)
{ if(n<10)n=0;if(n>290)n=300; return n; }
The above function will be called to round up or down the values retrieved from the scrolling, in case the user jumps too fast. Say for example that your first progress bar is filled in up to 348px out of 350 and the user suddenly move the scroll bar very fast, the actual script will not be able to cater for that and a gap will be visible. What we're doing here is correcting the low and high values to make sure that past a certain value, the progress will be fully filled in (or not at all). You can just comment out the content of the function to see what it does...but don't forget to leave the return value.
And now the core of the script:
function calcScroll()// sp = position of the scrolling bar
{
var tAS=$("#a").offset().top, tAE=$("#a").height(), m=tAS+tAE;
var tBS=$("#b").offset().top, tBE=$("#b").height(), n=tBS+tBE;
var tCS=$("#c").offset().top, tCE=$("#c").height(), o=tCS+tCE;
var tDS=$("#d").offset().top, tDE=$("#d").height(), p=tDS+tDE;
var sp=$(window).scrollTop();
// tAS = position of the #a element on the page (article 1)
// tAE = height of the article
// m = end position of the article (start position + height, since all of these are px values)
// A, B, C and D relate to the various article
if(sp<tDS)$("#title4 .filled").width(0);// when the function is called, we make sure to reset every scrollbar if the scrolling position is smaller than the offet position)
if(sp<tCS)$("#title3 .filled").width(0);
if(sp<tBS)$("#title2 .filled").width(0);
if(sp<tAS)$("#title1 .filled").width(0);
if(sp>=tAS && sp<m){// if the scrolling position is bigger than or equal to the start position (offset) of article 1 AND if the scrolling position is smaller than the end of the article, we redefine 'sp' = scrolling position minus start position (parsed as integer since we're dealing wih pixels), and 'h' = end position of article minus start of article (...I'm realizing now as I'm writing this is basically the height of the article...but whatever!)
var sp=parseInt(sp-tAS), h=m-$("#a").offset().top;
var nwidth=parseInt((300/h)*sp);
$("#title1 .filled").width(rndwidth(nwidth));
}
// quick rules of three : 'nwidth' (length in pixels of the filling of the progress bar) = 300 (length of the progress bar by default) divided by 'h', multiplied by 'sp'
// we then define the width of the "filled" class with the value of 'nwidth'
// in a nutshell: if and only if the scrolling position of the page is somewhere after the beginning of the article and the end of the article, we capture it and apply a simple rule of three: start position of article up to end of the article = 100% of the progress bar (meaning 300px), thus you need to recalculate a proper scaled value for the progress bar.
// ...again, this piece will be repeated for the 4 articles...but surely there's a way a cleaner way to do this in order to place everything in a single loop :-) Else just mind the use of variables and do not get mixed up!
if(sp>=tBS && sp<n){The HTML part is really straight-forward and simply consist in properly nesting the various elements (ReadThis object, progress bars, bars filled in, anchors...).
$("#title1 .filled").width(300);
var sp=parseInt(sp-tBS), h=n-$("#b").offset().top;
var nwidth=parseInt((300/h)*sp);
if(nwidth<10)nwidth=0;if(nwidth>290)nwidth=300;
$("#title2 .filled").width(rndwidth(nwidth));
}
if(sp>=tCS && sp<o){
$("#title2 .filled, #title1 .filled").width(300);
var sp=parseInt(sp-tCS), h=o-$("#c").offset().top;
var nwidth=parseInt((300/h)*sp);
$("#title3 .filled").width(rndwidth(nwidth));
}
if(sp>=tDS && sp<p){
$("#title3 .filled,#title2 .filled,#title1 .filled").width(300);
var sp=parseInt(sp-tDS), h=p-$("#d").offset().top;
var nwidth=parseInt((300/h)*sp);
$("#title4 .filled").width(rndwidth(nwidth));
}
}
To do
- if the page is too short and there is no extra space after the last article, the last progress bar won't be filled in completely; this happens because a progress bar will be fully filled in only when the top of the screen has passed the offset position of the end of the article -> if the scrolling stops as still 50% of the article is visible, only 50% of the progress will be filled in.
- implement smooth scrolling
- clean the code to have only one function (avoid repeating pieces of code for all articles); basically, you just need to put a proper ID/class to your various HTML elements, then use a for loop to get all variables in an array, and from that point on it's almost easier since you won't get confused with plenty of different variables :-)
That's all, folks!
Commentaires
Enregistrer un commentaire