<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
</head>
<body>
<div>
<div id="viewport">
<div class="inner">scroll me!</div>
</div>
<div>
Mode<br>
<input type="radio" name="mode" value="0" checked> - normal<br>
<input type="radio" name="mode" value="1"> - limited (dhilt)<br>
<input type="radio" name="mode" value="2"> - limited (user650881)<br>
<input type="radio" name="mode" value="3"> - limited (willem-dhaeseleer)<br>
<input type="radio" name="mode" value="4"> - limited (lodash debounce + leading)<br>
<input type="radio" name="mode" value="5"> - limited (Mikk3lRo)<br>
</div>
</div>
<div style="clear: both;"></div>
<div id="info">Scroll event handling<br></div>
<script src="dhilt.js"></script>
<script src="user650881.js"></script>
<script src="willem-dhaeseleer.js"></script>
<script src="Mikk3lRo.js"></script>
<script src="_script.js"></script>
</body>
</html>
const viewportElement = document.getElementById('viewport');
const infoElement = document.getElementById('info');
let count = 0;
const processEvent = (event) => {
count++;
infoElement.innerHTML += ' ' + count;
console.log('go ' + count);
//console.log(event);
}
const DELAY = 125;
const handler = [
processEvent, // normal
debounceNext(processEvent, DELAY), // dhilt
makeRateLimitedEventHandler(DELAY, processEvent), // user650881
debounceWithDelay(processEvent, DELAY, 0), // willem-dhaeseleer
_.debounce(processEvent, DELAY, {leading: true}), // lodash debounce + leading
debounceish(DELAY, processEvent) //Mikk3lRo
];
const getHandler = (event) => {
const index = document.querySelector('input[name="mode"]:checked').value;
return handler[index](event);
}
viewportElement.addEventListener('scroll', getHandler);
#viewport{
height: 200px;
width: 150px;
overflow: scroll;
float: left;
margin-right: 10px;
}
.inner {
height: 3000px;
}
#info {
margin-top: 10px;
}
This demo is based on "Selective timeout based handling: immediate first, debounce next" topic from stackoverflow: https://stackoverflow.com/questions/46633701/selective-timeout-based-handling-immediate-first-debounce-next
//https://stackoverflow.com/a/46677791/3211932
function makeRateLimitedEventHandler(delta_ms, cb) {
let timeoutId = 0;
let lastEventTimestamp = 0;
return (event) => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = 0;
}
var curTime = Date.now();
if (curTime < lastEventTimestamp + delta_ms) {
timeoutId = setTimeout(() => cb(event), delta_ms);
} else {
cb(event);
}
lastEventTimestamp = Date.now();
};
}
// https://stackoverflow.com/a/46699605/3211932
const debounceNext = (cb, delay) => {
let timer = null;
let next = null;
const runTimer = (delay, event) => {
timer = setTimeout(() => {
timer = null;
if(next) {
next(event);
next = null;
runTimer(delay);
}
}, delay);
};
return (event) => {
if(!timer) {
cb(event);
}
else {
next = cb;
clearTimeout(timer);
}
runTimer(delay, event);
}
};
// https://stackoverflow.com/a/46678116/3211932
const debounceWithDelay = (func, delay, postDelay) => {
let postDebounceWait;
let timeOutLeading = false;
const debounced = _.debounce((...args) => {
if (timeOutLeading) {
func(...args);
} else {
postDebounceWait = setTimeout(() => {
func(...args)
}, postDelay);
}
}, delay, {leading: true});
return (...args) => {
timeOutLeading = true;
clearTimeout(postDebounceWait);
debounced(args);
timeOutLeading = false;
}
};
// https://stackoverflow.com/a/46718342/3211932
function debounceish(delta, fn) {
let timer = null;
return (event) => {
if (timer === null) {
fn(event);
timer = setTimeout(() => timer = null, delta);
} else {
clearTimeout(timer);
timer = setTimeout(() => {
fn(event);
timer = setTimeout(() => timer = null, delta);
}, delta);
}
};
}