Scroll event skipped during event loop - javascript

According to the HTML Standart in the event loop during updateing the rendering 2 things happen sequentially:
For each fully active Document in docs, run the scroll steps for that Document, passing in now as the timestamp. [CSSOMVIEW]
For each fully active Document in docs, run the animation frame callbacks for that Document, passing in now as the timestamp.
Therefore I suppose that IF before the event loop run both
a scroll happens (via scrollTop or scrollLeft property of a scrollable element change or scrollTo execution)
a callback scheduled via requestAnimationFrame
THEN there should be at least one scroll event for each scrolled element in any event loop run,and callbacks execution always happens after scroll event listeners.
I wrote a function that checks this proposition and tried it in Chrome and FireFox:
function Test(iterations, stepTimeout) {
let xOuter = document.createElement("div");
xOuter.style.width = "100px";
xOuter.style.height = "100px";
xOuter.style.overflow = "scroll";
//xOuter.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps
xOuter.style.zIndex = "10000";
xOuter.style.position = "absolute";
xOuter.style.top = "0";
xOuter.style.left = "0";
xOuter.style.scrollBehavior = "auto";
let xInner = document.createElement("div");
xInner.style.width = "200%";
xInner.style.height = "200%";
xOuter.appendChild(xInner);
let xState = "What happens during each interation of Step:\n";
let xStepIndex = 0,
xScrollUpdateIndex = 0,
xLastExecutedScrollUpdate = -1;
xOuter.addEventListener("scroll", () => {
xState += `scroll-${xScrollUpdateIndex} `;
xLastExecutedScrollUpdate = xScrollUpdateIndex;
});
document.body.appendChild(xOuter);
function Step() {
if (xStepIndex > iterations) {
xOuter && xOuter.parentNode && xOuter.parentNode.removeChild(xOuter);
console.log(xState);
return;
}
let xScrollProp = Math.random() > 0.5 ? "scrollLeft" : "scrollTop";
let xOldScrollPos = xOuter[xScrollProp],
xNewScrollPos;
do {
xNewScrollPos = Math.round(Math.random() * 200);
} while (xOldScrollPos == xNewScrollPos);
xState += `\n ${xStepIndex++} : [${xNewScrollPos}] `;
xOuter[xScrollProp] = xNewScrollPos; // scroll event scheduled
xScrollUpdateIndex++;
let xCurrentScrollUpdateIndex = xScrollUpdateIndex;
requestAnimationFrame(() => {
if (xCurrentScrollUpdateIndex > xLastExecutedScrollUpdate)
console.log("Scroll failed to fire before rAF");
xState += `rAF-${xCurrentScrollUpdateIndex} `;
});
new Promise((resolve) => setTimeout(() => resolve(), stepTimeout)).then(Step);
}
Step();
}
Test(200, 10);
However it looks like in some event loop runs scroll event doesn't fire.
Is this an expected behavior?

Related

Dom element infinitely incrementing

