I can't figure out why smooth scrolling lags when other websites are being loaded simultaneously in other tabs.
For instance, on http://www.feedrover.com/, if you click multiple article links, the site's smooth scroll will lag heavily while the other articles load. This shouldn't be happening, since the javascript for feedrover should be done?
Any ideas on how to fix this?
A solution from MDN:
Since scroll events can fire at a high rate, the event handler shouldn't execute computationally expensive operations such as DOM modifications. Instead, it is recommended to throttle the event using requestAnimationFrame, setTimeout or customEvent, as follows:
(function() {
var throttle = function(type, name, obj) {
var obj = obj || window;
var running = false;
var func = function() {
if (running) { return; }
running = true;
requestAnimationFrame(function() {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
/* init - you can init any event */
throttle ("scroll", "optimizedScroll");
})();
// handle event
window.addEventListener("optimizedScroll", function() {
console.log("Resource conscious scroll callback!");
});
Related
Suppose I want to use both touch, and mouse (mouse, and touch screen might be available).
There is at least one way to deal.
When the touch fires use preventDefault (first way):
let onMove = event => {
//either touch, or mouse
};
document.body.addEventListener('touchmove', event=>{
//prevent mouse if this event fires
event.preventDefault();
onMove(event);
}, false);
document.body.addEventListener('mousemove', event=>{
onMove(event);
}, false);
What if instead we do this (second way):
let didTouch = false;
let onMove = event => {
//either touch, or mouse
};
document.body.addEventListener('touchmove', event=>{
didTouch = true;
onMove(event);
}, false);
document.body.addEventListener('mousemove', event=>{
if(didTouch){
didTouch = false;
}else{
onMove(event);
}
}, false);
Is the second way viable for handling both touch, and mouse? The first way is recommended, but I'm interested in the possibility of using either way as long as there isn't unforeseen problems.
In my opinion, the second approach is not a good solution because:
Performance: You are handling 2 events (touch and mouse). The first solution prevents mouse event from firing, so your application is doing less event handling.
Complexity: didTouch is adding unnecessary complexity to your code... as a general rule of thumb, if you can achieve the same result by writing less code, the the shorter solution is preferred, unless there is a valid benefit in using the longer solution.
Another potential problem to keep in mind is Mutual Exclusion. JavaScript is Thread Safe and you don't really need to worry about a second event firing and changing your didTouch flag, while you are still processing the first event. But keep in mind that it is not impossible to run into mutex problems with async methods.
I had this problem one time, but with other events, check my solution:
let handler = function (e) {
// do stuff
};
if (isTouchDevice()) {
key.addEventListener('touchstart', handler);
} else {
key.addEventListener('mousedown', handler);
}
function isTouchDevice() {
var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
var mq = function (query) {
return window.matchMedia(query).matches;
};
if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
return true;
}
// include the 'heartz' as a way to have a non matching MQ to help terminate the join
// https://git.io/vznFH
var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
return mq(query);
}
Is there a way to know when the browser is actively running requestAnimationFrame?
For example when I switch tabs from one that was running requestAnimationFrame, the function stops getting executed, when I switch back it continues, what is the best way to deal with this?
To detect if requestAnimationFrame is running 100% you can check:
window.addEventListener('blur', function() {
//not running full
}, false);
and
window.addEventListener('focus', function() {
//running optimal (if used)
}, false);
this can be used as we know requestAnimationFrame reduces trigger rate (in most browsers) when window (tab) is not the active one (IF being used - it depends on the code actually using the requestAnimationFrame).
If you want it to run constantly you can insert a mechanism such as this:
var isActiveTab = true; //update with the events above
function myLoop() {
//my cool stuff here
if (isActiveTab) {
requestAnimationFrame(myLoop);
} else {
setTimeout(myLoop, 16); //force a rate (vblank sync not necessary
//when display isn't updated
}
}
Note that the reduction in rate for requestAnimationFrame is not part of the standard and is a browser specific implementation.
When you again come back to the tab with animation,It must be working fine(If thats the case--following is your answer!!!)
This is what RAF made for.To optimize performance.
SetInterval and Settimeout can be used instead for creating animations, But they cannot interact with the browser and eventually end up hogging up the cpu and the performance is also quite slow.
But your question is really not a question.This is actually a trick used by RAF to better your overall animation experience.
There are several articles which explains RAF.
http://creativejs.com/resources/requestanimationframe/
Just An Optimization TRICK--No need to worry about it
A solution I used in a project for canvas repainting. It's not 100% accurate but it works for out of focus users
// This will run when user is inactive
let = handleVisibilityChange = () => {
if (document.hidden) {
setTimeout(() => {
updateYourStuff();
handleVisibilityChange();
}, 1000);
}
};
// Listen if user is active or inactive
document.addEventListener("visibilitychange", handleVisibilityChange, false);
// Your loop when user is active
function myLoop() {
updateYourStuff();
requestAnimationFrame(myLoop);
}
If you need to know at what time a frame was painted, you can call requestPostAnimationFrame (google canary) or use a polyfill for it.
I have the following jQuery function which is supposed to detect when an element first scrolls into view on a page:
function isScrolledIntoView(elem) {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
My mind is mush today... how can I use this function?
Assume I want to detect when $('.guardrail360') comes into view.
Seems like you need to bind the scroll event and do the detection when that event fires.
$(window).scroll(function () {
isScrolledIntoView('.guardrail360');
});
There are several ways to do it. Just don't use a naive window.scroll implementation if you are concerned with user experience and/or performance.
It's a very, very, bad idea to attach handlers to the window scroll
event. Depending upon the browser the scroll event can fire a lot and
putting code in the scroll callback will slow down any attempts to
scroll the page (not a good idea). Any performance degradation in the
scroll handler(s) as a result will only compound the performance of
scrolling overall. Instead it's much better to use some form of a
timer to check every X milliseconds OR to attach a scroll event and
only run your code after a delay (or even after a given number of
executions - and then a delay).
-John Resig, creator of jQuery, on ejohn.org
Method 1: setInterval
First, there is the naive approach using timers:
var $el = $('.guardrail360');
var timer = setInterval(function() {
if (isScrolledIntoView($el)) {
// do stuff
// to run this block only once, simply uncomment the next line:
//clearInterval(timer);
}
}, 150);
Method 2: window.scroll event
Then there is the trivial, yet crazy inefficient way using the scroll event (depending on the browser, the scroll event will fire hundreds of times per second, so you do NOT want to run a lot of code in here, particularly not code that triggers browser reflows/redraws).
Ever visited a site where scrolling down the page felt sluggish and jittery? That is often caused by a piece of code like this one right here:
var $el = $('.guardrail360');
$(window).on('scroll', function() {
if (isScrolledIntoView($el)) {
// do stuff
}
});
Method 3: best of both worlds
The nifty hybrid approach for high traffic sites, as proposed by John Resig in the aforementioned blog post:
var $el = $('.guardrail360'),
didScroll = false;
$(window).scroll(function() {
didScroll = true;
});
var timer = setInterval(function() {
if ( didScroll ) {
didScroll = false;
if (isScrolledIntoView($el)) {
// do stuff
// to run this block only once, simply uncomment the next line:
//clearInterval(timer);
}
}
}, 250);
Method 4: Throttling / Debouncing
Throttling (minimum N milliseconds between invocations) or debouncing (only one invocation per 'burst') patterns can also be used to efficiently limit the rate at which your inner code executes. Assuming you'd be using Ben Alman's jQuery throttle/debounce plugin, the code looks like this:
var $el = $('.guardrail360');
// Throttling
$(window).on('scroll', $.throttle( 250, function(){
if (isScrolledIntoView($el)) {
// do stuff
}
}));
// Debouncing
$(window).on('scroll', $.debounce( 250, function(){
if (isScrolledIntoView($el)) {
// do stuff
}
}));
(Note that debouncing acts slightly differently from the other implementations, but that can sometimes be what you want, depending on your user experience scenario)
if ( isScrolledIntoView('.guardrail360') ) {
}
As suggested by the other answers, you should use act upon $(window).scroll(). In addition, in order to make the if statement run the first time the element is scrolled into view, I created this run_once function for you:
$('window').on('scroll', function() {
if ( isScrolledIntoView('.guardrail360') ) run_once(function() {
// ...
});
});
function run_once( callback ) {
var done = false;
return function() {
if ( !done ) {
done = true;
return callback.apply( this, arguments );
}
};
}
I would like to know if it is possible to fire an event after the scrolling of a page, when using the scrollbar or mouse-wheel (or with a swipe on a touch device).
Basically, I'd like to detect when the user has stopped scrolling so I can then AJAX-load, rather than loading while scrolling.
It seems that jQuery's .scroll() is firing every time a user scrolls, and it seems clunky to have an event fire all the time. Is there such thing as .onScrollAfter(), synonymous to the .onMouseUp()?
I'd like to know whether this is possible (or if a function already exists) without using a framework, though I would consider one; especially jQuery.
This event does not exist. You can emulate it by using timeouts:
Example (concept code):
(function() {
var timer;
/* Basic "listener" */
function scroll_finish(ev) {
clearTimeout(timer);
timer = setTimeout(scroll_finished, 200, ev);
//200ms. Too small = triggered too fast. Too high = reliable, but slow
}
window.onscroll = scroll_finish; // Or addEventListener, it's just a demo
// Fire "events"
var thingey = [];
function scroll_finished(ev) {
// Function logic
for (var i=0; i<thingey.length; i++) {
thingey[i](ev);
}
}
// Add listener
window.addScrollListener = function(fn) {
if (typeof fn === 'function') {
thingey.push(fn);
} else {
throw TypeError('addScrollListener: First argument must be a function.');
}
}
window.removeScrollListener = function(fn) {
var index = thingey.indexOf(fn);
if (index !== -1) thingey.splice(index, 1);
}
})();
Thought I would add this as an answer even though it's old. The event you are trying to recreate I believe is synonymous to debounce. This is available in underscore.js
debounce_.debounce(function, wait, [immediate])
Creates and returns a new debounced version of the passed function which will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked. Useful for implementing behavior that should only happen after the input has stopped arriving. For example: rendering a preview of a Markdown comment, recalculating a layout after the window has stopped being resized, and so on.
So it will wait after your last execution of the specific event. if you do not want a delay, you can just specify 0. David Walsh has a pretty nice implementation you can include in any project.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
Which you can go ahead adding by doing
var myEfficientFn = debounce(function() {
// All the taxing stuff you do
}, 250);
window.addEventListener('scroll', myEfficientFn);
Description
You can use the nice jQuery plugin Special scroll events for jQuery by James Padoley.
Works really great.
Check out the page and this jsFiddle Demonstration (Just scroll ;))
More Information
Special scroll events for jQuery
jsFiddle Demonstration
Is there a definitive JavaScript method for checking whether or not a web page has loaded completely? Completely, meaning 100% complete. HTML, scripts, CSS, images, plugins, AJAX, everything!
As user interaction can effect AJAX, let's assume there is no further user interaction with the page, apart from the initial page request.
What you're asking for is pretty much impossible. There is no way to determine whether everything has loaded completely. Here's why:
On a lot of webpages, AJAX only starts once the onload (or DOMReady) event fires, making the method of using the onload event to see if the page has loaded impossible.
You could theoretically tell if the webpage was performing an AJAX request by overriding window.XMLHttpRequest, but you still couldn't tell if plugins like Flash were still loading or not.
On some sites, like Twitter.com, the page only loads once and simply makes AJAX requests to the server every time the user clicks a link. How do you tell if the page has finished loading on a page like that?
In fact, the browser itself can't be completely certain whether the page has completely finished loading, or whether it's about to make more AJAX requests.
The only way to know for sure that everything loaded is to have every single piece of code on the page that loads something tell your code that it has finished once it loads.
A hacky, incomplete solution: You could try overriding XMLHttpRequest with a function that wraps the existing XMLHttpRequest and returns it. That would allow you to tell if a AJAX event is currently taking place. However, that solution wouldn't work for seeing if plugins are loaded, and you wouldn't be able to tell the difference between AJAX events that are triggered at page load and AJAX requests that happen periodically, like the ones on Stack Overflow that change the Stack Exchange icon on the top-left if you have new notifications.
Try something like this:
(function(oldHttpRequest){
// This isn't cross-browser, just a demonstration
// of replacing XMLHttpRequest
// Keep track of requests
var requests_running = 0;
// Override XMLHttpRequest's constructor
window.XMLHttpRequest = function() {
// Create an XMLHttpRequest
var request = new oldHttpRequest();
// Override the send method
var old_send = request.send;
request.send = function () {
requests_running += 1;
old_send.apply(request, arguments);
};
// Wait for it to load
req.addEventListener("load", function() {
requests_running -= 1;
}, false);
// Return our modified XMLHttpRequest
return request;
};
window.addEventListener("load", function() {
// Check every 50 ms to see if no requests are running
setTimeout(function checkLoad() {
if(requests_running === 0)
{
// Load is probably complete
}
else
setTimeout(checkLoad, 50);
}, 50);
}, false);
})(window.XMLHttpRequest)
The:
window.onload
event will fire at this point.
window.onLoad = function(){
//Stuff to do when page has loaded.
}
or
<body onLoad="functionCall()">
Basically ADW and Keith answer the question, but I would suggest not to use window.onload but:
if (window.addEventListener) {
window.addEventListener("load", myfunction, false);
} else {
window.attachEvent("onload", myfunction);
}
function myfunction() {
...
}
Using a combination of window.onload, document.readyState, and callbacks for AJAX requests, you should be able to do what you want. Simply make sure the window has loaded, the DOM is ready for manipulation, and keep track of AJAX requests.
For AJAX in particular, depending on how many requests you make: Increment a variable each time you make a request, and when the variable === the total amount of requests, fire a function. If you don't happen to know the amount of AJAX requests, but know which one would be last, simply have a callback function fire when it finishes.
When all is set and true, fire a final function to do what you want, knowing everything should be loaded.
In regards to Flash and Silverlight applications (not sure if window.onload or document.ready keeps track of those), you could also record the amount of data loaded withing the application, and when the loaded data === the total data, have the application fire a function or increment a variable to the page.
window.onload = function() {
var time = window.setInterval(function() {
if(document.readyState == "interactive") {
increment();
window.clearInterval(time);
}
}, 250);
}
var total = 10, current = 0;
var increment = function() {
current += 1;
if(current === total) { weAreDone(); }
}
function weAreDone() {
// Everything should be done!
}
Here is the non intrusive js function I scripted, using events on load. In this case, I fire events on js script load as this is my js autoloader function, but you can just add event on other items using the same principle. Provided this script looks after js scripts loaded in a dedicated div tag.
function scriptLoaded(e) {
var oLoadedScript = e.target || e.srcElement;
alert ('loaded : ' + oLoadedScript.src);
return false;
}
/**
* Import js lib and fire function ControlData on events
* #param js_librairies
* #returns {Boolean}
*/
function init(){
// lib import
// Locate js in the div
var myscript_location = document.getElementById('js_script_goes_here');
// DEBUG
if (undefined == myscript_location)
alert('div not found');
else
alert('found div : ' + myscript_location);
// to prevent js script from catching in dev mode
var force_js_reload = "?version=1" ;
for (var i=0; i < js_librairies.length ; ++i) {
var my_script = document.createElement('script');
my_script.defer = false;
my_script.src = relative_path + js_librairies[i] + force_js_reload ;
my_script.type = 'text/javascript';
// DEBUG
my_script.onload = scriptLoaded;
myscript_location.appendChild(my_script);
}
return false;
}
/**
* Start non intrusive js
* #param func
*/
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
};
}
}
//ONLOAD
addLoadEvent(init);
function r(f){/in/(document.readyState)?setTimeout(r,9,f):f()}
Courtesy: Smallest DOMReady code, ever - Dustin Diaz
Update: And for IE
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
P.S: window.onload is a very different thing