Reputation: 83
The idea is that once the page is scrolled to a certain point, the background image will change to fixed and moved partially off screen (think twitter on mobile).
The current JavaScript is:
<script>
window.addEventListener('scroll', function ()
{
document.body.classList
[
window.scrollY <= 750 ? 'add': 'remove'
]
('notscrolled');
});
window.addEventListener('scroll', function ()
{
document.body.classList
[
window.scrollY >= 750 ? 'add': 'remove'
]
('scrolled');
});
</script>
This implementation is obviously firing every time the page is scrolled, making the page very laggy and unbearable.
Is there any way I could improve this to remove lag and speed up the page?
Upvotes: 0
Views: 6108
Reputation: 896
Absolutely! What you are describing is a classic use case for throttling an event stream. This is commonly also called 'backpressure' when dealing with mechanisms like throttling, debouncing and similar techniques for smoothing out the performance of a scroll event.
The simplest and direct way would be to just use an already coded implementation such as lodash's _.throttle method:
https://www.npmjs.com/package/lodash.throttle
if you want to learn more about throttling and debouncing, you can here:
https://css-tricks.com/debouncing-throttling-explained-examples/
$(document).ready(function(){
var $rawDiv = $('#raw-events'),
$debounceDiv = $('#debounced-events'),
$scrollArea = $('.scroll-area'),
initialized = false,
frequency = 100,
barLength = 0,
globalColor = 2,
colorNeedChange = false,
interval_id,
rawColor = 0,
debounceColor = 0,
maxBarLength = 87;
var throttledEvent = _.throttle(function(div){
debounceColor = globalColor;
}, frequency*5, {'trailing': true});
var changeDebouncedColor = _.debounce(function(div){
// Change colors, to visualize easier the "group of events" that is reperesenting this debounced event
globalColor++;
if (globalColor > 9){
globalColor = 2;
}
}, frequency*4, {leading:false, trailing:true});
function draw_tick_marks(){
// every x seconds, draw a tick mark in the bar
interval_id = setInterval(function(){
barLength++;
$rawDiv.append('<span class="color' + rawColor + '" >');
$debounceDiv.append('<span class="color' + debounceColor + '" >');
rawColor = 0;
debounceColor = 0;
if (barLength > maxBarLength){
clearInterval(interval_id);
}
}, frequency);
};
// Track Mouse movement or clicks for mobile
$scrollArea.on('scroll', function (){
if (!initialized) {
initialized = true;
draw_tick_marks();
$(this).addClass('active');
}
rawColor = globalColor;
throttledEvent();
changeDebouncedColor();
});
$('.reset').on('click', function(){
initialized = false;
$scrollArea.removeClass('active');
$rawDiv.empty();
$debounceDiv.empty();
barLength = 0;
clearInterval(interval_id);
});
});
body {
background: #ddd;
color: #000;
font: 15px/1.51 system, -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
margin:0 auto;
max-width:600px;
padding:20px;
}
.events{
padding:0px 20px 10px 20px;
height: 23px;
}
.events span {
height:17px;
width:6px;
display:inline-block;
border-right:1px solid #111;
}
.events span:last-of-type {
border:2px solid black;
border-bottom: 4px solid #AAA;
border-top: 0px;
margin-bottom:-17px;
margin-left:-2px;
}
h2 {
margin:10px 0 5px 0;
clear:both;
font-weight: normal;
font-size:14px;
padding:6px 20px;
}
.scroll-area {
width: 100%;
overflow-y: scroll;
}
.scroll-slug {
width: 18000px;
height: 50px;
background:
linear-gradient(45deg, #92baac 45px, transparent 45px)64px 64px,
linear-gradient(45deg, #92baac 45px, transparent 45px,transparent 91px, #e1ebbd 91px, #e1ebbd 135px, transparent 135px),
linear-gradient(-45deg, #92baac 23px, transparent 23px, transparent 68px,#92baac 68px,#92baac 113px,transparent 113px,transparent 158px,#92baac 158px);
background-color:#e1ebbd;
background-size: 128px 128px;
}
.trigger-area.active {
background:#2F5065;
}
.clickme:hover,
.clickme:active{
background-color: #333;
}
.clickme:active{
padding: 4px 5px;
}
.reset {
display:inline-block;
padding: 10px;
text-align: center;
font-size:24px;
cursor:pointer;
border: 1px solid white;
}
.visualizations {
margin-top:10px;
background:rgba(0,0,0,0.3);
}
.details {
font-size:13px;
color:#999;
}
/* stating the obvious: color0 represents our empty color */
.color0 { transparent}
.color1 { background-color: #FFE589}
.color2 { background-color: #B9C6FF}
.color3 { background-color: #99FF7E}
.color4 { background-color: #FFB38A}
.color5 { background-color: #A5FCFF}
.color6 { background-color: #FF8E9B}
.color7 { background-color: #E3FF7E}
.color8 { background-color: #FFA3D8}
.color9 { background-color: #5ca6ff}
.color10 { background-color: #9BFFBB}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a class="reset">Reset Timer</a>
<div class="visualizations">
<h2>Raw events over time</h2>
<div id="raw-events" class="events"></div>
<h2>Throttled events
<span class="details"> 300ms, trailing</span></h2>
<div id="debounced-events" class="events"></div>
<div class="scroll-area">
<div class="scroll-slug"></div>
</div>
</div>
Look at the code snippet. I have modified one of the examples to show throttling when you scroll. Scroll in the patterned area back and forth. See how the top one is constantly firing. look at the bottom one how it is only so often. Change the frequency.
a second thing to point out is that you are attaching multiple events to scroll. Remember, the reason we are throttling is because that thing fires like crazy!
This is how you fix scroll performance.
Upvotes: 2
Reputation: 90058
The only clear mistake in the code you posted is adding two events on scroll
, instead of one. scroll
is quite expensive, so in both theory and practice, this should be faster:
window.addEventListener("scroll",function(){
window.scrollY<750 ?
(document.body.classList.remove("scrolled"),
document.body.classList.add("notscrolled")):
(document.body.classList.add("scrolled"),
document.body.classList.remove("notscrolled"))
});
The script doesn't trigger repaint/reflow unless the 750px
scrollpoint is passed from one side to the other, but the same applies to your script as well.
If it's still laggy, consider creating a minimal, complete and verifiable example of the problem you are describing. So far I haven't been able to experience it in any browser, on any device.
There's more to it than what you're showing.
Upvotes: 1
Reputation: 1398
The following may work. You are not giving much details so based on what you provided I set to exceed 250. There is a timer which every time you scroll it will be clear and reset. Then when you are no longer scrolling it will execute the timed function (Only after you have stopped scrolling and the time has not been consumed) You can change the time and the condition.
var js = {
cl: "classList",
sy: "scrollY",
pyo: "pageYOffset",
};
var nav = document.getElementById("header");
var timer;
function ss2(e){
window.clearTimeout(timer);
timer = window.setTimeout(function(){
var mm, cl;
cl = nav[js.cl];
if((mm = window[js.sy] || window[js.pyo]) > 250){
if(!cl.contains("mutate")){
cl.add("mutate");
}
}
else{
cl.remove("mutate");
}
}, 250);
};
document.addEventListener("scroll", ss2);
body{
height: 5000px;
}
#header{
width: 100%;
height: 50px;
background: #f7f8f9;
}
.mutate{
position: fixed !important;
background: salmon !important;
}
<div id="header">
</div>
Upvotes: 1