I'm in my first steps of creating an auto aim system for the enemy bot but I can't even seem to get him to shoot properly.
I create a div element, and move it in a direction. This happens every 4 seconds. I delete the div before the next one gets created. This works. But somehow it creates more and more elements over time and soon turns into a massive amount of divs all flying in the same direction.
** make the bullet here **
function makeBullet() {
if (player.enemy) {
if (player.enemyBullet.bulletInterval == true) {
console.log("working");
player.enemyBullet.bullet = document.createElement('div');
player.enemyBullet.bullet.className = 'bullet';
gameArea.appendChild(player.enemyBullet.bullet);
player.enemyBullet.bullet.x = player.enemy.x;
player.enemyBullet.bullet.y = player.enemy.y;
player.enemyBullet.bullet.style.left = player.enemyBullet.bullet.x + 'px';
player.enemyBullet.bullet.style.top = player.enemyBullet.bullet.y + 'px';
player.enemyBullet.bulletInterval = false;
setInterval(function () {
player.enemyBullet.bulletInterval = true;
}, 4000);
}
}
}
** Move Bullet **
function moveBullet() {
let bullets = document.querySelectorAll('.bullet');
bullets.forEach(function (item) {
item.x += 3;
item.y -= 3;
item.style.left = item.x + 'px';
item.style.top = item.y + 'px';
if(item.y < 200){
item.parentElement.removeChild(item);
player.enemyBullet.bullet = null;
}
})
}
** invoked in request Animation function **
function playGame() {
if (player.inplay) {
moveBomb();
moveBullet();
makeBullet();
window.requestAnimationFrame(playGame);
}
}
** Initiate interval boolean here **
let player = {
enemyBullet: {
bulletInterval: true
}
}
** LINK TO JS FIDDLE FULL PROJECT ** (click here to see what's happening)
https://jsfiddle.net/mugs17/j7f12a0n/
You are using setInterval like set timeout. However setInterval does not only execute one time. Once you've called setInterval the first time, calling it again makes a new different interval that also executes every 4 seconds.
So each time your function gets invoked its creating a new Interval which is why your div elements are increasing as time goes on
Instead wrap the entire create bullet code inside setInterval and make it execute only once by setting a boolean conditional and then immediately changing that conditional to false. Like this:
if (player.enemy) {
if (player.enemyBullet.bulletInterval == true) {
player.enemyBullet.bulletInterval = false;
console.log("working");
setInterval(function () {
player.enemyBullet.bullet = document.createElement('div');
player.enemyBullet.bullet.className = 'bullet';
gameArea.appendChild(player.enemyBullet.bullet);
player.enemyBullet.bullet.x = player.enemy.x;
player.enemyBullet.bullet.y = player.enemy.y;
player.enemyBullet.bullet.style.left = player.enemyBullet.bullet.x + 'px';
player.enemyBullet.bullet.style.top = player.enemyBullet.bullet.y + 'px';
}, 4000);
}
}
}

Cannot add eventListener to img elements before they are created

I have a blank page, if you dblclick anywhere, an image will fade in where the dblclick event happened. This works fine.
I have a function appear which can be called to fade in elements or fade out, depending on parameter passed into it. I call that from the getItOut function, passing eventTarget as first parameter.
But, now I want a functionality to remove the images on click(our other event, does not really matter).
I try this:
var allImages = []; // initializing array to iterate over img node list
var displayedImages = document.getElementsByTagName('IMG'); get img node list
allImages.push(displayedImages); // push the displayed imgs to the array
// here I want to run this function by iterating with forEach over the array
allImages.forEach(function(el) {el.addEventListener('click', getItOut)}); // getting type error, el.addEventListener is not a function
function getItOut(event){ // this should be called during the above forEach iteration
var getOut = event.target;
appear(getOut, 100, -5, 40);
}
Here is the codepen link:
https://codepen.io/damPop/pen/RqMxex
I have commented out the above, as the thrown error would not let anything run.
What am I doing wrong here? Should I check if an img element exists in the DOM at some place?
Well, yes!
You CANNOT hook a listener to something, that does not exist.
There are 2 ways to go about it.
Wait for the elements to be created (document.ready)
Hook your listener to the document, and then parse to see what has been clicked. jQuery has got you covered: $(document).on('EVENT', 'SELECTOR', function(){})
Because you are pushing Images as HTMLCollection not a DOM element to allImages and when you iterate through you can't bind EventListener to a Collection.
allImages.push(displayedImages); // result is [HTMLCollection(1)]
Also wait for images or DOM content to load then add eventlistener
It works.I created a jsfiddle test it in jsfiddle
Code snippet:
var images = ['//unsplash.it/500/500', '//unsplash.it/400/500','//unsplash.it/500/300'];
function getItOut(event){
var getOut = event.target;
appear(getOut, 100, -5, 40);
//Fixing bug for click same position of disappeared image
// to not appear again
// and fix fadeOut opacity
var timerId = setInterval(function () {
const opacity = Number(getOut.style.opacity);
if(opacity == 0) {
getOut.parentNode.removeChild(getOut);
clearInterval(timerId);
}
}, 40)
}
document.addEventListener('dblclick', function(event) {
var currentImg;
var ix = event.clientX; // Get the coordinates
var iy = event.clientY;
var x = document.createElement("IMG");
x.setAttribute("src", images[0]);
x.setAttribute("width", "304");
x.setAttribute("height", "228");
x.style.position="absolute";
x.style.top= iy + 'px';
x.style.left= ix + 'px';
x.style.opacity = 0.1;
document.body.appendChild(x);
x.addEventListener('click', getItOut)
//x.classList.add("fadeIn");
var ix = "";
var iy = "";
console.log(event.currentTarget);
appear(x, 0, 5, 40);
});
function appear(elm, i, step, speed){
var t_o;
//initial opacity
i = i || 0;
//opacity increment
step = step || 5;
//time between opacity increments in ms
speed = speed || 50;
t_o = setInterval(function(){
//get opacity in decimals
var opacity = i / 100;
//set the next opacity step
i = i + step;
if(opacity > 1 || opacity < 0){
clearInterval(t_o);
//if 1-opaque or 0-transparent, stop
return;
}
//real browsers
elm.style.opacity = opacity;
//IE
elm.style.filter = 'alpha(opacity=' + opacity*100 + ')';
}, speed);
}

