On this site this site, I have some code which you can see below which figures out the offset for each post from the top of the document and assigns a colour from each post to the header when it scrolls into view. I am having to wait for the window load event until the code works which really is not very elegant. Can anyone see how I can improve this code?
$(window).load(function() {
var $header = $("header");
var numberOfSections = $("section").length;
var sectionOffsets = [];
for(var i = 0; i < numberOfSections; i++) {
sectionOffsets.push($("section").eq(i).offset().top);
}
$(window).scroll(function () {
$("section").each(function () {
if ($(window).scrollTop() > $(this).offset().top - 180) {
$("header").css('color', $(this).data("colour"));
}
});
}).scroll();
});
1 Answer 1
Instead of storing section
positions on window onload
time, which can be always kind of inaccurate since your images might still load...
get dynamically the current position / size data of an element using:
element.getBoundingClientRect()
using that JS method you don't even need to calculate the $(window).scrollTop()
cause the returned value is the element's position respective to the client
top (viewport top edge).
To retrieve only the one element that matches a top criteria you can use the jQuery's .filter()
method and return the element which...
$section.filter(function(){
var r = this.getBoundingClientRect();
return r.top + r.height - 100 > 0; // ...matches this
})
(100
is the header height in this demo; set as desired or calculate dynamically)
Now, since jQuery filtered more than one element that matches that criteria of (gbcr.top + gbcr.height - headeroffser) > 0
by going directly to chain another method to it like .data()
, the value will be respective to the first of elements returned in the .filter()
collection:
$header.css({
color : $section.filter(function(){
var r = this.getBoundingClientRect();
return r.top + r.height - 100 > 0;
}).data().colour // is the colour data of the first of filtered elements
});
Store that colouring stuff inside a setScrollColors
function and use like:
$(function() { // DOM ready shorthand
var $header = $("header"); // Cache selectors
var $section = $("section");
function setScrollColors() {
$header.css({
color : $section.filter(function(){
var r = this.getBoundingClientRect();
return r.top + r.height - 100 > 0;
}).data().colour
});
}
setScrollColors(); // Call inside dom ready
$(window).on("load scroll", setScrollColors); // call also on load and scroll
});
*{margin:0;}
header{
position:fixed;
z-index:1;
top:0;
width:100%;
background:#f9f9f9;
height:100px;
border-bottom:1px solid currentColor;
}
#content{
margin-top:100px;
}
section{
min-height: 1000px;
min-height: calc(100vh - 100px);
border-top:1px solid #000;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<header><h1>HEADER</h1></header>
<div id="content">
<section data-colour="black">black</section>
<section data-colour="red">red</section>
<section data-colour="blue">blue</section>
<section data-colour="green">green</section>
<section data-colour="fuchsia">fuchsia</section>
<section data-colour="orange">orange</section>
</div>
-
1\$\begingroup\$ This seems to be a more elegant solution than my approach. I have never used "getBoundingClientRect" before. It seems very useful. \$\endgroup\$2ne– 2ne2015年05月25日 15:26:40 +00:00Commented May 25, 2015 at 15:26
Explore related questions
See similar questions with these tags.
$header
,numberOfSections
, andsectionOffsets
? \$\endgroup\$load
event for you? \$\endgroup\$