Selenium: Scroll till end of the page

Selenium:
I am new to WebDriverJS. I have tried this approach in Java.
Long repaeted = 0l, scrollHeight = 0l, returnHeight = 0l;
while(true){
if (repaeted == 0) {
returnHeight = (Long) jse.executeScript("var scroll =document.documentElement.scrollHeight;window.scrollTo(0, scroll); return scroll;");
System.out.println("Height : "+scrollHeight +"\t Chnage : "+returnHeight+ "\t Repeated : "+repaeted);
scrollHeight = returnHeight;
}else {
returnHeight = (Long) jse.executeScript("var scroll = document.documentElement.scrollHeight;window.scrollTo(0, scroll); return scroll;");
System.out.println("Height : "+scrollHeight +"\t Chnage : "+returnHeight+ "\t Repeated : "+repaeted);
if (scrollHeight.intValue() == returnHeight.intValue()) {
System.out.println("Break.."+ returnHeight);
break;
} else { scrollHeight = returnHeight; }
}
repaeted++;
}
but I am facing problem in webdriverjs while iterating the loop.
var webdriver = require('..'),
By = webdriver.By,
until = webdriver.until;
// make sure chromedriver can be found on your system PATH
var driver = new webdriver.Builder()
.forBrowser('chrome')
.withCapabilities(webdriver.Capabilities.chrome())
.build();
driver.get('https://in.yahoo.com/').then(function(){
var window = new webdriver.WebDriver.Window(driver);
window.maximize();
driver.manage().timeouts().implicitlyWait(1000 * 3);
})
.then(function(){
console.log('Entered');
var check = 0, count = 0
for(var i = 0; i< 50; i++){
//driver.sleep(1000 * 2);
driver.executeScript('var dynamicscroll = document.documentElement.scrollHeight;window.scrollTo(0, dynamicscroll);return dynamicscroll;').then(function(height){
console.log('Check : '+check+' Height : '+height +' Repeated : '+(count++));
if(check === 0 || check !== height){console.log('continue'); check = height; }
else { console.log('break'); i = 100; }
});
}
})
.then(null, function(err) {
console.error("An error was thrown! By Promise..." + err);
});
driver.quit();
In my code I have hardcoded for loop to iterate until 50 times and I want to quit/break the loop when the scroll height is reached to end. In this approach, I want to remove hardcode like java-code because I don't know how many times to iterate for other applications whose scroll is kept on increasing dynamically.
For example, Facebook application, Yahoo News...
Scrolling to the bottom of a dynamic page can be challenging depending on how it is implemented by the page.
First you'll have to find the container with the scrollbar since it can be different from the one linked to window.scrollTo.
Then scroll the container by increasing scrollTop until the scrollHeight becomes steady with no pending requests. To check if there are pending requests, either evalute jQuery.active if the page has JQuery or hook XMLHttpRequest to monitor the calls on send.
Here is an example using on a generic function to scroll to the bottom of the page a number of times or until the end:
var webdriver = require('selenium-webdriver');
var driver = new webdriver.Builder().forBrowser('chrome').build();
driver.get('https://groups.google.com/forum/#!search/webdriverjs');
// scroll to the bottom 3 times
driver.executeAsyncScript(scrollBottom, 3)
.then(n => console.log(`scrolled ${n} time(s)`));
// scroll to the bottom until the end
driver.executeAsyncScript(scrollBottom)
.then(n => console.log(`scrolled ${n} time(s)`));
function scrollBottom(){
var count = arguments[arguments.length - 2] || 0x7fffffff;
var callback = arguments[arguments.length - 1];
/* get the scrollable container */
var elm = document.elementFromPoint(window.innerWidth - 25, window.innerHeight / 2);
for ( ;elm && (++elm.scrollTop, !elm.scrollTop); elm=elm.parentElement);
elm = elm || document.documentElement;
/* hook XMLHttpRequest to monitor Ajax requests */
if (!('idle' in XMLHttpRequest)) (function(){
var n = 0, t = Date.now(), send = XMLHttpRequest.prototype.send;
var dispose = function(){ --n; t = Date.now(); };
var loadend = function(){ setTimeout(dispose, 1) };
XMLHttpRequest.idle = function() { return n > 0 ? 0 : Date.now() - t; };
XMLHttpRequest.prototype.send = function(){
++n;
this.addEventListener('loadend', loadend);
send.apply(this, arguments);
};
})();
/* scroll until steady scrollHeight or count of scroll and no pending request */
var i = 0, scrollHeight = -1, scrollTop = -1;
(function scroll(){
if ((scrollHeight === elm.scrollHeight || i === count) && XMLHttpRequest.idle() > 60)
return callback(i);
scrollTop = elm.scrollTop;
scrollHeight = elm.scrollHeight;
if (i < count)
i += (elm.scrollTop = 0x7fffffff, scrollTop !== elm.scrollTop);
setTimeout(scroll, 100);
})();
}
Or by scrolling until the height no longer increases during a specific time (5 seconds here) :
function scrollBottom(){
var count = arguments[arguments.length - 2] || 0x7fffffff;
var callback = arguments[arguments.length - 1];
var timeout = 5000; /* 5 seconds timeout */
var i = 0;
/* get the scrollable container */
var elm = document.elementFromPoint(window.innerWidth - 25, window.innerHeight / 2);
for ( ;elm && (++elm.scrollTop, !elm.scrollTop); elm=elm.parentElement);
elm = elm || document.documentElement;
/* scroll while the height is increasing or until timeout */
(function scroll(){
var endtime = Date.now() + timeout;
var height = elm.scrollHeight;
elm.scrollTop = 0x7fffffff; /* scroll */
setTimeout(function check(){
if (Date.now() > endtime) /* returns if waited more than 5 sec */
callback(i);
else if (elm.scrollHeight == height) /* wait again if same height */
setTimeout(check, 60);
else if (++i === count) /* returns if scrolled the expected count */
callback(i);
else /* scroll again */
setTimeout(scroll, 60);
}, 250);
})();
}
From experience, the quickest way to scroll to the end of a page is to look for the footer element and movetoit, usually #footer or .footer or just footer selector will do it. E.g.:
footer = driver.findElement({id: "footer"});
driver.executeScript("arguments[0].scrollIntoView(false);", footer);
In the case of 'endless' streams like Facebook, Twitter, etc. They may block you when you reach a limit so it's okay to combine max iterations with window.scrollTo(0, 300); recursively and wait for some seconds after each scroll.
Pure JavaScript:
In JavaScript we can use setTimeout() function. which will call the specified function recursively after the time delay you specified.
I have tested the google groups application, whose div tag vertical scroll dynamically increases. To load the content I used the time delay of 5000. you can test this code in browser's console use this URL: https://groups.google.com/forum/#!search/webdrierjs.
var i = 0, height = 0, check = 0, t = null;
flow();
function run(arg){
var objDiv = document.querySelector('div.IVILX2C-b-F');
objDiv.scrollTop = objDiv.scrollHeight;
return objDiv.scrollHeight;
}
function flow() {
i++;
switch(i){
case 0: height = run(i);
sleep(5000);
break;
case -1: run(i);
clearTimeout(t); //stops flow
break;
default: check = run(i);
console.log('Return Height : '+check +' Count : '+i);
if(check === height){ i = -2;
console.log('Break message : '+i);
}else {
console.log('Changed...');
height = check;
}
sleep(5000);
break;
}
}
function sleep(delay) { t=setTimeout("flow()",delay);} //starts flow control again after time specified.
//function sleep(delay) { var start = new Date().getTime(); while (new Date().getTime() < start + delay); flow(); } // stops execution and then continues.
but even I cannot run this script using WebDriver/WebDriverJS because it is not going to call recursive function on time delay.
I am aware that this is kind of an old topic, yet I've still came across it while I had similar problem. Given sollutions didn't quite suite me.
I wrote my own function - without using any "arguments[...]" as argument of driver.executeScript(...).
...I just don't get that "arguments[...]" to be honest... x)
Below I'm presenting my solution.
(I believe its shorter and cleaner. And uses async/await syntax instead of ".then(s)")
// scroller.service.ts
import { WebDriver } from 'selenium-webdriver';
export async function scrollTillEnd(driver: WebDriver): Promise<void> {
const scrollDownTillEnd = async () => {
let counter = 0
let heightBefore = 0
let heightAfter = 0
let shouldContinue = true
const scrollDown = () => window.scrollBy(0, document.body.scrollHeight || document.documentElement.scrollHeight)
const scrollAndCalc = async () => {
heightBefore = document.body.scrollHeight || document.documentElement.scrollHeight
scrollDown()
await new Promise((res) => setTimeout(() => res(null), 2000)) // sleep in vanillaJS
heightAfter = document.body.scrollHeight || document.documentElement.scrollHeight
shouldContinue = heightAfter != heightBefore
counter++
console.log({ shouldContinue, heightBefore, heightAfter, counter })
}
while (shouldContinue) {
await scrollAndCalc()
}
}
await driver.executeScript(scrollDownTillEnd)
}
(executed on chrome 97 )
// example usage:
class App{
// ...
private async initDriver() {
this.driver = await new DriverInitiator().getDriver()
await this.driver.get(this.url)
}
private initPagesModels() {
this.cookiesWelcome = new CookiesWelcomePage(this.driver)
}
async runExample() {
await this.initDriver()
this.initPagesModels()
await this.driver.sleep(1000)
await this.cookiesWelcome.acceptCookies()
await scrollTillEnd(this.driver) // this is the part where i call my scrolling function :)
console.log("selenium script ended.")
await this.driver.sleep(5000)
await this.driver.quit();
}
}

Scroll event firing too many times. I only want it to fire a maximum of, say, once per second

I have a page with "infinite scroll". It calculates the difference between the end of the page and the current page and loads more content if this difference is small enough. The code is soemthing like this using jQuery:
$(window).on('scroll', function() {
if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
# load more content via ajax
}
Now, the problem is that every time I scroll, this event fires multiple times per scroll. I would like fire at most every x milliseconds. How would I do this?
Check out the Underscore.js library's "throttle" method.
http://underscorejs.org/#throttle
The example it gives is exactly what you're asking about - limiting how often you have to handle scroll events.
One way to solve this problem is to define a time interval and only process a scroll event once within that time interval. If more than one scroll event comes in during that time interval, you ignore it and process it only when that time interval has passed.
var scrollTimer, lastScrollFireTime = 0;
$(window).on('scroll', function() {
var minScrollTime = 100;
var now = new Date().getTime();
function processScroll() {
console.log(new Date().getTime().toString());
}
if (!scrollTimer) {
if (now - lastScrollFireTime > (3 * minScrollTime)) {
processScroll(); // fire immediately on first scroll
lastScrollFireTime = now;
}
scrollTimer = setTimeout(function() {
scrollTimer = null;
lastScrollFireTime = new Date().getTime();
processScroll();
}, minScrollTime);
}
});
This will fire the first scroll event immediately and then get you a scroll event approximately once every 100ms while the scrollbar is being moved and then one final event after the scrollbar stops moving. You can adjust the frequency of the event by changing the argument to the setTimeout (what is currently set to 100).
There is a demo here: http://jsfiddle.net/jfriend00/EBEqZ/ which you need to open a debug console window, start moving the scrollbar in the content window and then watch the time of each scroll event in the debug console window. On my version of Chrome, they are set for a minimum spacing of 100ms and they seem to occur every 100-200ms.
There is a cool explanation from John Resig, the creator of jQuery to resolve this situation.
var outerPane = $details.find(".details-pane-outer"),
didScroll = false;
$(window).scroll(function() {
didScroll = true;
});
setInterval(function() {
if ( didScroll ) {
didScroll = false;
// Check your page position and then
// Load in more results
}
}, 250);
The source:
http://ejohn.org/blog/learning-from-twitter/
var isWorking = 0;
$(window).on('scroll', function()
{
if(isWorking==0)
{
isWorking=1;
if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
# load more content via ajax
setTimeout(function(){isWorking=0},1000);
}
}
var now = new Date().getTime();
$(window).scroll( function () {
if (window.pageYOffset > loadMoreButton.offsetTop - 1000)
{
if (new Date().getTime() - now > 1000)
{
console.log("Task executed once per second");
now = new Date().getTime();
}
}
});
Or
You can use Throttling fonction calls:
throttling-function-calls
function throttle(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
You can call it like this:
$('body').on('mousemove', throttle(function (event) {
console.log('tick');
}, 1000));
Here is a solution that doesn't require the use of an extra JS library or plugin, that aims for simplicity. It might not be as efficient as other implementations but it is definitely a step up from firing your main event every time you scroll.
This was taken from this blog post by Danny Van Kooten. Which I have used in delaying my onscroll() events for my back-to-top button on my blog.
var timer;
$(window).scroll(function() {
if(timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function() {
// actual code here. Your call back function.
console.log( "Firing!" );
}, 100);
});
You can also further improve performance by moving out variables out of the callback function to avoid unnecessary recalculations, for example the value of $(window).height() or height of some static div element that won't change once the page is loaded.
Here's an example that is adapted from my use case.
var scrollHeight = $("#main-element").height(); //never changes, no need to recalculate.
$(window).on('scroll', function() {
if (timer)
window.clearTimeout(timer);
timer = window.setTimeout(function() {
var scrollPosition = $(window).height() + $(window).scrollTop();
if ($(window).scrollTop() < 500)
$(".toggle").fadeIn(800);
else
$(".toggle").fadeOut(800);
}, 150); //only fire every 150 ms.
});
This limits the actual function to only execute every 150ms, or else reset the timer back to 0 if 150ms has not passed. Tweak the value to suit what you need.
the scroll fire multiple times is correct and you should able to get the scroll position differently each time. I think you need to set a timer when you first get in the scroll event like you mentioned x milliseconds, and also record the time stamp, and then next time scroll event fire, check the last trigger time and ignore it if it's within x milliseconds, and do the real job in your Timer action.
One does not need a ton of local variables for a decent throttle function. The purpose of a throttle function is to reduce browser resources, not to apply so much overhead that you are using even more. As proof of evidence of this claim, I have devised a throttle function that has only 5 'hanging' variables referenes in its scope. Additionally, my different uses for throttle functions require many different circumstances for them. Here is my list of things that I believe 'good' throttle function needs.
Immediately calls the function if it has been more than interval MS since the last call.
Avoids executing function for another interval MS.
Delays excessive event firing instead of dropping the event altogether.
Updates the delayed event object on successive calls so that it doesn't become 'stale'.
And, I believe that the following throttle function satisfies all of those.
function throttle(func, alternateFunc, minimumInterval) {
var executeImmediately = true, freshEvt = null;
return function(Evt) {
if (executeImmediately) { // Execute immediatly
executeImmediately = false;
setTimeout(function(f){ // handle further calls
executeImmediately = true;
if (freshEvt !== null) func( freshEvt );
freshEvt = null;
}, minimumInterval);
return func( Evt );
} else { // Delayed execute
freshEvt = Evt;
if (typeof alternateFunc === "function") alternateFunc( Evt );
}
};
}
Then, to wrap this throttle function around DOM event listeners:
var ltCache = [];
function listen(obj, evt, func, _opts){
var i = 0, Len = ltCache.length, lF = null, options = _opts || {};
a: {
for (; i < Len; i += 4)
if (ltCache[i] === func &&
ltCache[i+1] === (options.alternate||null) &&
ltCache[i+2] === (options.interval||200)
) break a;
lF = throttle(func, options.alternate||null, options.interval||200);
ltCache.push(func, options.alternate||null, options.interval||200, lF);
}
obj.addEventListener(evt, lF || ltCache[i+3], _opts);
};
function mute(obj, evt, func, options){
for (var i = 0, Len = ltCache.length; i < Len; i += 4)
if (ltCache[i] === func &&
ltCache[i+1] === (options.alternate||null) &&
ltCache[i+2] === (options.interval||200)
) return obj.removeEventListener(evt, ltCache[i+3], options);
}
Example usage:
function throttle(func, alternateFunc, minimumInterval) {
var executeImmediately = true, freshEvt = null;
function handleFurtherCalls(f){
executeImmediately = true;
if (freshEvt !== null) func( freshEvt );
freshEvt = null;
};
return function(Evt) {
if (executeImmediately) { // Execute immediatly
executeImmediately = false;
setTimeout(handleFurtherCalls, minimumInterval);
return func( Evt );
} else { // Delayed execute
freshEvt = Evt;
if (typeof alternateFunc === "function") alternateFunc( Evt );
}
};
}
var ltCache = [];
function listen(obj, evt, func, _opts){
var i = 0, Len = ltCache.length, lF = null, options = _opts || {};
a: {
for (; i < Len; i += 4)
if (ltCache[i] === func &&
ltCache[i+1] === (options.alternate||null) &&
ltCache[i+2] === (options.interval||200)
) break a;
lF = throttle(func, options.alternate||null, options.interval||200);
ltCache.push(func, options.alternate||null, options.interval||200, lF);
}
obj.addEventListener(evt, lF || ltCache[i+3], _opts);
};
function mute(obj, evt, func, options){
for (var i = 0, Len = ltCache.length; i < Len; i += 4)
if (ltCache[i] === func &&
ltCache[i+1] === (options.alternate||null) &&
ltCache[i+2] === (options.interval||200)
) return obj.removeEventListener(evt, ltCache[i+3], options);
}
var numScrolls = 0, counter = document.getElementById("count");
listen(window, 'scroll', function whenbodyscrolls(){
var scroll = -document.documentElement.getBoundingClientRect().top;
counter.textContent = numScrolls++;
if (scroll > 900) {
console.log('Body scrolling stoped!');
mute(window, 'scroll', whenbodyscrolls, true);
}
}, true);
<center><h3>\/ Scroll Down The Page \/</h3></center>
<div style="position:fixed;top:42px"># Throttled Scrolls: <span id="count">0</span></div>
<div style="height:192em;background:radial-gradient(circle at 6em -5em, transparent 0px, rgba(128,0,0,.4) 90em),radial-gradient(circle at 10em 40em, rgba(255,255,255,.8) 0px, rgba(128,0,0,.02) 50em),radial-gradient(circle at 4em 80em, rgba(0,192,0,.75) 0px,rgba(0,128,0,.56) 10em,rgba(255,0,96,.03125) 30em),radial-gradient(circle at 86em 24em, rgba(255,0,0,.125) 0px,rgba(0,0,255,.0625) 60em,transparent 80em);"></div>
<style>body{margin:0}</style>
By default, this throttles the function to at most one call every 200ms. To change the interval to a different number of milliseconds, then pass a key named "interval" in the options argument and set it to the desired milliseconds.

Setting a time for flicker animation on img

I'm using this code to make my logo flicker on my website. But It becomes annoying when it continues to flicker while browsing, how can I set a time to allow it to flicker for something like the first 15seconds on page load, then stops?
JS code I'm using:
$(document).ready(
function(){
var t;
const fparam = 100;
const uparam = 100;
window.flickr = function(){
if(Math.round(Math.random())){
$("#logodcoi").css("visibility","hidden");
t = setTimeout('window.unflickr()',uparam);
}
else
t = setTimeout('window.flickr()',fparam);
}
window.unflickr = function(){
if(Math.round(Math.random())){
$("#logodcoi").css("visibility","visible");
t = setTimeout('window.flickr()',fparam);
}
else
t = setTimeout('window.unflickr()',uparam);
}
t = setTimeout('window.flickr()',fparam);
});
You could have a counter, which you then use to decide whether you want to set another timeout. As a side note, you should never add functions to window and then passing a string to setTimeout. Always just pass the function itself:
$(document).ready(function(){
var t;
var amount = 0;
const fparam = 100;
const uparam = 100;
function timeout(f, t) { // this function delegates setTimeout
if(amount++ < 150) { // and checks the amount already (un)flickered
setTimeout(f, t); // (150 * 100 ms = 15 s)
}
}
var flickr = function(){
if(Math.round(Math.random())){
$("#logodcoi").css("visibility","hidden");
t = timeout(unflickr,uparam);
}
else
t = timeout(flickr,fparam);
};
var unflickr = function(){
if(Math.round(Math.random())){
$("#logodcoi").css("visibility","visible");
t = timeout(flickr,fparam);
}
else
t = timeout(unflickr,uparam);
};
t = timeout(flickr,fparam);
});
I see you're using jquery, you could use the following, if I remember correctly, all the stuff I use below has been in jquery since 1.0, so you should be good:
counter = 1;
function hideOrShow(){
$(".classToSelect").animate({"opacity": "toggle"}, 100);
counter = counter +1;
if (counter >= 21) clearInterval(flickerInterval);
}
flickerInterval = setInterval(hideOrShow, 100);
Change the selector, animation duration, and variable names to whatever you fancy/need.

Categories

Resources